Shellshock mitigation with BIG-IP iRules
Yesterday, NIST released information on a new network exploitable vulnerability in the GNU Bash shell as demonstrated by vectors involving parts of OpenSSH sshd, the mod_cgi, and mod_cgid modules in the Apache HTTP Server, scripts executed by DHCP clients, and other situations where setting the environment around a bash process occurs. Vulnerability CVE-2014-6271, or Shellshock as everyone is calling it, allows remote attackers to execute arbitrary code on the target systems. Redhat has a great overview in their security blog posting on the topic.
What makes this vulnerability serious is the fact that server-side CGI applications running under the Apache Web Server with mod_cgi or mod_cgid are susceptible.
F5's Jeff Costlow is maintaining an article on the Shellshock vulnerability so make sure you check back to that article for F5's official stance.
The first step to mitigation is to setup a plan to patch the bash shell on all of your systems. In the interum, if you believe that any of your backend servers are vulnerable (if they are Unix-based then they likely are) and you are fronting them with a BIG-IP, below is an iRule solution you can apply to your Virtual Servers that will protect your servers against attacks.
Block-Shellshocked
The Block-Shellshock iRule searches for the pattern "*() {*" in the URI and in the HTTP headers. It is rare that this pattern of characters will occur in a URI or HTTP Header so we believe the number of false-positives will be minimal. If the pattern is found, an audit entry will be written to the system log with the client ip as well as the URI, and optionally the HTTP header, that the attack was detected. I chose to issue a reject on the connection. If you want to be more polite to the hackers, you can substitute the "reject" with a 403 - Forbidden.
"*() \{*"when HTTP_REQUEST { set pattern
; if { [string match $pattern [HTTP::uri]] } { log local0. "Detected CVE-2014-6271 attack from '[IP::client_addr]' in URI '[HTTP::uri]'"; reject; } else { foreach header_name [HTTP::header names] { foreach header_value [HTTP::header values $header_name] { if { [string match $pattern $header_value] } { log local0. "Detected CVE-2014-6271 attack from '[IP::client_addr]' in HTTP Header $header_name = '$header_value'; URI = '[HTTP::uri]'"; reject; break; } } } } }
Block-Shellshock-full
The first iRule gives a more detailed audit if the attack is detected. In the case where that is not a concern and you are more concerned with performance, the HTTP::request method can be used to perform a single search across all of the request header information. This does not cover the POST body, but at this point there is no indication that the exploit is exposed within the body portion of a POST request. This irule reports the client IP along with the requested URI that is the source of the attack.
"*() \{*"when HTTP_REQUEST { if { [string match
[HTTP::request]] } { log local0. "Detected CVE-2014-6271 attack from '[IP::client_addr]'; URI = '[HTTP::uri]'"; reject; } }
We will be monitoring this vulnerability over the coming weeks and update the iRule solution if a more optimal one becomes available. Please leave a comment if you have any suggestions. And, make sure you monitor Jeff's article for updates.
Special Considerations
Please be aware that this iRule will reject any connections containing the string "() {" in the URI or header. While we are confident that this is not a valid pattern for standard headers, there could be situations where custom application values contain this string pattern and this iRule would cause issues to those applications. We suggest running the longer iRule and actively monitoring the system log for false positives. If you find a case where your custom application is being blocked, you can customize the first iRule to exclude the header your application relies on.
- On the User-Agent front, I just ran through all the registered user agent strings for Safari, Opera, Chrome, Firefox, IE and the googlebot and msnbot search crawlers (~4600 different UA strings) and there were zero matches based on the "*(*)*{*" pattern.
- Shibu_NarendranNimbostratus@Joe, if there are applications using custom headers and values include this combinations, it will block it. Though I can't comment on the variety of http application out there, it's possible. Other case is custom applications - any http interface they use have their own user agents and it is usually not found in the published, well known lists. In any case, can you please comment on the use of custom signature in the ASM – by rejecting “() {“ pattern in header values? [for those has ASM].
- Gary_ZhuNimbostratusGreat discussion. On the concern of any custom header that might match, Joe's idea of emptying out that header or rewriting that header value would be good enough. Thank Joe for checking out patterns of user agent strings. So far, we have blocked several requests from Netherland.
- In doing some more testing I've believe I can further restrict my match pattern to "*()*{*;*}*". This should exclude most all User-Agent strings as I haven't found any with empty parenthesis followed by curly braces. I also plan on including URI decoding into the URI and header checks to try to make sure someone isn't encoding the values. I haven't been able to recreate the vuln by passing in encoded characters so I'm going to hold out on publishing that until I do. I would really like to hear of any false-positives anyone finds with the original match pattern.
- kollenh_51946Nimbostratus@Joe - per your request for false-positives, we implemented the second iRule and discovered all traffic through our Big-IP was being detected. Here is an example of the URI it detected as an attack: URI = '/ourfirm/offices/national/Mogul%20Winners/_w/Slalom.com%20Project%20Team%20-%20Q3_PNG.jpg'
- jpluna_169998NimbostratusI'm going to ask the completely noob question, forgive my ignorance. The Management Website is also vulnerable, correct? Is it possible to add this iRule to that site?
- @jpluna: An iRule can be applied to a virtual server only. In case you manage your F5 via network interfaces (not the management interface) it is possible to use a virtual server (SSL-termination & -reencryption) pointing to a node (local self IP to be used for inband management) via iRule. I would avoid to share the same IP address for the virtual server and self IP. Example below shows the required one-liner: when HTTP_REQUEST priority 600 { node 10.131.131.174 } You can add the other iRules to filter the requests.
- @kollenh - After more feedback and recent findings on the vulnerability, I've modified the pattern to look for the exact string of characters "() {" within a header. We believe there are no standard cases where this would match outside of a custom application. I've tested against all known user agents for the major browsers with a zero false-positive match. Could you try it out with the latest, more restrictive, pattern? If you are still getting matches, you might want to attempt the first iRule and log out exactly which header was causing triggering the block.
- ---UPDATE--- After receiving feedback and finding more details on the vulnerability, we've restricted the search pattern to search for containing the string "() {". This should greatly reduce the minimal amount of false positives that were found by custom applications but keep in mind that if you are doing something custom, that this pattern may still match so we suggest you use the longer iRule for initial analysis and auditing. The shorter iRule can be then run if/when performance is something you are concerned about.
- ConeZone_130022NimbostratusI put this first iRule on a VIP and the result was no one could connect to the Internet.