Learn F5 Technologies, Get Answers & Share Community Solutions Join DevCentral

Filter by:
  • Solution
  • Technology
Answers

Need help converting a small rule from 4x to 9x

I am totally not a programmer but have looked through the forums to try and work out the changes that would be needed. I am not even sure the original code is optimized. Basically I need some hand holding through all of this

Here is the working rule we have in 4x.

if (http_uri matches_regex ".*cmd\.exe.*" or http_uri matches_regex ".*root\.exe.*" or http_uri matches_regex ".*admin\.dll.*") {
discard
}
else if (not (http_method matches_regex "(^GET)|(^POST)|(^HEAD)|(^OPTIONS)")) {
discard
}
else {
use pool web_pool
}


Here is one of the many variations I have tried which all generate scope errors like the following. "command is not valid in the current scope"

if {expr [regex {.*cmd\.exe.*} [HTTP::uri]] || [regex {.*cmd\.exe.*} [HTTP::uri]]}{
discard
}
elseif {[regex {(^GET)|(^POST)|(^HEAD)|(^OPTIONS)")} $::http_methods] != 1} {
discard
}
else {
use pool web_pool
}

Also I read mention that regex is expensive but and not sure if contains would work instead.

Humble regards,

pwall
0
Rate this Question

Answers to this Question

placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Click here http://www.vj.com/f5

Check out the iRule summary...

The Operator you are looking for is ends_with. (In a matchclass test.)
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I was not sure that ends_with would work always work so I am trying contains. Would someone mind sanity checking this for me? I am no longer getting errors but once again I am not a coder and would really appreciate and experienced perspective on this.

Regards,

pwall

Here is what is in place now.


when HTTP_REQUEST {
if { [HTTP::uri] contains "cmd.exe." ||
[HTTP::uri] contains "root.exe." ||
[HTTP::uri] contains "admin.dll." } {
discard
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
reject
} else {
pool vipwc01_pool
}
}



Data Group List
Name valid_methods
Type string
POST
GET
HEAD
OPTIONS
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Check out this thread: http://devcentral.f5.com/Default.aspx?tabid=28&view=topic&forumid=5&postid=3568

-Brian
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Brian, I read the post 3568 "Subject: Using URI to rewrite host" and am not sure what I was suppose to take from it.

I am also having problems testing the valid methods HEAD method. I telnet to port 80 on a vip that has this iRule in place and when I try a POST, GET, or OPTIONS I get a reply but when I try a HEAD statement it drops the connection right away. This is not the behavior with the rule on my 4.5 BigIP's.
I have modified the data group list to only include HEAD and change the order around but it still drops the connection right away.

Here is a test to a 4.5 vip.

$ telnet 216.136.168.73 80
Trying 216.136.168.73...
Connected to 216.136.168.73.
Escape character is '^]'.
HEAD /rnt/rnw/css/enduser.css
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
</body></html>
Connection closed by foreign host.

Here is a test to the v9 vip

$ telnet 64.56.194.151 80
Trying 64.56.194.151...
Connected to 64.56.194.151.
Escape character is '^]'.
HEAD /rnt/rnw/css/enduser.css
Connection closed by foreign host.


Lastly I did one more edit and that was to remove the trailing dot after the .exe and .dll in the HTTP::uri lines.

when HTTP_REQUEST {
if { [HTTP::uri] contains "cmd.exe" ||
[HTTP::uri] contains "root.exe" ||
[HTTP::uri] contains "admin.dll" } {
discard
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
reject
} else {
pool vipwc01_pool
}
}

Data Group List
Name valid_methods
Type string
POST
GET
HEAD
OPTIONS
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
...
log local0. "HTTP Method: [HTTP::method]"
...


There is only so much we can do here from a distance in guessing what the problem is. The best approach when you have problems is to make sure you build a logic map of all possible inputs and add logging statements for your input variables as well as any control flows so that you can tell directly from the logs where you issue is.

-Joe
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Is this where I should put the logging?

when HTTP_REQUEST {
if { [HTTP::uri] contains "cmd.exe" ||
[HTTP::uri] contains "root.exe" ||
[HTTP::uri] contains "admin.dll" } {
discard
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
#log outcome to /var/log/ltm
log local0. "HTTP Method: [HTTP::method]"
reject
} else {
pool vipwc01_pool
}
}
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I would put log statements right before and after your discard statement.

-Brian
http://www.vj.com/f5 Click here

P.S. - I general log within each control context, so that I can verify the code is following the expected path of execution.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Around the discard or the reject?

Like so?

when HTTP_REQUEST {
if { [HTTP::uri] contains "cmd.exe" ||
[HTTP::uri] contains "root.exe" ||
[HTTP::uri] contains "admin.dll" } {
#log outcome to /var/log/ltm
log local0. "HTTP Method: [HTTP::method]"
discard
#log outcome to /var/log/ltm
log local0. "HTTP Method: [HTTP::method]"
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
reject
} else {
pool vipwc01_pool
}
}

Here is what I am seeing in the log with the way I was logging before. I do not see any HEAD methods.

Dec 13 06:36:00 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: BADMTHD
Dec 13 06:36:00 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: MKCOL
Dec 13 06:36:00 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: PUT
Dec 13 06:36:01 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: ABCD
Dec 13 06:36:03 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: CONNECT
Dec 13 06:36:19 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: TRACK
Dec 13 06:52:55 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: ABCD
Dec 13 06:52:55 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: QUALYS
Dec 13 06:52:55 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: TRACE
Dec 13 06:52:55 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: GET
Dec 13 06:52:55 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: PROPFIND
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I'd put the log statement at the top and then status messages in each of the conditions.

Change your code to the following:

when HTTP_REQUEST {
log local0. "HTTP Method: [HTTP::method]"
log local0. "HTTP Uri: [HTTP::uri]"
if { [HTTP::uri] contains "cmd.exe" ||
[HTTP::uri] contains "root.exe" ||
[HTTP::uri] contains "admin.dll" } {
log local0. "HTTP Uri is bad, discarding..."
discard
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
log local0. "HTTP Method not found in valid_methods list, rejecting..."
reject
} else {
log local0. "HTTP method found in valid_method list, defaulting to pool vipwc01_pool"
pool vipwc01_pool
}
}


This way you will know the inputs and all control paths that are reached. This will help with diagnosing your problem.

I just tested telnet on port 80 with and my log displayed the following for the method:

Dec 13 19:25:23 tmm tmm[1089]: Rule log_method <HTTP_REQUEST>: HTTP Method: HEAD


-Joe
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Thank you very much Joe. I will try this out.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I have changed the code to yours with all the logging and it looks like HEAD is not making it through.

$ telnet 64.56.194.151 80
Trying 64.56.194.151...
Connected to 64.56.194.151.
Escape character is '^]'.
HEAD /rnt/rnw/css/enduser.css
Connection closed by foreign host.

Here is from the log.
[root@lbwc01:Active] log # cat ltm |grep HEAD
Dec 13 14:03:59 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: HEAD


I have the following Data Group List defined as a STRING.

class valid_methods {
"GET"
"HEAD"
"OPTIONS"
"POST"
}

Any ideas?
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
What is the rest of the logging showing? I added log statements to each control path to let you know where the control is going. Is it displaying "HTTP Method not found..." or "HTTP Method found..."?

If it's showing "not found" then there is something odd going on in your code becuase I just tested the following rule:

class valid_methods  {
"GET"
"HEAD"
"OPTIONS"
"POST"
}


when HTTP_REQUEST {
log local0. "HTTP Method: [HTTP::method]"
log local0. "HTTP Uri: [HTTP::uri]"

if { not [matchclass [HTTP::method] equals $::valid_methods] } {
log local0. "HTTP Method not found in valid_methods"
} else {
log local0. "HTTP Method found in valid_methods"
}
}


And with the following inputs

$ telnet x.x.x.x 80
HEAD /rnt/rnw/css/enduser.css


it produced the expected output

Dec 13 20:43:27 tmm tmm[1089]: Rule log_method <HTTP_REQUEST>: HTTP Method: HEAD
Dec 13 20:43:27 tmm tmm[1089]: Rule log_method <HTTP_REQUEST>: HTTP Uri: /rnt/rnw/css/enduser.css
Dec 13 20:43:27 tmm tmm[1089]: Rule log_method <HTTP_REQUEST>: HTTP Method found in valid_methods


-Joe
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
I apologize! When I vi the log I see that it is going through.

Dec 13 14:03:59 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Method: HEAD
Dec 13 14:03:59 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP Uri: /rnt/rnw/css/enduser.css
Dec 13 14:03:59 tmm tmm[30208]: Rule security_rule <HTTP_REQUEST>: HTTP method found in valid_method list, defaulting to pool
vipwc01_pool

I will pull out the log lines.

Is there anything you would change in the code to make it better? Like take less overhead by using something other than "contains" or even if there are other uri vulnerabilities I should watch out for.

Thanks again.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
You could use a class for your uri strings as well. Since you only have a few, that's probably not that big a deal but it makes it more manageable. Also, it makes it very easy to add more cases to your first "contains" check by just adding the value to the class.

Oh, another think you might put in is a express change to lowercase on the URI before the comparison. This way a call with "Root.exe" won't pass through (contains is case-sensitive).

class bad_uris {
"cmd.exe"
"root.exe"
"admin.dll"
}
class valid_methods {
"GET"
"HEAD"
"OPTIONS"
"POST"
}

when HTTP_REQUEST {
#log local0. "HTTP Method: [HTTP::method]"
#log local0. "HTTP Uri: [HTTP::uri]"
if { [matchclass [string tolower [HTTP::uri]] contains $::bad_uris] } {
#log local0. "HTTP Uri is bad, discarding..."
discard
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
#log local0. "HTTP Method not found in valid_methods list, rejecting..."
reject
} else {
#log local0. "HTTP method found in valid_method list, defaulting to pool vipwc01_pool"
pool vipwc01_pool
}
}


Also, you could set your default pool on the virtual to vipwc01_pool (in the gui under the Virtual Server settings). Then you can eliminate the final else:

when HTTP_REQUEST {
#log local0. "HTTP Method: [HTTP::method]"
#log local0. "HTTP Uri: [HTTP::uri]"
if { [matchclass [string tolower [HTTP::uri]] contains $::bad_uris] } {
#log local0. "HTTP Uri is bad, discarding..."
discard
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
#log local0. "HTTP Method not found in valid_methods list, rejecting..."
reject
}
}


Finally, make sure you remove all logging (or comment it out as I've done above).

when HTTP_REQUEST {
if { [matchclass [string tolower [HTTP::uri]] contains $::bad_uris] } {
discard
} elseif { not [matchclass [HTTP::method] equals $::valid_methods] } {
reject
}
}


I don't think you can get much more optimal than that.

-Joe
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Joe, that is exactly what I needed. Thank you very much.
Please go down stairs to the Starbucks machine and have your self a double on me.

I know I am stretching this out pretty far but now that this code is optimized I am thinking about my 4.5 boxes. We have about ten 4.5 BigIP 5000 that are still working great but can not be upgraded to v9. They are running the origional rule below, which you just worked over, with all these matches_regex. What should I replace the regex with to make this 4.5 code more optimal?


if (http_uri matches_regex ".*cmd\.exe.*" or http_uri matches_regex ".*root\.exe.*" or http_uri matches_regex ".*admin\.dll.*") {
discard
}
else if (not (http_method matches_regex "(^GET)|(^POST)|(^HEAD)|(^OPTIONS)")) {
discard
}
else {
use pool web_pool
}
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
4.x also has classes and the contains and starts_with operators...

So, something like this should improve things alot:

First, make the same uri and method classes as you did for 9.x,
then use this rule:
if (http_uri contains one of bad_uris) {
discard
}
else if (not (http_method starts_with one of valid_methods)) {
discard
}
else {
use pool web_pool
}
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Thank you all for your detailed help with my rule. I really appreciate the thoroughness.
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
As a new member I would like to thank you all for your work on this iRule it was just what I was after, I wondered if was possible to expand this a little by decoding the URI converting it to lower case and then checking against the class list.

I don't have the experience to write this yet but thought that it would be of benefit by defending against the Code Red's of this world

Thanks for any help

Gary
0
placeholder+image
USER ACCEPTED ANSWER & F5 ACCEPTED ANSWER
Here's some code to get you started. The TCL builtin string tolower command will convert a string to lower case. You can then use the matchclass command with the contains operator to determine if the lowercase uri "contains" one of the items in the data group.

class hack_attempts {
"admin.dll"
"cmd.exe"
"root.exe"
}

#### Start Rule ####
when HTTP_REQUEST {
set uri [string tolower [HTTP::uri]]
if { [matchclass $uri contains $::hack_attempts] } {
log local0. "Found hack attempt! - [HTTP::uri]"
discard
} else {
log local0. "Valid uri: [HTTP::uri]"
}
}


Hope this helps...

-Joe
0