Technical Forum
Ask questions. Discover Answers.
cancel
Showing results for 
Search instead for 
Did you mean: 

Request for providing help on setting up an iRule

abhinay
Nimbostratus
Nimbostratus

Hi All,

Can you please let me know how can I accomplish the below requirement with an iRule.

Any requests that use any method and have "cs.exe" or "llisapi.dll" in the URI and also have a query string or form post variable called "fInArgs" that contains the string "%3D%23" should return 403 Forbidden.

Thanks,

Abhinay

2 ACCEPTED SOLUTIONS

mihaic
MVP
MVP

@abhinay  please share how you test in postman. 
I've tried and it works if the POST body is raw type and looks like this : fInArgs=%3D%23
This is what rules I am using:

when HTTP_REQUEST { 
if { ([class match [HTTP::uri] contains example_uri_1]) and ( [HTTP::query] contains "%3D%23") }{
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
log local0. "deny URI: [HTTP::uri] query:[HTTP::query]"
}
if {[HTTP::method] eq "POST"}{
# Trigger collection for up to 1MB of data
if {[HTTP::header "Content-Length"] ne "" && [HTTP::header "Content-Length"] <= 1048576}{
set content_length [HTTP::header "Content-Length"]
} else {
set content_length 1048576
}
# Check if $content_length is not set to 0
if { $content_length > 0} {
HTTP::collect $content_length
}
}
}
when HTTP_REQUEST_DATA {

if { [HTTP::method] equals "POST" }{
# Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
set http_request_body "?[HTTP::payload]"
log local0. "http payload: $http_request_body"
# Try to parse type value from the HTTP request body.
if { [URI::query $http_request_body fInArgs] equals "%3D%23" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
} }
}

if you use application/x-www-form-urlencoded you will have to match this "%253D%2523"

if { [URI::query $http_request_body fInArgs] equals "%253D%2523" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}

or use URI::decode :

if { [URI::decode [URI::query $http_request_body fInArgs]] equals "%3D%23" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}

 and if it is a form-data:

set varB [findstr [HTTP::payload] "fInArgs"]
if { $varB contains "%3D%23" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}

 

View solution in original post

I noticed from other comments in this thread that variable name is fInArgs with an uppercase "i".

Variable name in my code has a lowercase "L" -- I must have read that wrong before. If you just copy/pasted and didn't fix it, it might not match because of this. 

Otherwise, I'd expect it to work -- it does in my lab. 

View solution in original post

21 REPLIES 21

mihaic
MVP
MVP

Here is an example:

ltm data-group internal example_uri_1 {
records {
cs.exe { }
llisapi.dll { }
}
type string
}
when HTTP_REQUEST {
         if { ([matchclass [string tolower [HTTP::uri]] contains example_uri_1]) and ([string tolower [HTTP::query]] contains "%3D%23") }{
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache

}
}

For the form post variable, I think you need to have APM. 

Thanks for your response mihaic.

 

I see that you have created a data-group for URIs and calling it in the HTTP_Request along with the HTTP Query string which contains "%3D%23". 

Can you please let me know how to use this post variable called "fInArgs" in which I can check the string "%3D%23"

 

mihaic
MVP
MVP

can you provide what HTTP payload looks like? 

As far as I understand you need to look for some variable and its value in the HTTP payload.

yes @mihaic, the variable I am looking at is "fInArgs" that contains the string "%3D%23"

Tried your iRule below but this is not working in the HTTP payload

Hi @abhinay , 
As @mihaic figured out his iRule , it should meet your requirements. 
I want to add only this part to specify the ( Query string name and value ) in Requests 
you can add to @mihaic iRule : 

[URI::query [HTTP::uri] "fInArgs"] equals "%3D%23"]

#add this part after and operator in iRule instead of ([string tolower[HTTP::query]...)
it is only to be more specified
 

I think @NetOpsCommercial asks for this part , is not it. 

@mihaic , Did you simulated this irule and get its results ? 

_______________________
Regards
Mohamed Kansoh

mihaic
MVP
MVP

@Mohamed_Ahmed_Kansoh  is right, we need to remove "string tolower" part.

I only tested that the syntax is ok. The functionality, no. Did not have time yet.

Here is the irule with the variable also:(not functional tested):

ltm data-group internal example_uri_1 {
records {
cs.exe { }
llisapi.dll { }
}
type string
}

when HTTP_REQUEST {
if { ([matchclass [string tolower [HTTP::uri]] contains example_uri_1]) and ( [HTTP::query] contains "%3D%23") }{
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache

}
}


when HTTP_REQUEST_DATA {

if { [HTTP::method] equals "POST" }{
# Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
set http_request_body "?[HTTP::payload [HTTP::header value "Content-Length"]]"
# Try to parse type value from the HTTP request body.
if { [URI::decode [URI::query $request_body fInArgs]] equals "%3D%23" } {
log local0. "fInArgs : $fInArgs"
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}
}
}

 

@mihaic, URI along with query that contains "%3D%23" combination is working but the below payload with the body that contains %3D%23 doesnt seem to work. As we are decoding the query I tried "=#" too but no luck.
Any suggestions would be appreaciated

when HTTP_REQUEST_DATA {

if { [HTTP::method] equals "POST" }{
# Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
set http_request_body "?[HTTP::payload [HTTP::header value "Content-Length"]]"
# Try to parse type value from the HTTP request body.
if { [URI::decode [URI::query $request_body fInArgs]] equals "%3D%23" } {
log local0. "fInArgs : $fInArgs"
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}
}
}



 

I'm noticing this code is missing an HTTP::collect statement, so HTTP_REQUEST_DATA event will never be fired. 
@abhinay  Have you checked my other response yet? 

hi @CA_Valli, I did check your response and tried to implement it but was getting syntax errors. I think a } bracket and ] bracket were missing. Added it but still getting the error. I will have to check the syntax once.

Also there are 2 requirements.

1. URI contains cs.exe or llisapi.dll in the URI and query contains "%3D%23" - This is working

2. Request body value "fInArgs" which contains "%3D%23" - This is not working

 

Do you have any suggestions please.?

CA_Valli
MVP
MVP

Hello @abhinay ,
I've recently implemented a similar check on one of my customers. Try the following:

 

when HTTP_REQUEST { 
 if { [HTTP::uri] contains "cs.exe" || [HTTP::uri] contains "llisapi.dll" }{ 

    if { [URI::decode [HTTP::query]] contains "flnArgs" }{ 
     set value [findstr [URI::decode [HTTP::query] "flnArgs" 7 &]
	 if { $value contains "#=" }{
	   log local0. "violation detected: flnArgs=$value"  
	   HTTP::respond 403 content "Forbidden" 
	   
	   
	 } else {
     # if URI does not contain parameter, I capture traffic (up to 1MB) and perform this check again on full payload

       if {[HTTP::header "Content-Length"] ne "" && [HTTP::header "Content-Length"] <= 1048576}{ 
         set content_length [HTTP::header "Content-Length"]
       } else { set content_length 1048576 }
       if { $content_length > 0} { HTTP::collect $content_length ; set checkpayload 1 }
	   
     }
  }
}
when HTTP_REQUEST_DATA {
  if { $checkpayload }{ 
    set cleanpl [URI::decode [HTTP::payload]]
    set value [findstr $cleanpl "flnArgs" 7 &]
	if { $value contains "#=" }{
	   log local0. "violation detected: flnArgs=$value" 
	   HTTP::respond 403 content "Forbidden" 
	}
  }	
}

 

Notice that I'm decoding URI so %3D%23 will be normalized to #= , this match is more effective IMO

@CA_Valli Attached error is what I am seeing.

Oops, you're right - I've lost a couple parenthesis in the copy/paste, also #= should be inverted to =#
Sorry! Fixed below. 

 

 

when HTTP_REQUEST { 
 if { [HTTP::uri] contains "cs.exe" || [HTTP::uri] contains "llisapi.dll" }{ 

    if { [URI::decode [HTTP::query]] contains "flnArgs" }{ 
     set value [findstr [URI::decode [HTTP::query]] "flnArgs" 7 &]
	 if { $value contains "=#" }{
	   log local0. "violation detected: flnArgs=$value"  
	   HTTP::respond 403 content "Forbidden" 
	 }  
	} else {
     # if URI does not contain parameter, I capture traffic (up to 1MB) and perform this check again on full payload

       if {[HTTP::header "Content-Length"] ne "" && [HTTP::header "Content-Length"] <= 1048576}{ 
         set content_length [HTTP::header "Content-Length"]
       } else { set content_length 1048576 }
       if { $content_length > 0} { HTTP::collect $content_length ; set checkpayload 1 }
	   
     }
  }
}
when HTTP_REQUEST_DATA {
  if { $checkpayload }{ 
    set cleanpl [URI::decode [HTTP::payload]] 
    set value [findstr $cleanpl "flnArgs" 7 &]
	if { $value contains "=#" }{
	   log local0. "violation detected: flnArgs=$value" 
	   HTTP::respond 403 content "Forbidden" 
	}
  }	
}

 

 

seems to work in my lab

CA_Valli_0-1671642654689.png

 

Dec 21 20:14:52 bigip info tmm6[11336]: Rule /Common/iRule_DC <HTTP_REQUEST>: violation detected: flnArgs===#
Dec 21 20:14:55 bigip info tmm[11336]: Rule /Common/iRule_DC <HTTP_REQUEST_DATA>: violation detected: flnArgs===#

 

 

 

one more little thing I just noticed from logs, you see I'm specifying "flnArgs=%3D%23" when making the request but $value is " ==# " -- that's because at line #5 and at line #24 I'm not escaping the first "=" match (before the %3D). 

If you want to escape it, change 7 to 8 in said lines.  Otherwise, it will ALSO match anything that contains "flnArgs=%23" since the first "=" character is the equivalent of %3D  

@CA_Valli Thanks for the detailed information.

I used the same iRule but for some reason it is not working from Postman.

I tried using the curl and see that it is getting through for both GET and POST and not seeing any Forbidden page.

I noticed from other comments in this thread that variable name is fInArgs with an uppercase "i".

Variable name in my code has a lowercase "L" -- I must have read that wrong before. If you just copy/pasted and didn't fix it, it might not match because of this. 

Otherwise, I'd expect it to work -- it does in my lab. 

mihaic
MVP
MVP

@abhinay  please share how you test in postman. 
I've tried and it works if the POST body is raw type and looks like this : fInArgs=%3D%23
This is what rules I am using:

when HTTP_REQUEST { 
if { ([class match [HTTP::uri] contains example_uri_1]) and ( [HTTP::query] contains "%3D%23") }{
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
log local0. "deny URI: [HTTP::uri] query:[HTTP::query]"
}
if {[HTTP::method] eq "POST"}{
# Trigger collection for up to 1MB of data
if {[HTTP::header "Content-Length"] ne "" && [HTTP::header "Content-Length"] <= 1048576}{
set content_length [HTTP::header "Content-Length"]
} else {
set content_length 1048576
}
# Check if $content_length is not set to 0
if { $content_length > 0} {
HTTP::collect $content_length
}
}
}
when HTTP_REQUEST_DATA {

if { [HTTP::method] equals "POST" }{
# Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
set http_request_body "?[HTTP::payload]"
log local0. "http payload: $http_request_body"
# Try to parse type value from the HTTP request body.
if { [URI::query $http_request_body fInArgs] equals "%3D%23" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
} }
}

if you use application/x-www-form-urlencoded you will have to match this "%253D%2523"

if { [URI::query $http_request_body fInArgs] equals "%253D%2523" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}

or use URI::decode :

if { [URI::decode [URI::query $http_request_body fInArgs]] equals "%3D%23" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}

 and if it is a form-data:

set varB [findstr [HTTP::payload] "fInArgs"]
if { $varB contains "%3D%23" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}

 

@mihaic, small change in URI from "cs.exe" to "/cs". I dont mind having two different iRules for GET and POST.

Below GET is working abosultely fine

ltm data-group internal uri_list {
records {
/cs { }
llisapi.dll { }
}
type string
}

when HTTP_REQUEST {
if { ([matchclass [string tolower [HTTP::uri]] contains uri_list]) and ( [HTTP::query] contains "%3D%23") }{
HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"

}
}

For POST, I have verified internally and got the postman and see that the key is "_fInArgs=" and it should contain "=#". I have modified your iRule with these values but still fails.

The Postman body is below. Please let me know if anything else is needed.

				"body": {
					"mode": "urlencoded",
					"urlencoded": [
						{
							"key": "_ApiName",
							"value": "foo",
							"type": "default"
						},
						{
							"key": "_fInArgs=",
							"value": "A<1,?,'_ApiName'='SessionFree','_ConnectionName'=#AAAA>",
							"type": "default"
						}
					]
				},

mihaic
MVP
MVP

from what you shared  the the key is "_fInArgs= " and the value is not what you asked for . it is not "%3D%23"

"key": "_fInArgs=",
							"value": "A<1,?,'_ApiName'='SessionFree','_ConnectionName'=#AAAA>",

 the format you shared seems to be a JSON 

try to put something from the value.

@mihaic Thats right Sorry for the confusion, an Internal team was testing this earlier. I got the Postman file from them and checked that the key and value were different. 

I already made changes and tested for body with "_fInArgs=" that contains "=#" which did not work tried "_fInArgs=" that contains "SessionFree" which did not work either. your help will be appreciated.

when HTTP_REQUEST_DATA {

if { [HTTP::method] equals "POST" }{
# Extract the entire HTTP request body and escape it to become a HTTP::uri string (for easier parsings)
set http_request_body "?[HTTP::payload [HTTP::header value "Content-Length"]]"
# Try to parse type value from the HTTP request body.
if { [URI::decode [URI::query $request_body _fInArgs=]] contains "=#" } {
log local0. "_fInArgs= : $_fInArgs="
HTTP::respond 403 content "Forbidden" "Content-Type" "text/html"
}
}
}

mihaic
MVP
MVP

for it works this:

set token [lindex [ regexp -inline -- {"value": "(.+)"} [HTTP::payload] ] 1]
if { $token contains "SessionFree" } {
log local0. "token: $token"
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
} }

 But the problem is that it checkes everything starting teh first "value" it finds till the end.

I could not yet do a search only for key "_fInArgs=" and match only something in its value.

As this is a json format, we need a way to parse json.

mihaic
MVP
MVP

here is a better way.

set varB [findstr [HTTP::payload] "fInArgs=" 33 type]
if { $varB contains "SessionFree" } {
HTTP::respond 403 content "You don't have authorization to view this page. Access Denied" noserver Content-Type text/html Connection Close Cache-Control no-cache
}

 I've managed to extract the value field.

This works for me.