Forum Discussion

abhinay's avatar
abhinay
Icon for Nimbostratus rankNimbostratus
Dec 21, 2022

Request for providing help on setting up an iRule

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

  • 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
    }

     

  • 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. 

  • 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. 

    • NetOpsCommercial's avatar
      NetOpsCommercial
      Icon for Nimbostratus rankNimbostratus

      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"

       

  • 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 ? 

  • 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.

    • abhinay's avatar
      abhinay
      Icon for Nimbostratus rankNimbostratus

      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

  • 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
    }
    }
    }

     

    • abhinay's avatar
      abhinay
      Icon for Nimbostratus rankNimbostratus
      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
      }
      }
      }



       

      • CA_Valli's avatar
        CA_Valli
        Icon for MVP rankMVP

        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? 

  • 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's avatar
        CA_Valli
        Icon for MVP rankMVP

        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===#

         

         

         

  • 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
    }

     

    • abhinay's avatar
      abhinay
      Icon for Nimbostratus rankNimbostratus

      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"
      						}
      					]
      				},
  • 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.

    • abhinay's avatar
      abhinay
      Icon for Nimbostratus rankNimbostratus

      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"
      }
      }
      }

  • 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.

  • 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.