diff --git a/.github/workflows/check-packs.yml b/.github/workflows/check-packs.yml index 97cdf79a8..dd1349ac4 100644 --- a/.github/workflows/check-packs.yml +++ b/.github/workflows/check-packs.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 6c096a3e0..5243793ba 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -11,7 +11,7 @@ jobs: name: Build Dockerfile runs-on: ubuntu-latest steps: - - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 with: disable-sudo: true egress-policy: block @@ -28,10 +28,10 @@ jobs: www.python.org:443 - name: Checkout panther-analysis uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 - - uses: docker/setup-qemu-action@5927c834f5b4fdf503fca6f4c7eccda82949e1ee #v3.1.0 + - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf #v3.2.0 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@4fd812986e6c8c2a69e18311145f9371337f27d4 #v3.4.0 + uses: docker/setup-buildx-action@aa33708b10e362ff993539393ff100fa93ed6a27 #v3.5.0 - name: Build Image run: docker buildx build --load -f Dockerfile -t panther-analysis:latest . - name: Test Image diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f1c81e06e..1d0208580 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 971d361e7..e7fe544ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.PANTHER_BOT_AUTOMATION_TOKEN }} steps: - - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 702decec7..0b4511fa6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/upload.yml b/.github/workflows/upload.yml index d05afd7bb..2876225e5 100644 --- a/.github/workflows/upload.yml +++ b/.github/workflows/upload.yml @@ -14,7 +14,7 @@ jobs: API_HOST: ${{ secrets.API_HOST }} API_TOKEN: ${{ secrets.API_TOKEN }} steps: - - uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 + - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 with: egress-policy: audit - name: Validate Secrets diff --git a/global_helpers/panther_base_helpers.py b/global_helpers/panther_base_helpers.py index 856eb5115..ea7cbbfb8 100644 --- a/global_helpers/panther_base_helpers.py +++ b/global_helpers/panther_base_helpers.py @@ -521,9 +521,11 @@ def is_base64(b64: str) -> str: # handle false positives for very short strings if len(b64) < 12: return "" + # Pad args with "=" to ensure proper decoding + b64 = b64.ljust((len(b64) + 3) // 4 * 4, "=") # Check if the matched string can be decoded back into ASCII try: - return b64decode(b64).decode("ascii") + return b64decode(b64, validate=True).decode("ascii") except AsciiError: pass except UnicodeDecodeError: diff --git a/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.py b/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.py index 56b2d4683..1690a5e8f 100644 --- a/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.py +++ b/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.py @@ -25,8 +25,9 @@ def rule(event): # Split arguments from process path command_line_args = event.udm("cmd") - command_line_args = command_line_args.replace('"', "") - command_line_args = command_line_args.replace("'", "") + command_line_args = command_line_args.replace('"', " ") + command_line_args = command_line_args.replace("'", " ") + command_line_args = command_line_args.replace("=", " ") command_line_args = command_line_args.split(" ")[1:] # Check if Base64 encoded arguments are present in the command line diff --git a/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.yml b/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.yml index 049bd1ccf..41f7a8b98 100644 --- a/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.yml +++ b/rules/crowdstrike_rules/crowdstrike_base64_encoded_args.yml @@ -502,6 +502,67 @@ Tests: "timestamp": "2023-04-24 20:33:36.719", } - Name: Command Line Tool Execution without Base64 Argument (Negative) 6 + ExpectedResult: false + Log: + { + "ConfigBuild": "1007.3.0016606.11", + "ConfigStateHash": "3645117824", + "Entitlements": "15", + "TreeId": "4295752857", + "aid": "877761efa8db44d792ddc2redacted", + "aip": "1.1.1.1", + "cid": "cfe698690964434083fecdredacted", + "event": + { + "AuthenticationId": "293628", + "AuthenticodeHashData": "98a4762f52a", + "CommandLine": '"C:\Windows\system32\cmd.exe" /Q /C ""C:\Program Files (x86)\Google\GoogleUpdater\128.0.6537.0\uninstall.cmd" --dir="C:\Program Files (x86)\Google\GoogleUpdater\128.0.6537.0""', + "ConfigBuild": "1007.3.0016606.11", + "ConfigStateHash": "3645117824", + "EffectiveTransmissionClass": "2", + "Entitlements": "15", + "ImageFileName": "\\Device\\HarddiskVolume2\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "ImageSubsystem": "3", + "IntegrityLevel": "12288", + "MD5HashData": "c031e215b8b08c752bf362f6d4c5d3ad", + "ParentAuthenticationId": "293628", + "ParentBaseFileName": "pwsh.exe", + "ParentProcessId": "4370948876", + "ProcessCreateFlags": "1024", + "ProcessEndTime": "", + "ProcessParameterFlags": "24577", + "ProcessStartTime": "1682368414.719", + "ProcessSxsFlags": "64", + "RawProcessId": "3120", + "SHA1HashData": "0000000000000000000000000000000000000000", + "SHA256HashData": "840e1f9dc5a29bebf01626822d7390251e9cf05bb3560ba7b68bdb8a41cf08e3", + "SessionId": "2", + "SignInfoFlags": "8683538", + "SourceProcessId": "4370948876", + "SourceThreadId": "112532918543", + "Tags": "25, 27, 40, 151, 874, 924, 12094627905582, 12094627906234, 211106232533012, 263882790666253", + "TargetProcessId": "10413665481", + "TokenType": "1", + "TreeId": "4295752857", + "UserSid": "S-1-5-21-239183934-720705223-383019856-500", + "aid": "877761efa8db44d792ddc2redacted", + "aip": "1.1.1.1", + "cid": "cfe698690964434083fecdredacted", + "event_platform": "Win", + "event_simpleName": "ProcessRollup2", + "id": "b0c07877-f288-49f8-8cb3-150149a557b2", + "name": "ProcessRollup2V19", + "timestamp": "1682368416719", + }, + "event_platform": "Win", + "event_simpleName": "ProcessRollup2", + "fdr_event_type": "ProcessRollup2", + "id": "b0c07877-f288-49f8-8cb3-150149a557b2", + "name": "ProcessRollup2V19", + "p_log_type": "Crowdstrike.FDREvent", + "timestamp": "2023-04-24 20:33:36.719", + } + - Name: Command Line Tool Execution without Base64 Argument (Negative) 7 ExpectedResult: false Log: { @@ -562,3 +623,125 @@ Tests: "p_log_type": "Crowdstrike.FDREvent", "timestamp": "2023-04-24 20:33:36.719", } + - Name: base64 quoted argument + ExpectedResult: true + Log: + { + "ConfigBuild": "1007.3.0016606.11", + "ConfigStateHash": "3645117824", + "Entitlements": "15", + "TreeId": "4295752857", + "aid": "877761efa8db44d792ddc2redacted", + "aip": "1.1.1.1", + "cid": "cfe698690964434083fecdredacted", + "event": + { + "AuthenticationId": "293628", + "AuthenticodeHashData": "98a4762f52a", + "CommandLine": '/usr/bin/somebinary --b64="aGVsbG8taXMtaXQtbWUteW91cmUtbG9va2luZy1mb3IK"', + "ConfigBuild": "1007.3.0016606.11", + "ConfigStateHash": "3645117824", + "EffectiveTransmissionClass": "2", + "Entitlements": "15", + "ImageFileName": "\\Device\\HarddiskVolume2\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "ImageSubsystem": "3", + "IntegrityLevel": "12288", + "MD5HashData": "c031e215b8b08c752bf362f6d4c5d3ad", + "ParentAuthenticationId": "293628", + "ParentBaseFileName": "pwsh.exe", + "ParentProcessId": "4370948876", + "ProcessCreateFlags": "1024", + "ProcessEndTime": "", + "ProcessParameterFlags": "24577", + "ProcessStartTime": "1682368414.719", + "ProcessSxsFlags": "64", + "RawProcessId": "3120", + "SHA1HashData": "0000000000000000000000000000000000000000", + "SHA256HashData": "840e1f9dc5a29bebf01626822d7390251e9cf05bb3560ba7b68bdb8a41cf08e3", + "SessionId": "2", + "SignInfoFlags": "8683538", + "SourceProcessId": "4370948876", + "SourceThreadId": "112532918543", + "Tags": "25, 27, 40, 151, 874, 924, 12094627905582, 12094627906234, 211106232533012, 263882790666253", + "TargetProcessId": "10413665481", + "TokenType": "1", + "TreeId": "4295752857", + "UserSid": "S-1-5-21-239183934-720705223-383019856-500", + "aid": "877761efa8db44d792ddc2redacted", + "aip": "1.1.1.1", + "cid": "cfe698690964434083fecdredacted", + "event_platform": "Win", + "event_simpleName": "ProcessRollup2", + "id": "b0c07877-f288-49f8-8cb3-150149a557b2", + "name": "ProcessRollup2V19", + "timestamp": "1682368416719", + }, + "event_platform": "Win", + "event_simpleName": "ProcessRollup2", + "fdr_event_type": "ProcessRollup2", + "id": "b0c07877-f288-49f8-8cb3-150149a557b2", + "name": "ProcessRollup2V19", + "p_log_type": "Crowdstrike.FDREvent", + "timestamp": "2023-04-24 20:33:36.719", + } + - Name: base64 prefixed argument + ExpectedResult: true + Log: + { + "ConfigBuild": "1007.3.0016606.11", + "ConfigStateHash": "3645117824", + "Entitlements": "15", + "TreeId": "4295752857", + "aid": "877761efa8db44d792ddc2redacted", + "aip": "1.1.1.1", + "cid": "cfe698690964434083fecdredacted", + "event": + { + "AuthenticationId": "293628", + "AuthenticodeHashData": "98a4762f52a", + "CommandLine": '/usr/bin/somebinary --b64=aGVsbG8taXMtaXQtbWUteW91cmUtbG9va2luZy1mb3==', + "ConfigBuild": "1007.3.0016606.11", + "ConfigStateHash": "3645117824", + "EffectiveTransmissionClass": "2", + "Entitlements": "15", + "ImageFileName": "\\Device\\HarddiskVolume2\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "ImageSubsystem": "3", + "IntegrityLevel": "12288", + "MD5HashData": "c031e215b8b08c752bf362f6d4c5d3ad", + "ParentAuthenticationId": "293628", + "ParentBaseFileName": "pwsh.exe", + "ParentProcessId": "4370948876", + "ProcessCreateFlags": "1024", + "ProcessEndTime": "", + "ProcessParameterFlags": "24577", + "ProcessStartTime": "1682368414.719", + "ProcessSxsFlags": "64", + "RawProcessId": "3120", + "SHA1HashData": "0000000000000000000000000000000000000000", + "SHA256HashData": "840e1f9dc5a29bebf01626822d7390251e9cf05bb3560ba7b68bdb8a41cf08e3", + "SessionId": "2", + "SignInfoFlags": "8683538", + "SourceProcessId": "4370948876", + "SourceThreadId": "112532918543", + "Tags": "25, 27, 40, 151, 874, 924, 12094627905582, 12094627906234, 211106232533012, 263882790666253", + "TargetProcessId": "10413665481", + "TokenType": "1", + "TreeId": "4295752857", + "UserSid": "S-1-5-21-239183934-720705223-383019856-500", + "aid": "877761efa8db44d792ddc2redacted", + "aip": "1.1.1.1", + "cid": "cfe698690964434083fecdredacted", + "event_platform": "Win", + "event_simpleName": "ProcessRollup2", + "id": "b0c07877-f288-49f8-8cb3-150149a557b2", + "name": "ProcessRollup2V19", + "timestamp": "1682368416719", + }, + "event_platform": "Win", + "event_simpleName": "ProcessRollup2", + "fdr_event_type": "ProcessRollup2", + "id": "b0c07877-f288-49f8-8cb3-150149a557b2", + "name": "ProcessRollup2V19", + "p_log_type": "Crowdstrike.FDREvent", + "timestamp": "2023-04-24 20:33:36.719", + } diff --git a/rules/gcp_audit_rules/gcp_cloud_run_service_created.py b/rules/gcp_audit_rules/gcp_cloud_run_service_created.py index 6522b0fdd..488f29588 100644 --- a/rules/gcp_audit_rules/gcp_cloud_run_service_created.py +++ b/rules/gcp_audit_rules/gcp_cloud_run_service_created.py @@ -6,7 +6,8 @@ def rule(event): if deep_get(event, "severity") == "ERROR": return False - if not deep_get(event, "protoPayload", "methodName").endswith("Services.CreateService"): + method_name = deep_get(event, "protoPayload", "methodName", default="") + if not method_name.endswith("Services.CreateService"): return False authorization_info = deep_walk(event, "protoPayload", "authorizationInfo") diff --git a/rules/gcp_audit_rules/gcp_cloud_run_service_created.yml b/rules/gcp_audit_rules/gcp_cloud_run_service_created.yml index 154fa3179..b406dfea7 100644 --- a/rules/gcp_audit_rules/gcp_cloud_run_service_created.yml +++ b/rules/gcp_audit_rules/gcp_cloud_run_service_created.yml @@ -15,6 +15,47 @@ Severity: Low DedupPeriodMinutes: 60 Threshold: 1 Tests: + - Name: GCP No methodName found + ExpectedResult: false + Log: + { + "p_event_time": "2024-07-22 14:20:56.237323088", + "p_log_type": "GCP.AuditLog", + "insertId": "123456789xyz", + "logName": "projects/internal-sentry/logs/cloudaudit.googleapis.com%2Factivity", + "protoPayload": + { + "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog", + "authenticationInfo": + { + "principalEmail": "0000000000000@cloudbuild.gserviceaccount.com", + }, + "authorizationInfo": + [ + { + "granted": true, + "permission": "cloudbuild.builds.create", + "resource": "projects/00000000aaaaaaaa", + "resourceAttributes": {}, + }, + ], + "requestMetadata": + { + "destinationAttributes": {}, + "requestAttributes": + { "auth": {}, "time": "2024-07-22T14:20:55.898367039Z" }, + }, + }, + "receiveTimestamp": "2024-07-22 14:20:56.428021476", + "resource": + { + "labels": + { "method": "", "project_id": "some-project", "service": "" }, + "type": "audited_resource", + }, + "severity": "NOTICE", + "timestamp": "2024-07-22 14:20:56.237323088", + } - Name: GCP Run Service Created ExpectedResult: true Log: diff --git a/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.py b/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.py index 6acdc64f5..09e33a44a 100644 --- a/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.py +++ b/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.py @@ -6,7 +6,8 @@ def rule(event): if deep_get(event, "severity") == "ERROR": return False - if not deep_get(event, "protoPayload", "methodName").endswith("Services.SetIamPolicy"): + method_name = deep_get(event, "protoPayload", "methodName", default="") + if not method_name.endswith("Services.SetIamPolicy"): return False authorization_info = deep_walk(event, "protoPayload", "authorizationInfo") diff --git a/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.yml b/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.yml index f8d273e1f..31f3965e5 100644 --- a/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.yml +++ b/rules/gcp_audit_rules/gcp_cloud_run_set_iam_policy.yml @@ -152,3 +152,56 @@ Tests: "severity": "NOTICE", "timestamp": "2024-02-02 09:44:26.029835000", } + - Name: No method provided + ExpectedResult: false + Log: + { + "insertId": "l3jvzyd2s2s", + "logName": "projects/some-project/logs/cloudaudit.googleapis.com%2Factivity", + "protoPayload": + { + "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog", + "authenticationInfo": + { + "principalEmail": "some.user@company.com", + "principalSubject": "user:some.user@company.com", + }, + "authorizationInfo": + [ + { + "granted": false, + "permission": "run.services.setIamPolicy", + "resource": "projects/some-project/locations/us-west1/services/cloudrun-exfil", + "resourceAttributes": {}, + }, + { + "granted": false, + "permission": "run.services.setIamPolicy", + "resourceAttributes": {}, + }, + ], + "request": + { + "@type": "type.googleapis.com/google.iam.v1.SetIamPolicyRequest", + "policy": + { + "bindings": + [ + { + "members": ["user:some.user@company.com"], + "role": "roles/run.invoker", + }, + ], + }, + "resource": "projects/some-project/locations/us-west1/services/cloudrun-exfil", + }, + "requestMetadata": ..., + "resourceLocation": ..., + "resourceName": "projects/some-project/locations/us-west1/services/cloudrun-exfil", + "serviceName": "run.googleapis.com", + }, + "receiveTimestamp": "2024-02-02 09:44:26.653891982", + "resource": ..., + "severity": "NOTICE", + "timestamp": "2024-02-02 09:44:26.029835000", + } diff --git a/rules/slack_rules/slack_user_privilege_escalation.py b/rules/slack_rules/slack_user_privilege_escalation.py index 1715b13e7..a810df427 100644 --- a/rules/slack_rules/slack_user_privilege_escalation.py +++ b/rules/slack_rules/slack_user_privilege_escalation.py @@ -13,22 +13,26 @@ def rule(event): def title(event): - username = deep_get(event, "actor", "user", "name", default="") - email = deep_get(event, "actor", "user", "email", default="") - - if event.get("action") == "owner_transferred": - return f"Slack Owner Transferred from {username} ({email})" - - if event.get("action") == "permissions_assigned": - return f"Slack User, {username} ({email}), assigned permissions" - - if event.get("action") == "role_change_to_admin": - return f"Slack User, {username} ({email}), promoted to admin" - - if event.get("action") == "role_change_to_owner": - return f"Slack User, {username} ({email}), promoted to Owner" - - return f"Slack User Privilege Escalation event {event.get('action')} on {username} ({email})" + # This is the user taking the action. + actor_username = deep_get(event, "actor", "user", "name", default="") + actor_email = deep_get(event, "actor", "user", "email", default="") + # This is the user the action is taken on. + entity_username = deep_get(event, "entity", "user", "name", default="") + entity_email = deep_get(event, "entity", "user", "email", default="") + action = event.get("action") + if action == "owner_transferred": + return f"{USER_PRIV_ESC_ACTIONS[action]} from {actor_username} ({actor_email})" + + if action == "permissions_assigned": + return f"{USER_PRIV_ESC_ACTIONS[action]} {entity_username} ({entity_email})" + + if action == "role_change_to_admin": + return f"{USER_PRIV_ESC_ACTIONS[action]} {entity_username} ({entity_email})" + + if action == "role_change_to_owner": + return f"{USER_PRIV_ESC_ACTIONS[action]} {entity_username} ({entity_email})" + + return f"Slack User Privilege Escalation event {action} on {entity_username} ({entity_email})" def severity(event): diff --git a/rules/standard_rules/standard_dns_base64.yml b/rules/standard_rules/standard_dns_base64.yml index a9399f864..4d5b479d1 100644 --- a/rules/standard_rules/standard_dns_base64.yml +++ b/rules/standard_rules/standard_dns_base64.yml @@ -210,6 +210,6 @@ Tests: "externalIp": "136.24.229.58", "timestamp": "2020-05-21 19:20:25.000", "responseCode": "NOERROR", - "domain": "c29tZSBsb25nIGJhc2.example.io.", + "domain": "c29tZSBsb25IGJhc2.example.io.", "p_log_type": "CiscoUmbrella.DNS", }