Detection rules › Panther

AWS WAF Managed Known Bad Inputs Passthrough Rule

Severity
high
Log types
AWS.WAFWebACL
Tags
AWS, WAF, Managed Rules, Initial Access:Exploit Public-Facing Application, Execution:Command and Scripting Interpreter
Reference
https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html#aws-managed-rule-groups-baseline-known-bad-inputs
Source
github.com/panther-labs/panther-analysis

Detects AWS WAF Known Bad Inputs managed rule group matches. Covers Log4Shell (CVE-2021-44228), Java deserialization RCE, localhost Host header abuse, PROPFIND method, and exploitable paths.

MITRE ATT&CK coverage

Rule body yaml

AnalysisType: rule
Filename: aws_waf_managed_known_bad_inputs.py
RuleID: "AWS.WAF.Managed.KnownBadInputs"
DisplayName: "AWS WAF Managed Known Bad Inputs Passthrough Rule"
Enabled: true
LogTypes:
  - AWS.WAFWebACL
Tags:
  - AWS
  - WAF
  - Managed Rules
  - Initial Access:Exploit Public-Facing Application
  - Execution:Command and Scripting Interpreter
Reports:
  MITRE ATT&CK:
    - TA0001:T1190
    - TA0002:T1059
Severity: High
Description: >
  Detects AWS WAF Known Bad Inputs managed rule group matches. Covers Log4Shell (CVE-2021-44228),
  Java deserialization RCE, localhost Host header abuse, PROPFIND method, and exploitable paths.
Runbook: |
  1. Find all WAF log entries from httpRequest:clientIp in the 6 hours before and after this alert to identify exploitation attempts across multiple endpoints
  2. Check if httpRequest:clientIp appears in threat intelligence feeds or is associated with known exploit infrastructure
  3. If the action was ALLOW, search application and backend server logs for the targeted httpRequest:uri in the 1 hour after the alert to determine if exploitation was successful
Reference: https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html#aws-managed-rule-groups-baseline-known-bad-inputs
Tests:
  - Name: Log4Shell blocked via terminatingRuleId
    ExpectedResult: true
    Log:
      timestamp: "2024-03-20T10:30:00.000Z"
      webaclId: "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/test/a1b2c3d4"
      terminatingRuleId: "AWS-AWSManagedRulesKnownBadInputsRuleSet"
      terminatingRuleType: "MANAGED_RULE_GROUP"
      action: "BLOCK"
      httpSourceName: "ALB"
      httpRequest:
        clientIp: "203.0.113.45"
        country: "US"
        uri: "/api/endpoint"
        httpMethod: "POST"

  - Name: Java deserialization in ruleGroupList
    ExpectedResult: true
    Log:
      timestamp: "2024-03-20T10:35:00.000Z"
      webaclId: "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/test/a1b2c3d4"
      terminatingRuleId: "AWS-AWSManagedRulesKnownBadInputsRuleSet"
      action: "BLOCK"
      httpSourceName: "ALB"
      httpRequest:
        clientIp: "198.51.100.22"
        country: "CN"
        uri: "/api/v1/process"
        httpMethod: "POST"
      ruleGroupList:
        - ruleGroupId: "AWS#AWSManagedRulesKnownBadInputsRuleSet"
          terminatingRule:
            ruleId: "JavaDeserializationRCE_BODY"
            action: "BLOCK"

  - Name: Non-terminating Log4Shell match (COUNT mode)
    ExpectedResult: true
    Log:
      timestamp: "2024-03-20T10:40:00.000Z"
      webaclId: "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/test/a1b2c3d4"
      terminatingRuleId: "Default_Action"
      action: "ALLOW"
      httpSourceName: "CF"
      httpRequest:
        clientIp: "192.0.2.100"
        country: "RU"
        uri: "/login"
        httpMethod: "POST"
      ruleGroupList:
        - ruleGroupId: "AWS#AWSManagedRulesKnownBadInputsRuleSet"
          nonTerminatingMatchingRules:
            - ruleId: "Log4JRCE_BODY"
              action: "COUNT"

  - Name: Exploitable path via API Gateway
    ExpectedResult: true
    Log:
      timestamp: "2024-03-20T10:45:00.000Z"
      webaclId: "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/test/a1b2c3d4"
      terminatingRuleId: "AWS-AWSManagedRulesKnownBadInputsRuleSet"
      action: "BLOCK"
      httpSourceName: "APIGW"
      httpRequest:
        clientIp: "203.0.113.45"
        uri: "/.env"
        httpMethod: "GET"
      ruleGroupList:
        - ruleGroupId: "AWS#AWSManagedRulesKnownBadInputsRuleSet"
          terminatingRule:
            ruleId: "ExploitablePaths_URIPATH"
            action: "BLOCK"

  - Name: Different rule group - no alert
    ExpectedResult: false
    Log:
      timestamp: "2024-03-20T10:50:00.000Z"
      webaclId: "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/test/a1b2c3d4"
      terminatingRuleId: "AWS-AWSManagedRulesCommonRuleSet"
      action: "BLOCK"
      httpSourceName: "ALB"
      httpRequest:
        clientIp: "203.0.113.45"

  - Name: Normal traffic - no alert
    ExpectedResult: false
    Log:
      timestamp: "2024-03-20T10:55:00.000Z"
      webaclId: "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/test/a1b2c3d4"
      terminatingRuleId: "Default_Action"
      action: "ALLOW"
      httpSourceName: "ALB"
      httpRequest:
        clientIp: "198.51.100.10"

DedupPeriodMinutes: 60
Threshold: 1

Detection logic

Condition

nonTerminatingMatchingRules array_any or ruleGroupList array_any

This rule also runs imperative logic the parser cannot express as a filter; the conditions above are the structured part it could extract.

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
matched_ruleterminatingRuleId
client_iphttpRequest.clientIp
countryhttpRequest.country
http_methodhttpRequest.httpMethod
urihttpRequest.uri
action
sourcehttpSourceName
source_idhttpSourceId
terminating_rule_idterminatingRuleId
terminating_rule_typeterminatingRuleType