Executable Regular Expression Error

Description

Executable Regular Expression Error occurs when applications use regular expression engines that allow executable code within patterns. Some regex implementations (like Perl's /e modifier or Ruby's gsub with evaluation) can execute arbitrary code during pattern matching or substitution. When user input influences these regex operations, attackers can inject and execute malicious code.

Risk

Attackers can execute arbitrary code on the server through crafted regex patterns or replacement strings. This leads to full system compromise, data theft, and lateral movement. The vulnerability is particularly dangerous because regex patterns are often overlooked as potential code injection vectors. The attack surface includes both pattern construction and replacement string handling.

Solution

Avoid regex engines with code execution features when processing user input. Disable evaluation modifiers (/e in Perl, using \e in replacement). Use non-evaluating substitution methods. Escape user input before including in regex. Consider using simpler string operations instead of regex. Implement strict input validation before regex processing.

Common Consequences

ImpactDetails
IntegrityScope: Code Execution

Arbitrary code runs with application privileges.
ConfidentialityScope: Data Theft

Attackers can access any application data.
AvailabilityScope: System Compromise

Full control of the system.

Example Code + Solution Code

Vulnerable Code

#!/usr/bin/perl
# VULNERABLE: Perl regex with /e modifier

# User input used in replacement with evaluation
sub transform_vulnerable {
    my ($text, $pattern, $replacement) = @_;

    # The /e modifier evaluates $replacement as code!
    $text =~ s/$pattern/$replacement/ee;

    return $text;
}

# Attack: $replacement = 'system("rm -rf /")'
my $user_text = "Hello World";
my $user_pattern = "World";
my $user_replacement = $ARGV[0];  # Attacker controlled!

# If user_replacement is: system('cat /etc/passwd')
# It will execute the system command!
my $result = transform_vulnerable($user_text, $user_pattern, $user_replacement);

# VULNERABLE: Double evaluation
sub process_vulnerable {
    my $input = shift;
    my $template = shift;

    # Double /e allows complex attacks
    $input =~ s/\{\{(\w+)\}\}/$template->{$1}/eeg;
}

# VULNERABLE: Pattern itself is user-controlled
sub search_vulnerable {
    my ($haystack, $user_pattern) = @_;

    # User controls regex pattern
    # Attack: (?{system('whoami')})
    if ($haystack =~ /$user_pattern/) {
        return 1;
    }
    return 0;
}
# VULNERABLE: Ruby gsub with block evaluation
def transform_vulnerable(text, pattern, replacement)
  # If replacement is user-controlled and evaluated
  text.gsub(/#{pattern}/) { eval(replacement) }
end

# VULNERABLE: String interpolation in replacement
def replace_vulnerable(text, user_input)
  # User input in replacement string
  text.gsub(/pattern/, "#{eval(user_input)}")
end

# VULNERABLE: Dynamic regex with user input
def match_vulnerable(text, user_pattern)
  # User controls regex - code injection possible
  regex = Regexp.new(user_pattern)
  text.match(regex)
end
<?php
// VULNERABLE: preg_replace with /e modifier (deprecated in PHP 5.5, removed in PHP 7)
function transformVulnerable($text, $pattern, $replacement) {
    // The /e modifier evaluates replacement as PHP code!
    return preg_replace("/$pattern/e", $replacement, $text);
}

// Attack example:
// $pattern = ".*"
// $replacement = "system('id')"

// VULNERABLE: Using preg_replace_callback unsafely
function processVulnerable($text, $user_code) {
    return preg_replace_callback(
        '/\{\{(\w+)\}\}/',
        function($matches) use ($user_code) {
            // Evaluates user code!
            return eval("return $user_code;");
        },
        $text
    );
}

// VULNERABLE: create_function (deprecated, removed in PHP 8)
function filterVulnerable($array, $user_condition) {
    $func = create_function('$x', "return $user_condition;");
    return array_filter($array, $func);
}
?>

Fixed Code

#!/usr/bin/perl
use strict;
use warnings;

# SAFE: No /e modifier - simple string replacement
sub transform_safe {
    my ($text, $pattern, $replacement) = @_;

    # Escape special regex characters in user input
    $pattern = quotemeta($pattern);
    $replacement = quotemeta($replacement);

    # No /e modifier - literal replacement
    $text =~ s/$pattern/$replacement/g;

    return $text;
}

# SAFE: Use hash lookup instead of eval
sub process_safe {
    my ($input, $variables) = @_;

    $input =~ s/\{\{(\w+)\}\}/$variables->{$1} \/\/ ''/g;

    return $input;
}

# SAFE: Whitelist allowed patterns
sub search_safe {
    my ($haystack, $user_pattern) = @_;

    # Only allow alphanumeric patterns
    unless ($user_pattern =~ /^[\w\s]+$/) {
        die "Invalid pattern";
    }

    # Escape for literal matching
    my $safe_pattern = quotemeta($user_pattern);

    if ($haystack =~ /$safe_pattern/) {
        return 1;
    }
    return 0;
}

# SAFE: Using index() instead of regex for simple searches
sub find_safe {
    my ($haystack, $needle) = @_;
    return index($haystack, $needle) != -1;
}
# SAFE: No evaluation in replacement
def transform_safe(text, pattern, replacement)
  # Escape pattern for literal matching
  safe_pattern = Regexp.escape(pattern)

  # Literal replacement, no evaluation
  text.gsub(/#{safe_pattern}/, replacement)
end

# SAFE: Use hash substitution
def template_safe(text, variables)
  text.gsub(/\{\{(\w+)\}\}/) do |match|
    key = $1
    # Lookup in hash, no eval
    variables[key] || ''
  end
end

# SAFE: Validate and constrain patterns
def match_safe(text, user_pattern)
  # Whitelist safe characters
  unless user_pattern.match?(/^[\w\s.*?+]+$/)
    raise "Invalid pattern characters"
  end

  # Limit pattern complexity
  if user_pattern.length > 100
    raise "Pattern too long"
  end

  # Use timeout for regex execution
  Timeout.timeout(1) do
    text.match(Regexp.new(user_pattern))
  end
rescue Timeout::Error
  raise "Pattern took too long"
end

# SAFE: Use simple string methods when possible
def find_safe(text, search)
  text.include?(search)
end
<?php
// SAFE: Use preg_replace_callback with safe handling
function transformSafe($text, $pattern, $variables) {
    // Escape user pattern
    $safePattern = preg_quote($pattern, '/');

    return preg_replace_callback(
        "/$safePattern/",
        function($matches) use ($variables) {
            // Lookup, never eval
            return $variables[$matches[0]] ?? $matches[0];
        },
        $text
    );
}

// SAFE: Template with explicit variable lookup
function templateSafe($text, array $variables) {
    return preg_replace_callback(
        '/\{\{(\w+)\}\}/',
        function($matches) use ($variables) {
            $key = $matches[1];
            // Only lookup from predefined variables
            return array_key_exists($key, $variables)
                   ? htmlspecialchars($variables[$key])
                   : '';
        },
        $text
    );
}

// SAFE: Validate regex pattern before use
function searchSafe($text, $userPattern) {
    // Whitelist allowed characters
    if (!preg_match('/^[\w\s.*?+\[\]()]+$/', $userPattern)) {
        throw new InvalidArgumentException('Invalid pattern');
    }

    // Limit length
    if (strlen($userPattern) > 100) {
        throw new InvalidArgumentException('Pattern too long');
    }

    // Escape special regex chars if doing literal search
    $safePattern = preg_quote($userPattern, '/');

    return preg_match("/$safePattern/", $text);
}

// SAFE: Use str_replace for simple cases
function replaceSafe($text, $search, $replace) {
    return str_replace($search, $replace, $text);
}

// SAFE: Modern PHP - anonymous functions instead of create_function
function filterSafe($array, callable $predicate) {
    return array_filter($array, $predicate);
}

// Usage
$filtered = filterSafe([1, 2, 3, 4], fn($x) => $x > 2);
?>

Exploited in the Wild

Server Compromise

Perl web applications with /e modifier exploited for RCE.

CMS Vulnerabilities

Content management systems with regex processing exploited.

Template Injection

Template engines using regex evaluation compromised.


Tools to test/exploit

  • Burp Suite — test regex injection.

  • Custom scripts testing regex evaluation.

  • Static analyzers detecting /e modifier usage.


CVE Examples

  • CVEs from Perl applications with /e modifier.

  • PHP applications using preg_replace with /e.


References

  1. MITRE. "CWE-624: Executable Regular Expression Error." https://cwe.mitre.org/data/definitions/624.html

  2. Perl security documentation on /e modifier dangers.