Perl Command-Execution Sinks
Perl backticks/qx// sinks in Apache mod_perl handlers (reachability and exploitation)
One real-world pattern is Perl code building a shell command and executing it with backticks (qx//). In a mod_perl access handler, request data such as $r->uri() can reach that string before authentication completes. RCE requires both reachable attacker input and shell interpretation; neither mod_perl nor URI access alone creates command injection.[1]
Risky Perl execution primitives include:[4]
- Backticks /
qx//:my $out = `cmd ...`;. - One-argument
system STRINGwhen the string contains shell metacharacters; list form (system PROGRAM, LIST) avoids shell parsing. - Two-argument
openwith a pipe expression, such asopen my $fh, "cmd |"or"| cmd". Prefer three-argumentopenand explicit argument lists. IPC::Open3with one command scalar can inherit Perl’s single-string shell handling. Pass the program and arguments as separate list elements when no shell is required.[5]- An explicit shell such as
system('/bin/sh', '-c', $attacker_string), which is dangerous even though it uses list form.
Minimal vulnerable shape observed in the wild:[1]
sub getCASURL {
...
my $exec_cmd = "...";
if ($type eq 'login') {
$exec_cmd .= $uri; # $uri from $r->uri() → attacker-controlled
my $out = `$exec_cmd`; # backticks = shell
}
}
Key reachability considerations in mod_perl:[1]
- Handler registration:
httpd.confmust route the request into the Perl module, for example withPerlModule MOD_SEC_EMC::AccessHandlerand an applicable access-handler configuration. - Vulnerable branch: The Dell chain required the unauthenticated login flow (
type eq "login"), reached by omitting the expected authentication cookie. - Resolvable path: The requested URI must fall within a scope processed by the handler. Otherwise the sink is never reached.
Exploitation workflow [1]
- Inspect httpd.conf for PerlModule/MOD_PERL handler scopes to find a resolvable path processed by the handler.
- Send an unauthenticated request so the login redirect path is taken (type == “login”).
- Place shell metacharacters in the request-URI path so $r->uri() carries your payload into the command string.
Example HTTP PoC (path injection via ’;’)
GET /ui/health;id HTTP/1.1
Host: target
Connection: close
Payload notes
- Try separators such as
;,&&,|, backticks,$(), and encoded newlines (%0A) according to the surrounding quote context. - If other arguments are quoted but the URI remains raw in one branch, an end-of-string payload such as
;id#or&&/usr/bin/id#may terminate the intended command and comment the remainder.
Hardening
- Do not build shell strings. Prefer argument-vector execution:
system('/usr/bin/curl', '--silent', '--', $safe_url).[4] - If a shell is unavoidable, escape strictly and consistently across all branches; treat $r->uri() as hostile. Consider URI::Escape for paths/queries and strong allowlists.
- Avoid backticks/qx// for command execution; capture output via open3/list form if truly needed without invoking a shell.
- In mod_perl handlers, keep auth/redirect code paths free of command execution or ensure identical sanitization across branches to avoid “fixed everywhere but one branch” regressions.
Vulnerability hunting
- Patch-diff modules that assemble shell commands; look for inconsistent quoting between branches (e.g., if ($type eq ‘login’) left unescaped).
- Grep for backticks, qx//, open\s*(|||, and system\s*(\s*” to find string-based shells. Build a call graph from sink to request entry ($r) to verify pre-auth reachability.
Real-world case: Dell UnityVSA pre-auth RCE (CVE-2025-36604)[1][2][3]
- Pre-auth command injection via backticks in AccessTool.pm:getCASURL when type == “login” concatenated raw $uri ($r->uri()).
- Reachable through MOD_SEC_EMC::AccessHandler → make_return_address($r) → getCASLoginURL(…, type=“login”) → getCASURL(…, $uri, ‘login’).
- Practical nuance: use a resolvable path covered by the handler; otherwise the module won’t execute and the sink won’t be hit.
References
- [1] It’s Never Simple Until It Is: Dell UnityVSA Pre‑Auth Command Injection (CVE‑2025‑36604)
- [2] Dell PSIRT DSA‑2025‑281 – Security update for Dell Unity/UnityVSA/Unity XT
- [3] watchTowr Detection Artefact Generator – Dell UnityVSA Pre‑Auth CVE‑2025‑36604
- [4] Perl documentation —
system,exec, and avoiding the shell - [5] Perl documentation —
IPC::Open3