Skip to content

Latest commit

 

History

History
77 lines (69 loc) · 5.72 KB

File metadata and controls

77 lines (69 loc) · 5.72 KB

cmd-injection trap

Responds to "exposed admin endpoint that runs a shell command" probes and to classic CGI environment-leak scripts.

Path Method Response
/admin/config GET/POST landing HTML if no cmd= param; otherwise simulated command output (see below)
/admin/config.php GET/POST same handler; observed scanner variant of the admin-config probe shape
/printenv GET fake env-block whose AWS_* values are a Tracebit canary
/cgi-bin/printenv GET same as /printenv
/cgi-bin/test-cgi GET same as /printenv
*eval-stdin.php with phpunit in the path GET/POST echoes simple PHP probe output such as md5("Hello PHPUnit")
/?...auto_prepend_file=php://input / /hello.world?... POST PHP-CGI RCE probe; decodes base64_decode(...) command hints and echoes PHP probe output
/cgi-bin/.../bin/sh path traversal POST Apache CGI shell probe; logs the stdin command body and returns empty 200
/cgi-bin/php, /cgi-bin/php-cgi, /cgi-bin/php{5,7,8}{,-cgi,.cgi}, /cgi-bin/, /cgi-bin GET/HEAD PHP-CGI bare-path liveness probe; returns the canonical <br />\n<b>No input file specified.</b> 200 page with Server: Apache/2.4.41 (Win64) ... PHP/7.4.33 and X-Powered-By: PHP/7.4.33 so the gated CVE-2024-4577 scanners send their exploit POST next (which lands in the body-RCE row above)

The handler reads the cmd value from ?cmd=…, the equivalent POST form param (cmd, command, exec, c), and classifies it into one of the families below. Per-IP cache bounds Tracebit issuance the same way the fake-git trap does.

cmdFamily Trigger Response body
creds-aws cat …/.aws/credentials per-request Tracebit AWS canary in ~/.aws/credentials INI shape
creds-aws-config cat …/.aws/config per-request Tracebit AWS canary in ~/.aws/config INI shape
env printenv, env, or any /printenv route per-request Tracebit AWS canary in a printenv-shape env block
passwd cat /etc/passwd / /etc/shadow static fake /etc/passwd matching the webshell trap
id / whoami / uname / hostname / pwd / ls matching builtin static fake output (uid=33(www-data) …, etc.)
unknown anything else empty body — matches shell behaviour for builtins/assignments

Logged fields per event (in addition to the standard LOGS.md schema): result (cmd-injection-probe / cmd-injection-command / cmd-injection-creds-leak / cmd-injection-printenv / cmd-injection-php-cgi-rce / cmd-injection-apache-cgi-shell / phpunit-eval-stdin / php-cgi-liveness), cmdInjectionPath, cmdSource (query / form / ""), cmdKey, cmd, cmdFamily, outputBytes, and canaryStatus (issued / issue-failed) when a Tracebit canary was minted. Body-RCE rows also include bodyPreview; PHP-CGI rows include decodedCommand when a base64_decode(...) payload was decoded; liveness rows carry phpCgiLivenessPath and phpCgiLivenessMethod.

Why

Two observed shapes drove this trap:

  1. Scanner fleets escalating from passive credential-file harvesting (/.env, /.aws/credentials) to active command-injection probes against admin-config endpoints — typified by GET requests like /admin/config?cmd=cat%20/root/.aws/credentials. A 404 here teaches the scanner there's no exploit; a plausible response with canary credentials in the body keeps them on the line and gives us a replay alert if they ship the value to AWS.
  2. The 1990s-era CGI demo scripts /printenv and /cgi-bin/printenv are still hunted because exposed env blocks routinely carry AWS_ACCESS_KEY_ID, DATABASE_URL, etc. Returning a fake env block whose AWS values are a Tracebit canary lets us measure "how often does this class of scanner replay env-harvested AWS creds, and how fast?".
  3. Body capture showed high-volume active RCE probes whose useful signal was entirely in the request body: PHPUnit eval-stdin.php, PHP-CGI php://input, and Apache CGI path-traversal /bin/sh. These now get 200 responses and structured body/decoded-command logging instead of falling through as generic 404s.
  4. CVE-2024-4577 (Windows PHP-CGI argument injection) scanners split into two populations: "spray" scanners that POST the exploit on every target regardless of liveness, and "gated" scanners that probe /cgi-bin/php (or /cgi-bin/php-cgi, /cgi-bin/, etc.) with a bare GET first and only send the exploit if they get a 200 back with a Windows-shape Server / X-Powered-By header. The bare GETs were the top "not-handled" path in observed traffic by a wide margin. The php-cgi-liveness row returns the canonical <br />\n<b>No input file specified.</b> page with Server: Apache/2.4.41 (Win64) ... PHP/7.4.33 so the gated population sends the follow-up exploit POST, which then lands in the body-RCE handler in row #3.