Using Perl Compatible Regular Expressions (PCRE) in Protocol Inspection Custom Signatures
Detecting attacks that have a consistent pattern can be handled pretty easily with content checks, but these fail when attackers get creative in obfuscating (varying the patterns used in) attacks. Their goal is to present the attack in a way that the target will understand, but that an IDS/IPS will not. For example, let's look at a Directory Traversal attack from the early days of the World Wide Web. The web server receives a request for a relative path like:
../../etc/hosts
The ../
moves up one directory. So from the web server document directory, which is typically /var/www, this relative path takes us two directories below to / and then up to /etc to GET the hosts file.
That's easy to check for.
What if the attacker disguises the request?
Consider the URL encoding of ../
:
%2e%2e%2f
This will be understood by the webserver, but unless the signature on the IPS handles this pattern, the attack will not be detected. Since the attacker can mix and match from a number of encoding schemes, just writing a separate signature with a content check for every possibility becomes impractical.
For matching variable patterns, Perl Compatible Regular Expressions (PCRE) is the answer. These are also referred to as 'Regular Expressions' or 'Regex.'
Using PCRE in a Custom Signature
Begin by declaring the pcre check with the escaped double-quotes:
pcre:\"
Then delimit the start of the pattern with a ' / ':
pcre:\"/
then add the pattern to match and close the pattern with another ' / ':
pcre:\"/thisismypattern[0-9]/
then close the pcre check with escaped an double-quote and semi-colon ' \"; ':
pcre:\"/thisismypattern[0-9]/\";
This pattern looks for "thisismypattern" followed by a digit. (There are other ways to express the digit.)
When working with a regex, I find the Regex 101 site very helpful for testing patterns with test strings.
Example: detecting padded Apache Log4j exploit attempts
Recently the Apache Log4j2 vulnerability made the news. Researchers published exploits and proofs of concepts with obfuscation. At its base, the attacks all require the target to process a special URL of the form
${jndi
Each of these characters can be encoded in a variety of ways - unicode, html, hex, octal, etc. The $
character could appear as 36 (decimal ascii), 24 (hex ascii), 044 (octal ascii), $ (html), %24 (URL), and so on.
Padding is another way to obfuscate the attack.
Here is an excerpt from a PoC attack that disguises the 'j' character by creating an environment variable within an environment variable that consists, essentially, of "discard all the garbage leading up to 'j'."
${${CP:C:F:BPs:Ic:—j}
To cover padded attacks like this, I wrote the following signature sig section:
content: \"$\"; content: \"{\"; distance: 1; pcre:\"/(\?i)\\$'\?\\{.*\?j.*\?n.*\?d.*\?i.*\?\\:/s\";
Let's walk through this.
content: \"$\"; | Simple content match for ' $ ' character. This is used as a pre-filter so we can skip the expensive PCRE check coming up later if there's no ' $ ' character. |
content: \"{\"; | Simple content match for ' { ' character. This continues the pre-filter. |
distance: 1; | This requires the search for the ' { ' character to begin AFTER the previous match (the ' $ ' ). |
pcre: \" | As with content checks, we have to encapsulate the pattern with a set of quotation marks, and escape those quotation marks with a ' \ ' character. |
/ | This indicates the pattern to match is beginning. |
(\?i) | The pattern is case-insensitive |
\\$ | A literal ' $ ' character. The double-backslash is needed to properly escape the ' $ ' character. We need to pass a ' \ ' character to the Signature Matching Engine to escape the ' $ ' character, because that is a control character in the Regex.We need to escape the ' \ ' character we're passing to the Signature Matching Engine to pass BIG-IP configuration validation. |
'\? | A single quote character that is optional. This sometimes appears as padding. |
\\{ | A literal ' { ' character. The double-backslash is needed to properly escape the ' { ' character. (See the \\$ row, above) |
.*\? | Match zero or more of any character (' .* ') (including newline - see the ' s ' row below), with 'stingy' or 'lazy' matching (' \? ') to save searching through the entire packet. This catches padding. |
j.*\?n.*\?d.*\?i.*\? |
' j ' followed by zero or more of any character followed by ' n ' followed by zero or more of any character followed by ' d ' followed by zero or more of any character followed by ' i ' followed by zero or more of any character |
\\: | a literal ' : ' character |
/ | marks the end of the pattern to match |
s | a regex flag that lets the ' . ' character match newlines. |
\"; | concludes the PCRE check in our signature |
This is a relatively simple regex. I'm working on a more advanced regex to catch obfuscation by the use of various encoding techniques. Stay tuned!
CAVEAT
The ' | ' character shows up a lot in regexes as 'or' logic is common. Unfortunately, the ' | ' character is processed differently by configuration validation depending on how it is entered. If you create the command from a tmsh session, no special handling is required. If you issue a tmsh command from the bash shell, you must escape the ' | ' character with a ' \ ' character.
Ok from within tmsh:
(/Common)(tmos.security.protocol-inspection.signature)# create log4j-base64 sig "content:\"Base64\";nocase; pcre:\"/(\\$|(0\?44|([u0]00|x|(%|%25|[u0]00)78|%|[u0]0025|%25)24))/\"; pcre:\"/\\{|(0\?173|([u0]00|x|170|(%|045|%25|[u0]00)78|%|[u0]0025|%25)7b)/i\";distance: 1;" description "Apache Log4j attempt - Base64 encoding" service http
Fails from bash:
# tmsh create security protocol-inspection signature log4j-base64 sig "content:\"Base64\";nocase; pcre:\"/(\\$|(0\?44|([u0]00|x|(%|%25|[u0]00)78|%|[u0]0025|%25)24))/\"; pcre:\"/\\{|(0\?173|([u0]00|x|170|(%|045|%25|[u0]00)78|%|[u0]0025|%25)7b)/i\";distance: 1;" description "Apache Log4j attempt - Base64 encoding" service http Syntax Error: unexpected argument "|"
Succeeds from bash with ' | ' characters escaped.
# tmsh create security protocol-inspection signature log4j-base64 sig "content:\"Base64\";nocase; pcre:\"/(\\$\|(0\?44\|([u0]00\|x\|(%\|%25\|[u0]00)78\|%\|[u0]0025\|%25)24))/\";" description "Apache Log4j attempt - Base64 encoding" service http [root@affeld-221-afm-ltm-gtm-asm:Active:Standalone] config #