All articlesAttack Techniques

How to Test for OS Command Injection Without Compromising the Server

September 23, 2026·9 min read·Nautillo Pro Security Team

OS command injection is one of the few vulnerability classes that goes straight from "input field" to "full server compromise" with no privilege escalation step in between. It's also one of the few where a careless test can genuinely damage the target. Here's how to detect it safely — with read-only proof, not destructive commands.

Why command injection is still critical in 2026

Most developers know not to build raw SQL strings from user input. Fewer apply the same discipline to shell commands — because shelling out feels like a backend implementation detail, not a security boundary. It shows up in image-processing pipelines that call convert or ffmpeg, network utilities that wrap ping or traceroute for a "diagnostics" feature, PDF generators that shell out to a converter binary, and legacy admin tools that build CLI commands from form fields.

The impact is why it's classified CWE-78 under OWASP A03:2021 — Injection and consistently scores CVSS 9.8 Critical: a successful injection runs with the web process's own privileges, meaning the attacker doesn't need to find a second vulnerability to escalate — they already have code execution.

Three detection techniques, in priority order

1

Output-based

Inject a command whose output is unambiguous and unique, then check whether it appears in the response. This is the gold standard — zero false positives when the marker is specific enough.

2

Error-based

Inject a deliberately invalid command name. If the shell's own error message ("command not found", "not recognized as an internal or external command") leaks into the response, the input reaches a shell — even if you can't see command output directly.

3

Time-based blind

When the response gives no visible signal at all, inject a command that only delays the response (sleep 5) and measure against a timing baseline. The technique of last resort — but often the only one that works against a fully silent endpoint.

HTTP proof-of-concept for each technique

1. Output-based — sentinel token injection

GET /api/diagnostics?host=127.0.0.1;+echo+NAUT_TEST_7739 HTTP/1.1

HTTP/1.1 200 OK
{ "result": "PING 127.0.0.1... \nNAUT_TEST_7739\n" }
# → sentinel string appears verbatim in the response = confirmed

A cross-platform, unambiguous alternative when a sentinel echo is filtered — the id command's output format can't occur by coincidence:

GET /api/diagnostics?host=127.0.0.1;id HTTP/1.1

HTTP/1.1 200 OK
{ "result": "uid=33(www-data) gid=33(www-data) groups=33(www-data)" }
# → uid=NNN( pattern is conclusive proof of command execution

2. Error-based — invalid command disclosure

GET /api/diagnostics?host=127.0.0.1;invalid_cmd_xyz_9921 HTTP/1.1

HTTP/1.1 200 OK
{ "result": "bash: invalid_cmd_xyz_9921: command not found" }
# → shell error leaked = input reaches a shell, even with no visible
#   command output otherwise

3. Time-based blind — no visible output at all

# Baseline
GET /api/diagnostics?host=127.0.0.1 HTTP/1.1
→ responds in 340ms

# Injected
GET /api/diagnostics?host=127.0.0.1;sleep+5 HTTP/1.1
→ responds in 5,310ms

# 5-second delay matching the injected sleep duration, measured
# against baseline, confirms blind command injection

Safe testing methodology — proof without damage

The line between "proving command injection" and "causing an incident" is thin, because the vulnerability's whole nature is arbitrary code execution. A responsible testing methodology never crosses from detection into modification:

Use a unique sentinel, not a destructive command

echo NAUT_TEST_7739 proves execution as conclusively as rm -rf / would — without the risk. Never test with commands that write, delete, or modify anything.

Read-only reconnaissance commands only

id, whoami, and reading /etc/passwd or win.ini are non-destructive and give unambiguous proof of OS-level access without touching application data.

Measure a timing baseline before any time-based probe

A single slow response on a loaded server is not proof. Compare injected-payload timing against an un-injected baseline request, and require a delay that matches the injected duration.

Stop after the first confirmed proof per parameter

Once output-based or error-based detection confirms the vulnerability, there's no need to keep firing payloads — every additional request is unnecessary risk for no additional evidence.

Where to look: the highest-risk input surfaces

Network diagnostic tools (ping, traceroute, DNS lookup)

High

The most classic vector — a feature that legitimately needs to invoke a system network command is one bad string-concatenation away from full injection.

Image, video, and document processing

High

Pipelines wrapping ImageMagick, ffmpeg, or PDF converters often pass filenames or conversion options through to the shell without sanitization.

File upload and export features

Medium

Filenames, especially when used to build archive or conversion commands, are a frequently-missed injection point.

Legacy admin panels and internal tools

Medium

Older internal tooling built for convenience over security often shells out directly to system utilities based on form input.

Webhook and integration configuration

Low–Medium

Fields that get passed to a backend script for validation or testing (e.g. 'test this URL') sometimes reach a shell command unexpectedly.

CVSS 3.1 scoring

Confirmed OS command injection, unauthenticated, with server-level command execution:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

AV:N — Network (remotely exploitable)

AC:L — Low complexity (no special conditions required)

PR:N — No privileges required

UI:N — No user interaction required

C:H / I:H / A:H — Full confidentiality, integrity, and availability impact — the attacker runs arbitrary commands with the web process's privileges

Score: 9.8 Critical

This is consistently one of the highest-scoring vulnerability classes in any assessment — there is no meaningful "medium severity" command injection. If the shell is reachable at all, the ceiling is full server compromise.

Test your app for command injection safely

Nautillo Pro runs scope-safe command injection probes — sentinel-token and read-only detection only, with a measured timing baseline before any blind test. Every confirmed finding includes the exact request, response, and CVSS score.

Related articles