20-Dec-2022 20:55
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
Solved! Go to Solution.
22-Dec-2022 02:50 - edited 22-Dec-2022 04:56
@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
}
27-Dec-2022 04:18 - edited 27-Dec-2022 04:19
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.
20-Dec-2022 23:06 - edited 20-Dec-2022 23:20
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.
20-Dec-2022 23:23
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"
20-Dec-2022 23:47
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.
21-Dec-2022 08:34
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
21-Dec-2022 01:03
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 ?
21-Dec-2022 01:47
@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
}
}
}
21-Dec-2022 07:30
@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
}
}
}
21-Dec-2022 07:47 - edited 21-Dec-2022 07:49
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?
21-Dec-2022 07:58
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.?
21-Dec-2022 02:15 - edited 21-Dec-2022 07:48
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
21-Dec-2022 08:36
21-Dec-2022 08:58 - edited 21-Dec-2022 09:15
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
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===#
21-Dec-2022 09:25 - edited 21-Dec-2022 09:28
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
21-Dec-2022 10:18
@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.
27-Dec-2022 04:18 - edited 27-Dec-2022 04:19
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.
22-Dec-2022 02:50 - edited 22-Dec-2022 04:56
@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
}
22-Dec-2022 06:06 - edited 22-Dec-2022 06:07
@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" } ] },
22-Dec-2022 07:12
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.
22-Dec-2022 07:34
@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"
}
}
}
22-Dec-2022 07:39
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.
22-Dec-2022 08:01
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.