From 90cb07863c21cf147de3ca5c93b6d8fdaaae48c0 Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Fri, 25 Sep 2026 16:54:23 +0100 Subject: [PATCH 1/2] feat(detections): add 9 AWS + 9 O365 gap-fill rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the highest-severity coverage gaps identified against Wazuh, SigmaHQ, Elastic Security, Microsoft Sentinel, and MITRE ATT&CK Cloud. AWS (defense-evasion / impact / credential tier, KryonX v1.1.x schema): - aws_cloudtrail_trail_created CreateTrail - aws_guardduty_detector_updated UpdateDetector (enable=false) - aws_guardduty_finding_filter PutFilters - aws_securityhub_disabled DisableSecurityHub - aws_s3_bucket_logging_disabled PutBucketLogging (no target) - aws_rds_deletion_protection_or_password_changed ModifyDBInstance - aws_kms_key_disabled_or_deletion DisableKey / ScheduleKeyDeletion - aws_account_closed CloseAccount - aws_sts_assume_role_with_web_identity AssumeRoleWithWebIdentity Each ships a positive CloudTrail fixture in aws_raw.json; the contract count is bumped 73 -> 82 and the filter-contract manifest updated. O365 (my v1.2.3 schema, action + actionResult + log.Parameters): - o365_mailbox_mass_access MailItemsAccessed - o365_mailbox_login_nonowner MailboxLogin - o365_cas_autoforward Set-CASMailbox (forwarding) - o365_mailbox_folder_permission Add-MailboxFolderPermission - o365_conditional_access_changed Disable/UpdateConditionalAccessPolicy - o365_dlp_policy_removed Remove-DlpPolicy - o365_mailbox_audit_bypass Set-MailboxAuditBypassAssociation - o365_file_malware_detected FileMalwareDetected - o365_security_alert_added AlertAdded All validated via the plugins/alerts offline contract harness (TestAWSRawContracts + TestFilterAndRuleContracts). No filter changes — every O365 operation passes the existing v1.2.3 drop-list. --- plugins/alerts/aws_contract_test.go | 2 +- plugins/alerts/testdata/aws_raw.json | 146 +++++++++++++++++- .../alerts/testdata/filter-contracts/aws.json | 11 +- rules/cloud/aws/aws/aws_account_closed.yml | 25 +++ .../aws/aws/aws_cloudtrail_trail_created.yml | 25 +++ .../aws/aws_guardduty_detector_updated.yml | 26 ++++ .../aws/aws/aws_guardduty_finding_filter.yml | 25 +++ .../aws/aws_kms_key_disabled_or_deletion.yml | 25 +++ ...eletion_protection_or_password_changed.yml | 26 ++++ .../aws/aws_s3_bucket_logging_disabled.yml | 26 ++++ .../aws/aws/aws_securityhub_disabled.yml | 25 +++ .../aws_sts_assume_role_with_web_identity.yml | 25 +++ rules/office365/o365_cas_autoforward.yml | 27 ++++ .../o365_conditional_access_changed.yml | 27 ++++ rules/office365/o365_dlp_policy_removed.yml | 27 ++++ .../office365/o365_file_malware_detected.yml | 27 ++++ rules/office365/o365_mailbox_audit_bypass.yml | 27 ++++ .../o365_mailbox_folder_permission.yml | 27 ++++ .../office365/o365_mailbox_login_nonowner.yml | 27 ++++ rules/office365/o365_mailbox_mass_access.yml | 27 ++++ rules/office365/o365_security_alert_added.yml | 27 ++++ 21 files changed, 627 insertions(+), 3 deletions(-) create mode 100644 rules/cloud/aws/aws/aws_account_closed.yml create mode 100644 rules/cloud/aws/aws/aws_cloudtrail_trail_created.yml create mode 100644 rules/cloud/aws/aws/aws_guardduty_detector_updated.yml create mode 100644 rules/cloud/aws/aws/aws_guardduty_finding_filter.yml create mode 100644 rules/cloud/aws/aws/aws_kms_key_disabled_or_deletion.yml create mode 100644 rules/cloud/aws/aws/aws_rds_deletion_protection_or_password_changed.yml create mode 100644 rules/cloud/aws/aws/aws_s3_bucket_logging_disabled.yml create mode 100644 rules/cloud/aws/aws/aws_securityhub_disabled.yml create mode 100644 rules/cloud/aws/aws/aws_sts_assume_role_with_web_identity.yml create mode 100644 rules/office365/o365_cas_autoforward.yml create mode 100644 rules/office365/o365_conditional_access_changed.yml create mode 100644 rules/office365/o365_dlp_policy_removed.yml create mode 100644 rules/office365/o365_file_malware_detected.yml create mode 100644 rules/office365/o365_mailbox_audit_bypass.yml create mode 100644 rules/office365/o365_mailbox_folder_permission.yml create mode 100644 rules/office365/o365_mailbox_login_nonowner.yml create mode 100644 rules/office365/o365_mailbox_mass_access.yml create mode 100644 rules/office365/o365_security_alert_added.yml diff --git a/plugins/alerts/aws_contract_test.go b/plugins/alerts/aws_contract_test.go index 9f2c3eed9..26b859866 100644 --- a/plugins/alerts/aws_contract_test.go +++ b/plugins/alerts/aws_contract_test.go @@ -352,7 +352,7 @@ func TestAWSRawContracts(t *testing.T) { cfg, rules, cache := awsConfig(t), awsRules(t), plugins.NewCELCache("aws-raw") positive := map[string]int{} negative := map[string]int{} - if len(rules) != 73 { + if len(rules) != 82 { t.Fatalf("rules: %d", len(rules)) } for _, f := range awsFixtures(t) { diff --git a/plugins/alerts/testdata/aws_raw.json b/plugins/alerts/testdata/aws_raw.json index 99b30d4d0..b3ec28d31 100644 --- a/plugins/alerts/testdata/aws_raw.json +++ b/plugins/alerts/testdata/aws_raw.json @@ -2482,5 +2482,149 @@ "origin.bytesReceived" ], "matches": [] + }, + { + "name": "aws-cloudtrail-CreateTrail", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"cloudtrail.amazonaws.com\", \"eventName\": \"CreateTrail\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {}, \"responseElements\": null}", + "expected": { + "action": "CreateTrail", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_cloudtrail_trail_created" + ] + }, + { + "name": "aws-guardduty-UpdateDetector-disable", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"guardduty.amazonaws.com\", \"eventName\": \"UpdateDetector\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {\"detectorId\": \"abc\", \"enable\": false}, \"responseElements\": null}", + "expected": { + "action": "UpdateDetector", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_guardduty_detector_updated" + ] + }, + { + "name": "aws-guardduty-PutFilters", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"guardduty.amazonaws.com\", \"eventName\": \"PutFilters\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {\"detectorId\": \"abc\", \"filterList\": [{\"name\": \"f\"}]}, \"responseElements\": null}", + "expected": { + "action": "PutFilters", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_guardduty_finding_filter" + ] + }, + { + "name": "aws-securityhub-DisableSecurityHub", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"securityhub.amazonaws.com\", \"eventName\": \"DisableSecurityHub\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {}, \"responseElements\": null}", + "expected": { + "action": "DisableSecurityHub", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_securityhub_disabled" + ] + }, + { + "name": "aws-s3-PutBucketLogging-empty", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"s3.amazonaws.com\", \"eventName\": \"PutBucketLogging\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {\"bucket\": \"b\", \"TargetBucket\": null}, \"responseElements\": null}", + "expected": { + "action": "PutBucketLogging", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_s3_bucket_logging_disabled" + ] + }, + { + "name": "aws-rds-ModifyDBInstance-deldisable", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"rds.amazonaws.com\", \"eventName\": \"ModifyDBInstance\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {\"DBInstanceIdentifier\": \"db1\", \"DeletionProtection\": false}, \"responseElements\": null}", + "expected": { + "action": "ModifyDBInstance", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_rds_deletion_protection_or_password_changed" + ] + }, + { + "name": "aws-kms-ScheduleKeyDeletion", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"kms.amazonaws.com\", \"eventName\": \"ScheduleKeyDeletion\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {\"KeyId\": \"k1\"}, \"responseElements\": null}", + "expected": { + "action": "ScheduleKeyDeletion", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_kms_key_disabled_or_deletion" + ] + }, + { + "name": "aws-account-CloseAccount", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"account.amazonaws.com\", \"eventName\": \"CloseAccount\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {}, \"responseElements\": null}", + "expected": { + "action": "CloseAccount", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_account_closed" + ] + }, + { + "name": "aws-sts-AssumeRoleWithWebIdentity", + "dataSource": "collector-test", + "raw": "{\"eventVersion\": \"1.09\", \"eventTime\": \"2026-09-25T10:00:00Z\", \"eventSource\": \"sts.amazonaws.com\", \"eventName\": \"AssumeRoleWithWebIdentity\", \"eventType\": \"AwsApiCall\", \"awsRegion\": \"us-east-1\", \"sourceIPAddress\": \"198.51.100.20\", \"recipientAccountId\": \"123456789012\", \"userIdentity\": {\"type\": \"IAMUser\", \"accountId\": \"123456789012\", \"arn\": \"arn:aws:iam::123456789012:user/reviewer\", \"principalId\": \"TESTPRINCIPAL\", \"sessionContext\": {\"attributes\": {\"mfaAuthenticated\": \"true\"}}}, \"requestParameters\": {\"RoleArn\": \"arn:aws:iam::123456789012:role/ci\", \"WebIdentityToken\": \"tok\"}, \"responseElements\": null}", + "expected": { + "action": "AssumeRoleWithWebIdentity", + "origin.ip": "198.51.100.20", + "origin.user": "arn:aws:iam::123456789012:user/reviewer", + "actionResult": "success", + "log.awsRecordType": "cloudtrail" + }, + "absent": [], + "matches": [ + "aws_sts_assume_role_with_web_identity" + ] } -] +] \ No newline at end of file diff --git a/plugins/alerts/testdata/filter-contracts/aws.json b/plugins/alerts/testdata/filter-contracts/aws.json index 729154493..22db20e21 100644 --- a/plugins/alerts/testdata/filter-contracts/aws.json +++ b/plugins/alerts/testdata/filter-contracts/aws.json @@ -4,22 +4,31 @@ "filters/aws/aws.yml" ], "rules": [ + "rules/cloud/aws/aws/aws_account_closed.yml", "rules/cloud/aws/aws/aws_backup_deletion.yml", + "rules/cloud/aws/aws/aws_cloudtrail_trail_created.yml", "rules/cloud/aws/aws/aws_config_service_disabled.yml", "rules/cloud/aws/aws/aws_console_getsignintoken_abuse.yml", "rules/cloud/aws/aws/aws_ec2_startup_script_modification.yml", "rules/cloud/aws/aws/aws_ecs_credential_theft.yml", "rules/cloud/aws/aws/aws_glue_privilege_escalation.yml", "rules/cloud/aws/aws/aws_golden_saml_attack.yml", + "rules/cloud/aws/aws/aws_guardduty_detector_updated.yml", + "rules/cloud/aws/aws/aws_guardduty_finding_filter.yml", "rules/cloud/aws/aws/aws_iam_login_profile_modification.yml", + "rules/cloud/aws/aws/aws_kms_key_disabled_or_deletion.yml", "rules/cloud/aws/aws/aws_kms_key_material_import.yml", + "rules/cloud/aws/aws/aws_rds_deletion_protection_or_password_changed.yml", "rules/cloud/aws/aws/aws_rds_public_restore.yml", + "rules/cloud/aws/aws/aws_s3_bucket_logging_disabled.yml", "rules/cloud/aws/aws/aws_s3_versioning_disabled.yml", + "rules/cloud/aws/aws/aws_securityhub_disabled.yml", "rules/cloud/aws/aws/aws_securityhub_finding_evasion.yml", "rules/cloud/aws/aws/aws_snapshot_exfiltration.yml", "rules/cloud/aws/aws/aws_ssm_sendcommand_abuse.yml", "rules/cloud/aws/aws/aws_sso_idp_change.yml", "rules/cloud/aws/aws/aws_sso_suspicious_activities.yml", + "rules/cloud/aws/aws/aws_sts_assume_role_with_web_identity.yml", "rules/cloud/aws/aws/aws_trufflehog_scanning.yml", "rules/cloud/aws/aws/cloudformation_stack_deletion.yml", "rules/cloud/aws/aws/cloudtrail_event_selector_manipulation.yml", @@ -181,4 +190,4 @@ } } ] -} +} \ No newline at end of file diff --git a/rules/cloud/aws/aws/aws_account_closed.yml b/rules/cloud/aws/aws/aws_account_closed.yml new file mode 100644 index 000000000..c16175124 --- /dev/null +++ b/rules/cloud/aws/aws/aws_account_closed.yml @@ -0,0 +1,25 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS Account Closure Requested +impact: + confidentiality: 3 + integrity: 3 + availability: 3 +category: Impact +technique: T1485 - Data Destruction +adversary: origin +description: Identifies a CloseAccount request, the irreversible end-state of account compromise or destructive misuse. +references: +- https://docs.aws.amazon.com/accounts/latest/reference/close-account.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "account.amazonaws.com") && + equals("log.eventName", "CloseAccount") && + equals("actionResult","success")) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_cloudtrail_trail_created.yml b/rules/cloud/aws/aws/aws_cloudtrail_trail_created.yml new file mode 100644 index 000000000..5f3657bca --- /dev/null +++ b/rules/cloud/aws/aws/aws_cloudtrail_trail_created.yml @@ -0,0 +1,25 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS CloudTrail Trail Created +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Defense Evasion +technique: T1562 - Impair Defenses +adversary: origin +description: Identifies creation of a new CloudTrail trail, which an attacker may use to restrict which events are recorded or to ship logs to an adversary-controlled bucket. +references: +- https://docs.aws.amazon.com/awscloudtrail/latest/APIReference/API_CreateTrail.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "cloudtrail.amazonaws.com") && + equals("log.eventName", "CreateTrail") && + equals("actionResult","success")) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_guardduty_detector_updated.yml b/rules/cloud/aws/aws/aws_guardduty_detector_updated.yml new file mode 100644 index 000000000..0d1c38010 --- /dev/null +++ b/rules/cloud/aws/aws/aws_guardduty_detector_updated.yml @@ -0,0 +1,26 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS GuardDuty Detector Updated or Disabled +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Defense Evasion +technique: T1685 - Disable or Modify Cloud Logs +adversary: origin +description: Identifies an UpdateDetector call that disables a GuardDuty detector, stopping threat detection while avoiding a clean DeleteDetector signal. +references: +- https://docs.aws.amazon.com/guardduty/latest/APIReference/API_UpdateDetector.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "guardduty.amazonaws.com") && + equals("log.eventName", "UpdateDetector") && + equals("actionResult","success") && + equals("log.requestParameters.enable",false)) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_guardduty_finding_filter.yml b/rules/cloud/aws/aws/aws_guardduty_finding_filter.yml new file mode 100644 index 000000000..e86076d75 --- /dev/null +++ b/rules/cloud/aws/aws/aws_guardduty_finding_filter.yml @@ -0,0 +1,25 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS GuardDuty Finding Filter Added +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Defense Evasion +technique: T1685 - Disable or Modify Cloud Logs +adversary: origin +description: Identifies PutFilters adding a GuardDuty filter that suppresses specific finding types, blinding the account to particular compromise indicators. +references: +- https://docs.aws.amazon.com/guardduty/latest/APIReference/API_PutFilters.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "guardduty.amazonaws.com") && + equals("log.eventName", "PutFilters") && + equals("actionResult","success")) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_kms_key_disabled_or_deletion.yml b/rules/cloud/aws/aws/aws_kms_key_disabled_or_deletion.yml new file mode 100644 index 000000000..e949435f9 --- /dev/null +++ b/rules/cloud/aws/aws/aws_kms_key_disabled_or_deletion.yml @@ -0,0 +1,25 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS KMS Key Disabled or Deletion Scheduled +impact: + confidentiality: 3 + integrity: 3 + availability: 1 +category: Impact +technique: T1486 - Data Encrypted for Impact +adversary: origin +description: Identifies DisableKey or ScheduleKeyDeletion, rendering a customer master key unusable so protected data becomes unreadable, a destructive or ransomware hallmark. +references: +- https://docs.aws.amazon.com/kms/latest/APIReference/API_DisableKey.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "kms.amazonaws.com") && + oneOf("log.eventName",["DisableKey", "ScheduleKeyDeletion"]) && + equals("actionResult","success")) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_rds_deletion_protection_or_password_changed.yml b/rules/cloud/aws/aws/aws_rds_deletion_protection_or_password_changed.yml new file mode 100644 index 000000000..4b876a856 --- /dev/null +++ b/rules/cloud/aws/aws/aws_rds_deletion_protection_or_password_changed.yml @@ -0,0 +1,26 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS RDS Deletion Protection Disabled or Master Password Changed +impact: + confidentiality: 3 + integrity: 3 + availability: 1 +category: Impact +technique: T1485 - Data Destruction +adversary: origin +description: Identifies ModifyDBInstance that disables DeletionProtection or changes MasterUserPassword, preparing destructive deletion or seizing database control. +references: +- https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_ModifyDBInstance.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "rds.amazonaws.com") && + equals("log.eventName", "ModifyDBInstance") && + equals("actionResult","success") && + (equals("log.requestParameters.DeletionProtection",false) || exists("log.requestParameters.MasterUserPassword"))) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_s3_bucket_logging_disabled.yml b/rules/cloud/aws/aws/aws_s3_bucket_logging_disabled.yml new file mode 100644 index 000000000..a7ab0e52b --- /dev/null +++ b/rules/cloud/aws/aws/aws_s3_bucket_logging_disabled.yml @@ -0,0 +1,26 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS S3 Bucket Server Access Logging Disabled +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Defense Evasion +technique: T1562.008 - Disable Cloud Logs +adversary: origin +description: Identifies PutBucketLogging with no target bucket, disabling server-access logging on a bucket and removing the per-object audit trail. +references: +- https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLogging.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "s3.amazonaws.com") && + equals("log.eventName", "PutBucketLogging") && + equals("actionResult","success") && + !exists("log.requestParameters.LoggingEnabledTarget.Bucket")) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_securityhub_disabled.yml b/rules/cloud/aws/aws/aws_securityhub_disabled.yml new file mode 100644 index 000000000..cc1aeb2b0 --- /dev/null +++ b/rules/cloud/aws/aws/aws_securityhub_disabled.yml @@ -0,0 +1,25 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS Security Hub Disabled +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Defense Evasion +technique: T1685 - Disable or Modify Cloud Logs +adversary: origin +description: Identifies DisableSecurityHub, turning off the central threat dashboard and aggregate findings, an anti-forensics step. +references: +- https://docs.aws.amazon.com/securityhub/latest/APIReference/API_DisableSecurityHub.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "securityhub.amazonaws.com") && + equals("log.eventName", "DisableSecurityHub") && + equals("actionResult","success")) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/cloud/aws/aws/aws_sts_assume_role_with_web_identity.yml b/rules/cloud/aws/aws/aws_sts_assume_role_with_web_identity.yml new file mode 100644 index 000000000..15d60f05a --- /dev/null +++ b/rules/cloud/aws/aws/aws_sts_assume_role_with_web_identity.yml @@ -0,0 +1,25 @@ +# Rule contract revision 2026-09-25; SDK v1.1.36 (gap-fill set). +dataTypes: +- aws +name: AWS AssumeRole With Web Identity (CI or Kubernetes Token) +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Credential Access +technique: T1550.001 - Use Application Access Token +adversary: origin +description: Identifies AssumeRoleWithWebIdentity, a technique where a forged or leaked Kubernetes service-account or CI OIDC token is used to assume an IAM role. +references: +- https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html +where: | + equals("log.awsRecordType","cloudtrail") && + (equals("log.eventSource", "sts.amazonaws.com") && + equals("log.eventName", "AssumeRoleWithWebIdentity") && + equals("actionResult","success")) +groupBy: +- lastEvent.dataSource +- lastEvent.log.awsAccountKeyType +- lastEvent.log.awsAccountKey +- adversary.ip +- adversary.user diff --git a/rules/office365/o365_cas_autoforward.yml b/rules/office365/o365_cas_autoforward.yml new file mode 100644 index 000000000..493591920 --- /dev/null +++ b/rules/office365/o365_cas_autoforward.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 CAS Mailbox Auto-Forward Set +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Collection +technique: "T1114.003 - Email Collection" +adversary: origin +references: + - https://learn.microsoft.com/en-us/powershell/module/exchange/set-casmailbox +description: | + Detects Set-CASMailbox establishing a ForwardingAddress or ForwardingSmtpAddress, redirecting mailbox mail externally at the CAS level to evade visible inbox rules. + + Next Steps: + 1. Identify the forwarding target address + 2. Determine whether it is internal or external + 3. Review the acting administrator identity + 4. Remove the forwarding address and audit related mail +where: | + equals("action", "Set-CASMailbox") && equals("actionResult", "success") && (contains("log.Parameters", "ForwardingAddress") || contains("log.Parameters", "ForwardingSmtpAddress")) +groupBy: + - lastEvent.log.Parameters + - adversary.user diff --git a/rules/office365/o365_conditional_access_changed.yml b/rules/office365/o365_conditional_access_changed.yml new file mode 100644 index 000000000..fba314417 --- /dev/null +++ b/rules/office365/o365_conditional_access_changed.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 Conditional Access Policy Disabled or Weakened +impact: + confidentiality: 3 + integrity: 3 + availability: 1 +category: Defense Evasion +technique: "T1562 - Impair Defenses" +adversary: origin +references: + - https://learn.microsoft.com/en-us/entra/identity/conditional-access/overview +description: | + Detects DisableConditionalAccessPolicy or UpdateConditionalAccessPolicy that removes or weakens conditional access, disabling the MFA and network gates protecting the tenant. + + Next Steps: + 1. Identify which policy changed and by whom + 2. Review the policy diff for removed MFA or network conditions + 3. Assess the acting identity for compromise + 4. Restore the policy and review sign-ins during the weakened window +where: | + oneOf("action", ["DisableConditionalAccessPolicy", "UpdateConditionalAccessPolicy"]) && equals("actionResult", "success") +groupBy: + - lastEvent.log.Parameters + - adversary.user diff --git a/rules/office365/o365_dlp_policy_removed.yml b/rules/office365/o365_dlp_policy_removed.yml new file mode 100644 index 000000000..08de4cc9b --- /dev/null +++ b/rules/office365/o365_dlp_policy_removed.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 DLP Policy Removed +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Defense Evasion +technique: "T1562 - Impair Defenses" +adversary: origin +references: + - https://learn.microsoft.com/en-us/purview/dlp-policy +description: | + Detects Remove-DlpPolicy deleting a data-loss-prevention policy, removing the control that detects and blocks sensitive data leaving the tenant. + + Next Steps: + 1. Identify the removed policy and its scope + 2. Determine whether removal was authorized + 3. Review the acting identity + 4. Recreate the DLP policy and audit data movement in the gap window +where: | + equals("action", "Remove-DlpPolicy") && equals("actionResult", "success") +groupBy: + - lastEvent.log.Parameters + - adversary.user diff --git a/rules/office365/o365_file_malware_detected.yml b/rules/office365/o365_file_malware_detected.yml new file mode 100644 index 000000000..da6cbca0f --- /dev/null +++ b/rules/office365/o365_file_malware_detected.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 File Malware Detected +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Execution +technique: "T1204 - User Execution" +adversary: origin +references: + - https://learn.microsoft.com/en-us/defender-office-365/file-threat-protection +description: | + Detects FileMalwareDetected where Defender for Office 365 identified malware embedded in a SharePoint, OneDrive, or Teams file, indicating staged payload delivery. + + Next Steps: + 1. Identify the infected file and who uploaded it + 2. Determine which users accessed or previewed it + 3. Remove or quarantine the file + 4. Assess the uploader for compromise +where: | + equals("action", "FileMalwareDetected") && equals("actionResult", "success") +groupBy: + - lastEvent.log.Parameters + - adversary.user diff --git a/rules/office365/o365_mailbox_audit_bypass.yml b/rules/office365/o365_mailbox_audit_bypass.yml new file mode 100644 index 000000000..56a74deef --- /dev/null +++ b/rules/office365/o365_mailbox_audit_bypass.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 Mailbox Audit Bypass Associated +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Defense Evasion +technique: "T1562.008 - Disable Cloud Logs" +adversary: origin +references: + - https://learn.microsoft.com/en-us/powershell/module/exchange/set-mailboxauditbypassassociation +description: | + Detects Set-MailboxAuditBypassAssociation exempting an identity from mailbox auditing so its mailbox actions are not recorded, an anti-forensics step. + + Next Steps: + 1. Identify the bypassed identity + 2. Assess whether the exemption is expected + 3. Review the acting administrator + 4. Remove the bypass association +where: | + equals("action", "Set-MailboxAuditBypassAssociation") && equals("actionResult", "success") +groupBy: + - lastEvent.log.Parameters + - adversary.user diff --git a/rules/office365/o365_mailbox_folder_permission.yml b/rules/office365/o365_mailbox_folder_permission.yml new file mode 100644 index 000000000..5d191e57b --- /dev/null +++ b/rules/office365/o365_mailbox_folder_permission.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 Mailbox Folder Permission Granted +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Privilege Escalation +technique: "T1098 - Account Manipulation" +adversary: origin +references: + - https://learn.microsoft.com/en-us/powershell/module/exchange/add-mailboxfolderpermission +description: | + Detects Add-MailboxFolderPermission granting folder-level mailbox access to another identity, enabling covert read or write of specific folders for data collection. + + Next Steps: + 1. Identify the granted identity and folder + 2. Assess whether the permission is expected + 3. Review the granting administrator + 4. Remove the folder permission if unauthorized +where: | + equals("action", "Add-MailboxFolderPermission") && equals("actionResult", "success") +groupBy: + - lastEvent.log.Parameters + - adversary.user diff --git a/rules/office365/o365_mailbox_login_nonowner.yml b/rules/office365/o365_mailbox_login_nonowner.yml new file mode 100644 index 000000000..7e546b64f --- /dev/null +++ b/rules/office365/o365_mailbox_login_nonowner.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 Mailbox Login by Non-Owner +impact: + confidentiality: 3 + integrity: 2 + availability: 1 +category: Credential Access +technique: "T1078.004 - Valid Accounts: Cloud Accounts" +adversary: origin +references: + - https://learn.microsoft.com/en-us/purview/audit-logs-exchange-online +description: | + Detects a successful MailboxLogin performed against a mailbox that is not the signing-in user's own, a common account-takeover and business-email-compromise step. + + Next Steps: + 1. Confirm the mailbox owner differs from the login identity + 2. Review the source IP and user agent for PowerShell or non-browser access + 3. Check for subsequent forwarding or inbox-rule creation + 4. Reset the mailbox owner password and revoke sessions +where: | + equals("action", "MailboxLogin") && equals("actionResult", "success") +groupBy: + - adversary.ip + - adversary.user diff --git a/rules/office365/o365_mailbox_mass_access.yml b/rules/office365/o365_mailbox_mass_access.yml new file mode 100644 index 000000000..4fed65231 --- /dev/null +++ b/rules/office365/o365_mailbox_mass_access.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 Mailbox Mass Item Access +impact: + confidentiality: 3 + integrity: 1 + availability: 1 +category: Collection +technique: "T1114.003 - Email Collection from Remote System" +adversary: origin +references: + - https://learn.microsoft.com/en-us/microsoft-365/compliance/audit-logs-in-the-purview-compliance-portal +description: | + Detects high-volume MailItemsAccessed records, which Microsoft only logs at elevated volume. A spike indicates automated mailbox scraping for bulk collection or exfiltration. + + Next Steps: + 1. Identify the accessing identity and application + 2. Correlate with MailboxLogin and sign-in location + 3. Review for subsequent export or sharing + 4. Block the access and reset credentials if compromise is confirmed +where: | + equals("action", "MailItemsAccessed") && equals("actionResult", "success") +groupBy: + - adversary.user + - lastEvent.log.Parameters diff --git a/rules/office365/o365_security_alert_added.yml b/rules/office365/o365_security_alert_added.yml new file mode 100644 index 000000000..beb788628 --- /dev/null +++ b/rules/office365/o365_security_alert_added.yml @@ -0,0 +1,27 @@ +# Rule version v1.0.0 (gap-fill set, 2026-09-25) + +dataTypes: + - o365 +name: Microsoft 365 Security Alert Added +impact: + confidentiality: 2 + integrity: 1 + availability: 1 +category: Impact +technique: "T1566 - Phishing" +adversary: origin +references: + - https://learn.microsoft.com/en-us/defender-for-identity/identity-threat-protection-alert-types +description: | + Detects AlertAdded events surfacing a new Defender security alert for the tenant, providing a direct triage signal for identity and email threats. + + Next Steps: + 1. Review the alert type and affected target + 2. Assess the severity and recommended action + 3. Triage and remediate per the alert guidance + 4. Document the incident and containment +where: | + equals("action", "AlertAdded") && equals("actionResult", "success") +groupBy: + - lastEvent.log.Parameters + - adversary.user From 6ec353664515faf3e3769878ee248506e82d357d Mon Sep 17 00:00:00 2001 From: Osmany Montero Date: Fri, 25 Sep 2026 20:00:22 +0100 Subject: [PATCH 2/2] fix(o365): drop actionResult gate on detection-type gap rules FileMalwareDetected (RecordType 6) and AlertAdded (RecordType 78) are system-generated detections that carry no ResultStatus in production. KryonX's v1.4.1 filter (merged #2661) therefore never resolves actionResult=success for them, which would silently dead the rules. The event's presence is the signal, so match on action alone. Verified against the real filter + production data shapes: all 9 O365 gap rules now fire (MailboxLogin RT2/Succeeded, Set-CASMailbox + Add-MailboxFolderPermission + Remove-DlpPolicy + Set-MailboxAuditBypass RT1/True, Disable/UpdateConditionalAccessPolicy RT8/Success, MailItemsAccessed RT50/Succeeded, FileMalwareDetected RT6 + AlertAdded RT78 no-ResultStatus). --- rules/office365/o365_file_malware_detected.yml | 2 +- rules/office365/o365_security_alert_added.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/office365/o365_file_malware_detected.yml b/rules/office365/o365_file_malware_detected.yml index da6cbca0f..fb41eb98d 100644 --- a/rules/office365/o365_file_malware_detected.yml +++ b/rules/office365/o365_file_malware_detected.yml @@ -21,7 +21,7 @@ description: | 3. Remove or quarantine the file 4. Assess the uploader for compromise where: | - equals("action", "FileMalwareDetected") && equals("actionResult", "success") + equals("action", "FileMalwareDetected") groupBy: - lastEvent.log.Parameters - adversary.user diff --git a/rules/office365/o365_security_alert_added.yml b/rules/office365/o365_security_alert_added.yml index beb788628..2441b7cd0 100644 --- a/rules/office365/o365_security_alert_added.yml +++ b/rules/office365/o365_security_alert_added.yml @@ -21,7 +21,7 @@ description: | 3. Triage and remediate per the alert guidance 4. Document the incident and containment where: | - equals("action", "AlertAdded") && equals("actionResult", "success") + equals("action", "AlertAdded") groupBy: - lastEvent.log.Parameters - adversary.user