Re: Apache Vulnerability through a Proxy?

From: Ben Laurie (benat_private)
Date: Sat Jun 22 2002 - 10:31:10 PDT

  • Next message: Pete Ehlke: "Re: Ending a few arguments with one simple attachment."

    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
    



    This archive was generated by hypermail 2b30 : Sat Jun 22 2002 - 11:24:23 PDT