From eac407010f5db88fab9f227d474ff707bc5cf50d Mon Sep 17 00:00:00 2001 From: Ariel Ropek <79653153+arielkr256@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:45:51 -0700 Subject: [PATCH 1/6] Revert "Revert "EKS Anonymous API Access Detection Rule (#1405)" (#1429)" This reverts commit af950a2ecbc852d7eed3a34368f597e098f8a405. --- packs/aws.yml | 1 + rules/aws_eks_rules/anonymous_api_access.py | 30 +++++ rules/aws_eks_rules/anonymous_api_access.yml | 132 +++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 rules/aws_eks_rules/anonymous_api_access.py create mode 100644 rules/aws_eks_rules/anonymous_api_access.yml diff --git a/packs/aws.yml b/packs/aws.yml index c4591af93..820a4a43f 100644 --- a/packs/aws.yml +++ b/packs/aws.yml @@ -144,6 +144,7 @@ PackDefinition: - AWS.CMK.KeyRotation - AWS.DynamoDB.TableTTLEnabled - AWS.EC2.Vulnerable.XZ.Image.Launched + - Amazon.EKS.AnonymousAPIAccess - AWS.IAM.Policy.DoesNotGrantAdminAccess - AWS.IAM.Policy.DoesNotGrantNetworkAdminAccess - AWS.IAM.Resource.DoesNotHaveInlinePolicy diff --git a/rules/aws_eks_rules/anonymous_api_access.py b/rules/aws_eks_rules/anonymous_api_access.py new file mode 100644 index 000000000..b85f24fde --- /dev/null +++ b/rules/aws_eks_rules/anonymous_api_access.py @@ -0,0 +1,30 @@ +from panther_aws_helpers import eks_panther_obj_ref + + +def rule(event): + # Check if the username is set to "system:anonymous", which indicates anonymous access + p_eks = eks_panther_obj_ref(event) + if p_eks.get("actor") == "system:anonymous": + return True + return False + + +def title(event): + p_eks = eks_panther_obj_ref(event) + return ( + f"Anonymous API access detected on Kubernetes API server " + f"from [{p_eks.get('sourceIPs')[0]}] to [{p_eks.get('resource')}] " + f"in namespace [{p_eks.get('ns')}] on [{p_eks.get('p_source_label')}]" + ) + + +def dedup(event): + p_eks = eks_panther_obj_ref(event) + return f"anonymous_access_{p_eks.get('p_source_label')}_{p_eks.get('sourceIPs')[0]}" + + +def alert_context(event): + p_eks = eks_panther_obj_ref(event) + mutable_event = event.to_dict() + mutable_event["p_eks"] = p_eks + return dict(mutable_event) diff --git a/rules/aws_eks_rules/anonymous_api_access.yml b/rules/aws_eks_rules/anonymous_api_access.yml new file mode 100644 index 000000000..e8f2548fb --- /dev/null +++ b/rules/aws_eks_rules/anonymous_api_access.yml @@ -0,0 +1,132 @@ +AnalysisType: rule +Filename: anonymous_api_access.py +RuleID: "Amazon.EKS.AnonymousAPIAccess" +DisplayName: "EKS Anonymous API Access Detected" +Enabled: true +LogTypes: + - Amazon.EKS.Audit +Severity: Medium +Reports: + MITRE ATT&CK: + - "TA0001:T1190" # Initial Access: Exploit Public-Facing Application +Description: > + This rule detects anonymous API requests made to the Kubernetes API server. + In production environments, anonymous access should be disabled to prevent + unauthorized access to the API server. +DedupPeriodMinutes: 60 +Reference: + https://kubernetes.io/docs/reference/access-authn-authz/authentication/#anonymous-requests +Runbook: > + Check the EKS cluster configuration and ensure that anonymous access + to the Kubernetes API server is disabled. This can be done by verifying the API + server arguments and authentication webhook configuration. +SummaryAttributes: + - user:username + - p_any_ip_addresses + - p_source_label +Tags: + - EKS + - Security Control + - API + - Initial Access:Exploit Public-Facing Application +Tests: + - Name: Anonymous API Access + ExpectedResult: true + Log: + { + "annotations": { + "authorization.k8s.io/decision": "allow", + "authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding system:public-info-viewer" + }, + "apiVersion": "audit.k8s.io/v1", + "auditID": "abcde12345", + "kind": "Event", + "level": "Request", + "objectRef": { + "apiVersion": "v1", + "name": "test-pod", + "namespace": "default", + "resource": "pods" + }, + "p_any_aws_account_ids": [ + "123412341234" + ], + "p_any_aws_arns": [ + "arn:aws:iam::123412341234:role/DevAdministrator" + ], + "p_any_ip_addresses": [ + "8.8.8.8" + ], + "p_any_usernames": [ + "system:anonymous" + ], + "p_event_time": "2022-11-29 00:09:04.38", + "p_log_type": "Amazon.EKS.Audit", + "p_parse_time": "2022-11-29 00:10:25.067", + "p_row_id": "2e4ab474b0f0f7a4a8fff4f014a9b32a", + "p_source_id": "4c859cd4-9406-469b-9e0e-c2dc1bee24fa", + "p_source_label": "example-cluster-eks-logs", + "requestReceivedTimestamp": "2022-11-29 00:09:04.38", + "requestURI": "/api/v1/namespaces/default/pods/test-pod", + "responseStatus": { + "code": 200 + }, + "sourceIPs": [ + "8.8.8.8" + ], + "stage": "ResponseComplete", + "user": { + "username": "system:anonymous" + }, + "userAgent": "kubectl/v1.25.4" + } + - Name: Non-Anonymous API Access + ExpectedResult: false + Log: + { + "annotations": { + "authorization.k8s.io/decision": "allow", + "authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding system:public-info-viewer" + }, + "apiVersion": "audit.k8s.io/v1", + "auditID": "abcde12345", + "kind": "Event", + "level": "Request", + "objectRef": { + "apiVersion": "v1", + "name": "test-pod", + "namespace": "default", + "resource": "pods" + }, + "p_any_aws_account_ids": [ + "123412341234" + ], + "p_any_aws_arns": [ + "arn:aws:iam::123412341234:role/DevAdministrator" + ], + "p_any_ip_addresses": [ + "8.8.8.8" + ], + "p_any_usernames": [ + "kubernetes-admin" + ], + "p_event_time": "2022-11-29 00:09:04.38", + "p_log_type": "Amazon.EKS.Audit", + "p_parse_time": "2022-11-29 00:10:25.067", + "p_row_id": "2e4ab474b0f0f7a4a8fff4f014a9b32a", + "p_source_id": "4c859cd4-9406-469b-9e0e-c2dc1bee24fa", + "p_source_label": "example-cluster-eks-logs", + "requestReceivedTimestamp": "2022-11-29 00:09:04.38", + "requestURI": "/api/v1/namespaces/default/pods/test-pod", + "responseStatus": { + "code": 200 + }, + "sourceIPs": [ + "8.8.8.8" + ], + "stage": "ResponseComplete", + "user": { + "username": "kubernetes-admin" + }, + "userAgent": "kubectl/v1.25.4" + } From 57daf49eb99cbdf9de9660698a52cc71b81bf195 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Nov 2024 14:02:48 -0700 Subject: [PATCH 2/6] tuning --- rules/aws_eks_rules/anonymous_api_access.py | 17 +++-- rules/aws_eks_rules/anonymous_api_access.yml | 65 ++++++++++++++++++++ 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/rules/aws_eks_rules/anonymous_api_access.py b/rules/aws_eks_rules/anonymous_api_access.py index b85f24fde..1674888ce 100644 --- a/rules/aws_eks_rules/anonymous_api_access.py +++ b/rules/aws_eks_rules/anonymous_api_access.py @@ -2,9 +2,16 @@ def rule(event): + if event.deep_get("annotations", "authorization.k8s.io/decision") != "allow": + return False + src_ip = event.get("sourceIPs", ["0.0.0.0"]) + if src_ip == ["127.0.0.1"]: + return False + if event.get("userAgent", "") == "ELB-HealthChecker/2.0" and src_ip[0].startswith("10.0."): + return False + # Check if the username is set to "system:anonymous", which indicates anonymous access - p_eks = eks_panther_obj_ref(event) - if p_eks.get("actor") == "system:anonymous": + if event.deep_get("user", "username") == "system:anonymous": return True return False @@ -13,14 +20,14 @@ def title(event): p_eks = eks_panther_obj_ref(event) return ( f"Anonymous API access detected on Kubernetes API server " - f"from [{p_eks.get('sourceIPs')[0]}] to [{p_eks.get('resource')}] " - f"in namespace [{p_eks.get('ns')}] on [{p_eks.get('p_source_label')}]" + f"from [{p_eks.get('sourceIPs')[0]}] to [{event.get('requestURI', 'NO_URI')}] " + f"on [{p_eks.get('p_source_label')}]" ) def dedup(event): p_eks = eks_panther_obj_ref(event) - return f"anonymous_access_{p_eks.get('p_source_label')}_{p_eks.get('sourceIPs')[0]}" + return f"anonymous_access_{p_eks.get('p_source_label')}_{event.get('userAgent')}" def alert_context(event): diff --git a/rules/aws_eks_rules/anonymous_api_access.yml b/rules/aws_eks_rules/anonymous_api_access.yml index e8f2548fb..f7ff96e78 100644 --- a/rules/aws_eks_rules/anonymous_api_access.yml +++ b/rules/aws_eks_rules/anonymous_api_access.yml @@ -130,3 +130,68 @@ Tests: }, "userAgent": "kubectl/v1.25.4" } + - Name: Anonymous API Access Web Scanner Allowed + ExpectedResult: true + Log: + { + "annotations": { + "authorization.k8s.io/decision": "allow", + "authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding \"system:public-info-viewer\" of ClusterRole \"system:public-info-viewer\" to Group \"system:unauthenticated\"" + }, + "apiVersion": "audit.k8s.io/v1", + "auditID": "d976bfc6-a2bc-49d5-bdeb-074441e0b875", + "kind": "Event", + "level": "Metadata", + "requestReceivedTimestamp": "2024-11-13 18:34:10.595141000", + "requestURI": "/version", + "responseStatus": { + "code": 200 + }, + "sourceIPs": [ + "44.238.138.237" + ], + "stage": "ResponseComplete", + "stageTimestamp": "2024-11-13 18:34:10.595494000", + "user": { + "groups": [ + "system:unauthenticated" + ], + "username": "system:anonymous" + }, + "userAgent": "python-requests/2.31.0", + "verb": "get" + } + - Name: Anonymous API Access Web Scanner Denied + ExpectedResult: false + Log: + { + "annotations": { + "authorization.k8s.io/decision": "forbid", + "authorization.k8s.io/reason": "" + }, + "apiVersion": "audit.k8s.io/v1", + "auditID": "edf35e8d-92c3-4507-9bc6-4dd9cf068bcf", + "kind": "Event", + "level": "Metadata", + "requestReceivedTimestamp": "2024-11-13 23:50:32.672347000", + "requestURI": "/vendor/phpunit/src/Util/PHP/eval-stdin.php", + "responseStatus": { + "code": 403, + "message": "forbidden: User \"system:anonymous\" cannot get path \"/vendor/phpunit/src/Util/PHP/eval-stdin.php\"", + "reason": "Forbidden", + "status": "Failure" + }, + "sourceIPs": [ + "8.216.81.10" + ], + "stage": "ResponseComplete", + "stageTimestamp": "2024-11-13 23:50:32.673504000", + "user": { + "groups": [ + "system:unauthenticated" + ], + "username": "system:anonymous" + }, + "userAgent": "Custom-AsyncHttpClient", + "verb": "get" + } From 9d55a6373003f0047d854d8c3649e6601cb2a9f7 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Nov 2024 14:04:42 -0700 Subject: [PATCH 3/6] ref --- rules/aws_eks_rules/anonymous_api_access.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/aws_eks_rules/anonymous_api_access.yml b/rules/aws_eks_rules/anonymous_api_access.yml index f7ff96e78..2e0feb681 100644 --- a/rules/aws_eks_rules/anonymous_api_access.yml +++ b/rules/aws_eks_rules/anonymous_api_access.yml @@ -15,7 +15,7 @@ Description: > unauthorized access to the API server. DedupPeriodMinutes: 60 Reference: - https://kubernetes.io/docs/reference/access-authn-authz/authentication/#anonymous-requests + https://raesene.github.io/blog/2023/03/18/lets-talk-about-anonymous-access-to-Kubernetes/ Runbook: > Check the EKS cluster configuration and ensure that anonymous access to the Kubernetes API server is disabled. This can be done by verifying the API From 1b33005170985d65599d5cbc75b950d7153b1a43 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Nov 2024 14:07:22 -0700 Subject: [PATCH 4/6] nosec --- rules/aws_eks_rules/anonymous_api_access.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/aws_eks_rules/anonymous_api_access.py b/rules/aws_eks_rules/anonymous_api_access.py index 1674888ce..9ccc3490e 100644 --- a/rules/aws_eks_rules/anonymous_api_access.py +++ b/rules/aws_eks_rules/anonymous_api_access.py @@ -4,7 +4,7 @@ def rule(event): if event.deep_get("annotations", "authorization.k8s.io/decision") != "allow": return False - src_ip = event.get("sourceIPs", ["0.0.0.0"]) + src_ip = event.get("sourceIPs", ["0.0.0.0"]) # nosec if src_ip == ["127.0.0.1"]: return False if event.get("userAgent", "") == "ELB-HealthChecker/2.0" and src_ip[0].startswith("10.0."): From d587c901320ff3388d04cdce0687e483d088d478 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Nov 2024 14:10:23 -0700 Subject: [PATCH 5/6] sev --- rules/aws_eks_rules/anonymous_api_access.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/aws_eks_rules/anonymous_api_access.yml b/rules/aws_eks_rules/anonymous_api_access.yml index 2e0feb681..171affa00 100644 --- a/rules/aws_eks_rules/anonymous_api_access.yml +++ b/rules/aws_eks_rules/anonymous_api_access.yml @@ -5,7 +5,7 @@ DisplayName: "EKS Anonymous API Access Detected" Enabled: true LogTypes: - Amazon.EKS.Audit -Severity: Medium +Severity: Low Reports: MITRE ATT&CK: - "TA0001:T1190" # Initial Access: Exploit Public-Facing Application From 24780efb0a7ac65d3dd0eb974ecd7edc2a0196ab Mon Sep 17 00:00:00 2001 From: Ariel Date: Wed, 4 Dec 2024 09:56:35 -0700 Subject: [PATCH 6/6] dynamic severity --- rules/aws_eks_rules/anonymous_api_access.py | 10 ++++++++-- rules/aws_eks_rules/anonymous_api_access.yml | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/rules/aws_eks_rules/anonymous_api_access.py b/rules/aws_eks_rules/anonymous_api_access.py index 9ccc3490e..e7e75f34a 100644 --- a/rules/aws_eks_rules/anonymous_api_access.py +++ b/rules/aws_eks_rules/anonymous_api_access.py @@ -2,8 +2,6 @@ def rule(event): - if event.deep_get("annotations", "authorization.k8s.io/decision") != "allow": - return False src_ip = event.get("sourceIPs", ["0.0.0.0"]) # nosec if src_ip == ["127.0.0.1"]: return False @@ -25,6 +23,14 @@ def title(event): ) +def severity(event): + if event.deep_get("annotations", "authorization.k8s.io/decision") != "allow": + return "INFO" + if event.get("requestURI") == "/version": + return "INFO" + return "DEFAULT" + + def dedup(event): p_eks = eks_panther_obj_ref(event) return f"anonymous_access_{p_eks.get('p_source_label')}_{event.get('userAgent')}" diff --git a/rules/aws_eks_rules/anonymous_api_access.yml b/rules/aws_eks_rules/anonymous_api_access.yml index 171affa00..96ff68577 100644 --- a/rules/aws_eks_rules/anonymous_api_access.yml +++ b/rules/aws_eks_rules/anonymous_api_access.yml @@ -162,7 +162,7 @@ Tests: "verb": "get" } - Name: Anonymous API Access Web Scanner Denied - ExpectedResult: false + ExpectedResult: true Log: { "annotations": {