Re: Code Red Revisited and Stack-Based Exception Handler Frame Bug

From: Roland Postle (mailat_private)
Date: Thu Feb 13 2003 - 15:08:00 PST

  • Next message: Mandrake Linux Security Team: "MDKSA-2003:016 - Updated util-linux packages provide stronger randomness in mcookie"

    On Tue, 11 Feb 2003 16:11:15 -0500, Peter Huang wrote:
    
    >As I followed the trail described by Mr. Szor and Mr. Chien’s article
    >(http://www.peterszor.com/blended.pdf), it became clear to me the following
    >was the transfer point to the abused exception handler like Code Red. For a
    >normal C/C++ function, it was OK to assume that sub-functions will not
    >depend on the values of EBX, EDI, and ESI and will not abuse them. However,
    >that assumption was proved wrong with the Code Red exploitation and should
    >be corrected. One simple way is to save EBX and xor EBX out before it
    >transfers the rein. A more panicky way is to save and xor out all the unused
    >registers like EBX, EDI, and ESI for this function before it calls ECX
    
    Apparently this is exactly what happens now, except Microsoft chose to
    nuke eax as well (see below). But it's a bit misleading to say this
    made the Code Red exploitation possible. There are a numerous other
    ways to find your shellcode in memory without relying on having a
    register pointing directly at it, albeit some are more reliable than
    others. In this case I believe the shellcode is some way down the stack
    (?) in which case pointing the call ecx at some code like:
    
    retn 10h
    
    and arranging for the retn to pop a pointer to a 
    
    mov eax, esp
    call eax
    
    would probably do the trick.
    
    You could run around all day making sure registers didn't have values
    that were useful to attackers, but all you'd end up doing is
    introducing masses of overhead and making exploitation a bit more
    challenging (and more satisfying). I hope you're not really suggesting
    that *every* function call should begin with fresh registers? It's just
    about justified in this case because exception handlers are one of the
    more common things that get overwritten.
    
    - Blazde
    
    From XP SP1 ntdll.dll:
    
    .text:77F79B4D _RtlpExecuteHandlerForUnwind@20 proc near ; CODE XREF:
    RtlUnwind(x,x,x,x)+BDp
    .text:77F79B4D 
    .text:77F79B4D arg_0           = dword ptr  4
    .text:77F79B4D arg_4           = dword ptr  8
    .text:77F79B4D arg_8           = dword ptr  0Ch
    .text:77F79B4D arg_C           = dword ptr  10h
    .text:77F79B4D arg_10          = dword ptr  14h
    .text:77F79B4D 
    .text:77F79B4D                 mov     edx, offset loc_77F79BDF
    .text:77F79B52                 lea     ecx, [ecx]
    .text:77F79B54 
    .text:77F79B54 ExecuteHandler@20:                      ; CODE XREF:
    RtlpExecuteHandlerForException(x,x,x,x,x)+5j
    .text:77F79B54                 push    ebx
    .text:77F79B55                 push    esi
    .text:77F79B56                 push    edi
    .text:77F79B57                 xor     eax, eax
    .text:77F79B59                 xor     ebx, ebx
    .text:77F79B5B                 xor     esi, esi
    .text:77F79B5D                 xor     edi, edi
    .text:77F79B5F                 push    [esp+0Ch+arg_10]
    .text:77F79B63                 push    [esp+10h+arg_C]
    .text:77F79B67                 push    [esp+14h+arg_8]
    .text:77F79B6B                 push    [esp+18h+arg_4]
    .text:77F79B6F                 push    [esp+1Ch+arg_0]
    .text:77F79B73                 call    ExecuteHandler2@20
    .text:77F79B78                 pop     edi
    .text:77F79B79                 pop     esi
    .text:77F79B7A                 pop     ebx
    .text:77F79B7B                 retn    14h
    .text:77F79B7B _RtlpExecuteHandlerForUnwind@20 endp
    



    This archive was generated by hypermail 2b30 : Thu Feb 13 2003 - 15:41:27 PST