Bugtraq mailing list archives

Re: Apache Vulnerability through a Proxy?


From: Ben Laurie <ben () algroup co uk>
Date: Sat, 22 Jun 2002 18:31:10 +0100

Ulf Bahrenfuss wrote:
> Hi!
>
> Does anyone know, if the chunk handling vulnerability carries through
> a proxy i.e. Squid or Webcache? (Updating is currently not possible,
> because it is not the plain apache, but the Oracle IAS flavour...)
>
> Or has anyone further information how this vulnerabilty really works?

Here's an analysis I wrote for iternal use at the ASF - it doesn't go into detail on the shellcode (which is just the usual shellcode), but does explain how the expected SEGV from overrunning the stack is avoided. Note that someone (sorry, forgotten who) posted a similar generic analyis a day or two ago - this one was independently arrived at and refers to the Gobbles attack specifically.

First, the exploit code puts stuff on the stack (legitimately, in buffers). It then arranges a negative offset, as previously described, to be handed to memcpy. Here's where it gets cute. memcpy has memmove semantics (i.e., it copies in the correct direction to handle overlapping source/dest) on both OpenBSD and FreeBSD (in fact, I believe this is a requirement for this exploit to work on any system where the stack grows downwards). As a result, when the memcpy is attempted, it is done backwards (i.e. the copy starts at source+length-1 -> dest+length-1 and downwards for length bytes). Now, here's the cute bit. memmove (et al) are optimised to copy in 4 byte chunks, for speed. This means that they have to copy the leftover bytes separately. This is handled by copying the odd 0-3 bytes before the remaining bytes.

So, if you arrange for the negative offset of the buffer to point at where the length is stored on the stack, then when these odd bytes are copied, you can modify the length. What they do is modify an initial length of 0xffffxxxx to 0x0000xxxx - note that the length is also the offset, so there is also a certain amount of luck involved, but all that is needed is for the offset to be small enough that the length remains big enough to zap enough stack (since the offset is a few hundred, that leaves the length at near to 64k, which is plenty to zap a few return addresses). Then, when the length is reloaded to do the second copy, it is miraculously smaller (I boggled first time I saw this in the debugger), and doesn't cause the expected SEGV, just nice corruption of the stack, as required![1]

So, to illustrate with source:

0x400f9d6c <memcpy>:    push   %esi
0x400f9d6d <memcpy+1>:  push   %edi
0x400f9d6e <memcpy+2>:  mov    0xc(%esp,1),%edi
0x400f9d72 <memcpy+6>:  mov    0x10(%esp,1),%esi
0x400f9d76 <memcpy+10>: mov    0x14(%esp,1),%ecx
0x400f9d7a <memcpy+14>: cmp    %esi,%edi
0x400f9d7c <memcpy+16>: jae    0x400f9d94 <memcpy+40>
...

at this point, we've decided to go backwards, edi is dest, esi is source and ecx is count (aka -146 aka ffffff6e)

0x400f9d94 <memcpy+40>: add    %ecx,%edi
0x400f9d96 <memcpy+42>: add    %ecx,%esi

Now we are pointing at the "end" of the buffers (i.e. somewhere down the stack from them, and, lo and behold, edi now points at the two MS bytes of the count)

0x400f9d98 <memcpy+44>: std
0x400f9d99 <memcpy+45>: and    $0x3,%ecx

calculate spare bytes (2 in this case)

0x400f9d9c <memcpy+48>: dec    %edi
0x400f9d9d <memcpy+49>: dec    %esi
0x400f9d9e <memcpy+50>: repz movsb %ds:(%esi),%es:(%edi)

and copy them - in fact two zeroes are copied, so the length is now 0000ff6e.

0x400f9da0 <memcpy+52>: mov    0x14(%esp,1),%ecx

load the length again (now ff6e)

0x400f9da4 <memcpy+56>: shr    $0x2,%ecx

divide by 4

0x400f9da7 <memcpy+59>: sub    $0x3,%esi
0x400f9daa <memcpy+62>: sub    $0x3,%edi
0x400f9dad <memcpy+65>: repz movsl %ds:(%esi),%es:(%edi)

and copy that many longs (i.e. just shy of 64k bytes). Here is where we would have gone bang with a SEGV, but don't coz of the cunningness.

0x400f9daf <memcpy+67>: mov    0xc(%esp,1),%eax
0x400f9db3 <memcpy+71>: pop    %edi
0x400f9db4 <memcpy+72>: pop    %esi
0x400f9db5 <memcpy+73>: cld
0x400f9db6 <memcpy+74>: ret

return to a corrupted return address (or is it the next one up that's corrupted? not sure, don't care). And hey presto, remote shell.

Note that glibc is _not_ vulnerable in this way, so I have no idea how the Linux attack works. I have not examined Solaris.

Cheers,

Ben.

[1] For those not familiar with this class of exploit, the stack is corrupted such that the return address for some function call points to code which spawns a shell, which is then used by the attacker to have his or her evil way with your machine.

--
http://www.apache-ssl.org/ben.html       http://www.thebunker.net/

"There is no limit to what a man can do or how far he can go if he
doesn't mind who gets the credit." - Robert Woodruff


Current thread: