From 71045a77f25928afd109791a3320a5c18e895346 Mon Sep 17 00:00:00 2001 From: rileydakota Date: Fri, 12 Jul 2024 13:45:33 -0400 Subject: [PATCH 1/3] feat: add initial exec rule --- rules/aws_eks_rules/kubectl_exec.py | 92 ++++++++++++++++++++++++++++ rules/aws_eks_rules/kubectl_exec.yml | 22 +++++++ 2 files changed, 114 insertions(+) create mode 100644 rules/aws_eks_rules/kubectl_exec.py create mode 100644 rules/aws_eks_rules/kubectl_exec.yml diff --git a/rules/aws_eks_rules/kubectl_exec.py b/rules/aws_eks_rules/kubectl_exec.py new file mode 100644 index 000000000..b5ebd459c --- /dev/null +++ b/rules/aws_eks_rules/kubectl_exec.py @@ -0,0 +1,92 @@ +import urllib.parse +from panther_base_helpers import deep_get + +# update to username value for mapping in aws-auth configmap or eks access entries api +# docs: https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html#creating-access-entries +CLUSTER_ADMIN_USERNAMES = [] + +# update to production environment source labels +PROD_SOURCE_LABELS = [] + +def get_exec_command(request_uri: str) -> str: + """Takes the requesturi from an Kubernetes Audit Log event for + an exec call and url decodes it, then returns it as a string + + example input: + + "/api/v1/namespaces/defectdojo/pods/defectdojo-postgresql-0/exec?command=sh&command=-c&command=command+-v+bash+%3E%2Fdev%2Fnull+%26%26+exec+bash+%7C%7C+exec+sh&container=postgresql&stdin=true&stdout=true&tty=true" + + example output: + + "sh -c command -v bash >/dev/null && exec bash || exec sh" + + Args: + request_uri (str): The request + + Returns: + str: _description_ + """ + + decoded_uri = urllib.parse.unquote(request_uri) + parsed_uri = urllib.parse.urlparse(decoded_uri) + + if not parsed_uri.path.startswith("/api/v1/namespaces"): + return "" + + if not parsed_uri.path.endswith("/exec"): + return "" + + query_params = urllib.parse.parse_qs(parsed_uri.query) + exec_command = "" + + if "command" in query_params: + exec_command = " ".join(query_params["command"]) + + return exec_command + + +def rule(event): + k8s_username = deep_get(event, "user", "username") + verb = event.get("verb") + source_label = event.get("p_source_label") + subresource = deep_get(event, "objectref", "subresource") + + return ( + subresource == "exec" + and k8s_username in CLUSTER_ADMIN_USERNAMES + and source_label in PROD_SOURCE_LABELS + and verb == "create" + ) + +def title(event): + user = deep_get(event, "user", "extra", "sessionName", default=["NOT FOUND"])[0] + k8s_username = deep_get(event, "user", "username") + + return f"Exec into production EKS pod detected user: {user} as {k8s_username}" + + +def alert_context(event): + request_uri = event.get("requesturi") + + namespace = deep_get(event, "objectref", "namespace") + resource_name = deep_get(event, "objectref", "name") + command = get_exec_command(request_uri) + audit_log_timestamp = event.get("requestreceivedtimestamp") + useragent = event.get("useragent") + + return { + "namespace": namespace, + "resource_name": resource_name, + "command": command, + "audit_log_timestamp": audit_log_timestamp, + "useragent": useragent, + } + + +def dedup(event): + namespace = deep_get(event, "objectref", "namespace") + resource_name = deep_get(event, "objectref", "name") + useragent = event.get("useragent") + user = deep_get(event, "user", "extra", "sessionName", default="NOT FOUND") + + return f"{namespace}-{resource_name}-{user[0]}-{useragent}" \ No newline at end of file diff --git a/rules/aws_eks_rules/kubectl_exec.yml b/rules/aws_eks_rules/kubectl_exec.yml new file mode 100644 index 000000000..f3687e355 --- /dev/null +++ b/rules/aws_eks_rules/kubectl_exec.yml @@ -0,0 +1,22 @@ +AnalysisType: rule +Filename: kubectl_exec.py +RuleID: "Amazon.EKS.Audit.Exec" +DisplayName: "User exec into running container on Amazon EKS" +Enabled: true +LogTypes: + - Amazon.EKS.Audit +Tags: + - EKS +Reports: + MITRE ATT&CK: + - "TA0002:T1609" +Reference: https://aws.github.io/aws-eks-best-practices/security/docs/detective/ +Severity: Info +Description: > # (Optional) + This detection indicates an identity has used their credentials to create an interactive session with a runner container on the cluster. +DedupPeriodMinutes: 30 # The amount of time in minutes for grouping alerts (Optional, defaults to 60) +Threshold: 10 # The minimum number of event matches prior to an alert sending (Optional, defaults to 1) +SummaryAttributes: # A list of fields in the event to create top 5 summaries for (Optional) + - user:username + - p_any_ip_addresses + - p_source_label From 29269c967dc807c4b4842f6e93dec8458f69e598 Mon Sep 17 00:00:00 2001 From: rileydakota Date: Fri, 12 Jul 2024 14:17:39 -0400 Subject: [PATCH 2/3] format: linting --- rules/aws_eks_rules/kubectl_exec.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rules/aws_eks_rules/kubectl_exec.py b/rules/aws_eks_rules/kubectl_exec.py index b5ebd459c..90b9e4e8e 100644 --- a/rules/aws_eks_rules/kubectl_exec.py +++ b/rules/aws_eks_rules/kubectl_exec.py @@ -6,7 +6,7 @@ CLUSTER_ADMIN_USERNAMES = [] # update to production environment source labels -PROD_SOURCE_LABELS = [] +PROD_SOURCE_LABELS = [] def get_exec_command(request_uri: str) -> str: """Takes the requesturi from an Kubernetes Audit Log event for @@ -14,7 +14,9 @@ def get_exec_command(request_uri: str) -> str: example input: - "/api/v1/namespaces/defectdojo/pods/defectdojo-postgresql-0/exec?command=sh&command=-c&command=command+-v+bash+%3E%2Fdev%2Fnull+%26%26+exec+bash+%7C%7C+exec+sh&container=postgresql&stdin=true&stdout=true&tty=true" + "/api/v1/namespaces/n/pods/pod/exec?command=sh&command=-c + &command=command+-v+bash+%3E%2Fdev%2Fnull+%26%26+exec+bash+%7C%7C+exec+sh&container=pod + &stdin=true&stdout=true&tty=true" example output: @@ -89,4 +91,4 @@ def dedup(event): useragent = event.get("useragent") user = deep_get(event, "user", "extra", "sessionName", default="NOT FOUND") - return f"{namespace}-{resource_name}-{user[0]}-{useragent}" \ No newline at end of file + return f"{namespace}-{resource_name}-{user[0]}-{useragent}" From 6340e08addad7a817c9487fa2757ad440d383291 Mon Sep 17 00:00:00 2001 From: rileydakota Date: Fri, 12 Jul 2024 14:20:33 -0400 Subject: [PATCH 3/3] format: import order --- rules/aws_eks_rules/kubectl_exec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/aws_eks_rules/kubectl_exec.py b/rules/aws_eks_rules/kubectl_exec.py index 90b9e4e8e..f4473d3e2 100644 --- a/rules/aws_eks_rules/kubectl_exec.py +++ b/rules/aws_eks_rules/kubectl_exec.py @@ -1,5 +1,5 @@ -import urllib.parse from panther_base_helpers import deep_get +import urllib.parse # update to username value for mapping in aws-auth configmap or eks access entries api # docs: https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html#creating-access-entries