Detection rules › Panther
Panther rules: zia
| Rule | Severity |
|---|---|
| ZIA Account Access Removed | medium |
| ZIA Additional Cloud Roles | medium |
| ZIA Backup Deleted | medium |
| ZIA Cloud Account Created | medium |
| ZIA Golden Restore Point Dropped | medium |
| ZIA Insecure Password Settings | medium |
| ZIA Log Streaming Disabled | medium |
| ZIA Logs Downloaded | medium |
| ZIA Password Expiration | medium |
| ZIA Trust Modification | medium |
ZIA Account Access Removed
#This rule detects when admin user/role was deleted.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Impact |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
SENSITIVE_CATEGORIES = ["ADMINISTRATOR_MANAGEMENT", "ROLE_MANAGEMENT"]
def rule(event):
if not zia_success(event):
return False
event_data = event.get("event", {})
return (
event_data.get("action", "ACTION_NOT_FOUND") == "DELETE"
and event_data.get("category", "CATEGORY_NOT_FOUND") in SENSITIVE_CATEGORIES
)
def title(event):
return (
f"[Zscaler.ZIA]: Admin account was deleted by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Account.Access.Removed
Description: This rule detects when admin user/role was deleted.
DisplayName: ZIA Account Access Removed
Runbook: Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Reference: https://help.zscaler.com/zia/authentication-administration/administrator-role-management
Enabled: true
Filename: zia_account_access_removal.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0040:T1531 # Account Access removal
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisDELETEevent.categoryis one ofADMINISTRATOR_MANAGEMENT,ROLE_MANAGEMENT
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"DELETE" |
event.category | in |
| field:"event.category" kind:in |
event.errorcode | eq |
| field:"event.errorcode" kind:eq value:"None" |
event.result | eq |
| field:"event.result" kind:eq value:"SUCCESS" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "DELETE",
"adminid": "admin@16991311.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "ADMINISTRATOR_MANAGEMENT",
"clientip": "123.123.123.123",
"errorcode": "None",
"interface": "UI",
"postaction": {},
"preaction": {
"adminScope": {
"scopeEntities": [],
"scopeGroupMemberEntities": [],
"type": "ORGANIZATION"
},
"disabled": false,
"email": "johndoe@company.com",
"id": 19752821,
"isExecMobileAppEnabled": true,
"isPasswordLoginAllowed": true,
"loginName": "johndoe@dev-company.com",
"pwdLastModifiedTime": 1729634767,
"role": {
"deleted": false,
"extensions": {
"adminRank": "0",
"roleType": "EXEC_INSIGHT_AND_ORG_ADMIN"
},
"id": 24354,
"isNameL10nTag": true,
"name": "Super Admin"
},
"userName": "johndoe1123"
},
"recordid": "327",
"resource": "johndoe1123",
"result": "SUCCESS",
"subcategory": "ADMINISTRATOR_ADMIN_USER",
"time": "2024-10-22 22:09:01.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Additional Cloud Roles
#This rule detects when an additional cloud role was created.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
event_data = event.get("event", {})
return (
event_data.get("action", "ACTION_NOT_FOUND") == "CREATE"
and event_data.get("category", "CATEGORY_NOT_FOUND") == "ROLE_MANAGEMENT"
)
def title(event):
return (
f"[Zscaler.ZIA]: Additional cloud role was created by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Additional.Cloud.Roles
Description: This rule detects when an additional cloud role was created.
DisplayName: ZIA Additional Cloud Roles
Runbook: Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Reference: https://help.zscaler.com/zia/about-role-management
Enabled: true
Filename: zia_additional_cloud_roles.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0003:T1098.003 # Persistence: Additional Cloud Roles
- TA0004:T1098.003 # Priv Escalation: Additional Cloud Roles
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisCREATEevent.categoryisROLE_MANAGEMENT
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"CREATE" |
event.category | eq |
| field:"event.category" kind:eq value:"ROLE_MANAGEMENT" |
event.errorcode | eq |
| field:"event.errorcode" kind:eq value:"None" |
event.result | eq |
| field:"event.result" kind:eq value:"SUCCESS" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "CREATE",
"adminid": "admin@16991311.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "ROLE_MANAGEMENT",
"clientip": "123.123.123.123",
"errorcode": "None",
"interface": "UI",
"postaction": {
"adminAcctAccess": "READ_WRITE",
"alertingAccess": "READ_WRITE",
"analysisAccess": "READ_ONLY",
"dashboardAccess": "READ_WRITE",
"deviceInfoAccess": "READ_ONLY",
"id": 32780,
"logsLimit": "Unrestricted",
"name": "mega admin",
"permissions": [
"ADVANCED_SETTINGS",
"COMPLY",
"FIREWALL_DNS",
"NSS_CONFIGURATION",
"SECURE",
"SSL_POLICY",
"VZEN_CONFIGURATION",
"PARTNER_INTEGRATION",
"REMOTE_ASSISTANCE_MANAGEMENT",
"LOCATIONS",
"VPN_CREDENTIALS",
"HOSTED_PAC_FILES",
"EZ_AGENT_CONFIGURATIONS",
"SECURE_AGENT_NOTIFICATIONS",
"PROXY_GATEWAY",
"STATIC_IPS",
"GRE_TUNNELS",
"SUBCLOUDS",
"AUTHENTICATION_SETTINGS",
"USER_MANAGEMENT",
"IDENTITY_PROXY_SETTINGS",
"APIKEY_MANAGEMENT",
"POLICY_RESOURCE_MANAGEMENT",
"CLIENT_CONNECTOR_PORTAL",
"CUSTOM_URL_CAT",
"OVERRIDE_EXISTING_CAT",
"TENANT_PROFILE_MANAGEMENT"
],
"policyAccess": "READ_WRITE",
"rank": 7,
"reportAccess": "READ_WRITE",
"reportTimeDuration": -1,
"roleType": "EXEC_INSIGHT_AND_ORG_ADMIN",
"usernameAccess": "READ_ONLY"
},
"preaction": {
"adminAcctAccess": "READ_WRITE",
"alertingAccess": "READ_WRITE",
"analysisAccess": "READ_ONLY",
"dashboardAccess": "READ_WRITE",
"deviceInfoAccess": "READ_ONLY",
"id": 0,
"isAuditor": false,
"isNonEditable": false,
"logsLimit": "Unrestricted",
"name": "mega admin",
"permissions": [
"ADVANCED_SETTINGS",
"COMPLY",
"FIREWALL_DNS",
"NSS_CONFIGURATION",
"SECURE",
"SSL_POLICY",
"VZEN_CONFIGURATION",
"PARTNER_INTEGRATION",
"REMOTE_ASSISTANCE_MANAGEMENT",
"LOCATIONS",
"VPN_CREDENTIALS",
"HOSTED_PAC_FILES",
"EZ_AGENT_CONFIGURATIONS",
"SECURE_AGENT_NOTIFICATIONS",
"PROXY_GATEWAY",
"STATIC_IPS",
"GRE_TUNNELS",
"SUBCLOUDS",
"AUTHENTICATION_SETTINGS",
"USER_MANAGEMENT",
"IDENTITY_PROXY_SETTINGS",
"APIKEY_MANAGEMENT",
"POLICY_RESOURCE_MANAGEMENT",
"CLIENT_CONNECTOR_PORTAL",
"CUSTOM_URL_CAT",
"OVERRIDE_EXISTING_CAT",
"TENANT_PROFILE_MANAGEMENT"
],
"policyAccess": "READ_WRITE",
"rank": 7,
"reportAccess": "READ_WRITE",
"reportTimeDuration": -1,
"roleType": "EXEC_INSIGHT_AND_ORG_ADMIN",
"usernameAccess": "READ_ONLY"
},
"recordid": "341",
"resource": "mega admin",
"result": "SUCCESS",
"subcategory": "ADMINISTRATOR_ROLE",
"time": "2024-10-22 22:19:57.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Backup Deleted
#This rule detects when ZIA backup data was deleted.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
action = event.deep_get("event", "action", default="ACTION_NOT_FOUND")
category = event.deep_get("event", "category", default="CATEGORY_NOT_FOUND")
if action == "DELETE" and category == "BACKUP_AND_RESTORE":
return True
return False
def title(event):
return (
f"[Zscaler.ZIA]: Backup was deleted by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Backup.Deleted
Description: This rule detects when ZIA backup data was deleted.
DisplayName: ZIA Backup Deleted
Runbook: Verify that this change was planned. If not, make sure to restore the backup.
Reference: https://help.zscaler.com/zia/about-backup-and-restore
Enabled: true
Filename: zia_backup_deleted.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0005:T1562.008 # Disable or Modify Cloud Logs
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisDELETEevent.categoryisBACKUP_AND_RESTORE
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"DELETE" |
event.category | eq |
| field:"event.category" kind:eq value:"BACKUP_AND_RESTORE" |
event.errorcode | eq |
| field:"event.errorcode" kind:eq value:"None" |
event.result | eq |
| field:"event.result" kind:eq value:"SUCCESS" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, make sure to restore the backup.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "DELETE",
"adminid": "admin@test.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "BACKUP_AND_RESTORE",
"clientip": "1.2.3.4",
"errorcode": "None",
"interface": "UI",
"postaction": {},
"preaction": {
"adminLogin": "admin@test.zscalerbeta.net",
"goldenRestorePoint": false,
"id": 163372,
"name": "test-restore-2",
"time": 1730737925000
},
"recordid": "366",
"resource": "test-restore-2",
"result": "SUCCESS",
"subcategory": "BACKUP_AND_RESTORE",
"time": "2024-11-04 16:32:18.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Cloud Account Created
#This rule detects when new cloud account was created.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
action = event.deep_get("event", "action", default="ACTION_NOT_FOUND")
category = event.deep_get("event", "category", default="CATEGORY_NOT_FOUND")
role_name = event.deep_get(
"event", "postaction", "role", "name", default="<ROLE_NAME_NOT_FOUND>"
).lower()
if (
action == "CREATE"
and category == "ADMINISTRATOR_MANAGEMENT"
and ("admin" in role_name or "audit" in role_name)
):
return True
return False
def title(event):
return (
f"[Zscaler.ZIA]: New admin role was created by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Cloud.Account.Created
Description: This rule detects when new cloud account was created.
DisplayName: ZIA Cloud Account Created
Runbook: Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Reference: https://help.zscaler.com/zia/choosing-provisioning-and-authentication-methods
Enabled: true
Filename: zia_create_cloud_account.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0003:T1136.003 # Persistence: Create Cloud Account
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisCREATEevent.categoryisADMINISTRATOR_MANAGEMENTany of:
event.postaction.role.namecontainsadminevent.postaction.role.namecontainsaudit
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"CREATE" |
event.category | eq |
| field:"event.category" kind:eq value:"ADMINISTRATOR_MANAGEMENT" |
event.errorcode | eq |
| field:"event.errorcode" kind:eq value:"None" |
event.postaction.role.name | contains |
| field:"event.postaction.role.name" kind:contains |
event.result | eq |
| field:"event.result" kind:eq value:"SUCCESS" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "CREATE",
"adminid": "admin@16991311.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "ADMINISTRATOR_MANAGEMENT",
"clientip": "123.123.123.123",
"errorcode": "None",
"interface": "UI",
"postaction": {
"adminScope": {
"scopeEntities": [],
"scopeGroupMemberEntities": [],
"type": "ORGANIZATION"
},
"disabled": false,
"email": "ajohndoe@company.com",
"id": 19752821,
"isExecMobileAppEnabled": true,
"isPasswordLoginAllowed": true,
"loginName": "johndoe@dev-company.com",
"pwdLastModifiedTime": 1729634767,
"role": {
"deleted": false,
"extensions": {
"adminRank": "0",
"roleType": "EXEC_INSIGHT_AND_ORG_ADMIN"
},
"id": 24354,
"isNameL10nTag": true,
"name": "Super Admin"
},
"userName": "johndoe1123"
},
"preaction": {
"adminScope": {
"scopeEntities": [],
"scopeGroupMemberEntities": [],
"type": "ORGANIZATION"
},
"disabled": false,
"email": "johndoe@company.com",
"id": 0,
"isAuditor": false,
"isDefaultAdmin": false,
"isExecMobileAppEnabled": true,
"isPasswordExpired": false,
"isPasswordLoginAllowed": true,
"loginName": "johndoe@dev-company.com",
"newLocationCreateAllowed": false,
"password": "*****",
"pwdLastModifiedTime": 0,
"role": {
"deleted": false,
"id": 24354,
"isNameL10nTag": false,
"name": "Super Admin"
},
"userName": "johndoe1123"
},
"recordid": "326",
"resource": "johndoe1123",
"result": "SUCCESS",
"subcategory": "ADMINISTRATOR_ADMIN_USER",
"time": "2024-10-22 22:06:04.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Golden Restore Point Dropped
#This rule detects when ZIA goldenRestorePoint was dropped. It means that some piece of information that was impossible to delete before, now is deletable
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
action = event.deep_get("event", "action", default="ACTION_NOT_FOUND")
category = event.deep_get("event", "category", default="CATEGORY_NOT_FOUND")
golden_restore_point_pre = event.deep_get(
"event",
"preaction",
"goldenRestorePoint",
default="<PRE_RESTORE_POINT_NOT_FOUND>",
)
golden_restore_point_post = event.deep_get(
"event",
"postaction",
"goldenRestorePoint",
default="<POPT_RESTORE_POINT_NOT_FOUND>",
)
if (
action == "UPDATE"
and category == "BACKUP_AND_RESTORE"
and golden_restore_point_pre is True
and golden_restore_point_post is False
):
return True
return False
def title(event):
return (
f"[Zscaler.ZIA]: goldenRestorePoint was dropped by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Golden.Restore.Point.Dropped
Description: This rule detects when ZIA goldenRestorePoint was dropped.
It means that some piece of information that was impossible to delete before, now is deletable
DisplayName: ZIA Golden Restore Point Dropped
Runbook: Verify that this change was planned. If not, revert the change.
Reference: https://help.zscaler.com/zia/about-backup-and-restore
Enabled: true
Filename: zia_golden_restore_point_dropped.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0005:T1562.008 # Disable or Modify Cloud Logs
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisUPDATEevent.categoryisBACKUP_AND_RESTOREevent.preaction.goldenRestorePointistrueevent.postaction.goldenRestorePointisfalse
Indicators
These rows show field, operator, and value matches.
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, revert the change.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "UPDATE",
"adminid": "admin@test.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "BACKUP_AND_RESTORE",
"clientip": "1.2.3.4",
"errorcode": "None",
"interface": "UI",
"postaction": {
"adminLogin": "admin@test.zscalerbeta.net",
"goldenRestorePoint": false,
"id": 163371,
"name": "test-restore",
"time": 1730737915000
},
"preaction": {
"adminLogin": "admin@test.zscalerbeta.net",
"goldenRestorePoint": true,
"id": 163371,
"name": "test-restore",
"time": 1730737915000
},
"recordid": "367",
"resource": "test-restore",
"result": "SUCCESS",
"subcategory": "BACKUP_AND_RESTORE",
"time": "2024-11-04 16:32:28.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Insecure Password Settings
#This rule detects when password settings are insecure.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Defense Impairment | |
| Credential Access |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
auth_frequency = event.deep_get(
"event",
"postaction",
"authFrequency",
default="<AUTH_FREQUENCY_NOT_FOUND>",
)
password_expiry = event.deep_get(
"event",
"postaction",
"passwordExpiry",
default="<PASSWORD_EXPIRY_NOT_FOUND>",
)
password_strength = event.deep_get(
"event",
"postaction",
"passwordStrength",
default="<PASSWORD_STRENGTH_NOT_FOUND>",
)
if (
auth_frequency == "PERMANENT_COOKIE"
or password_expiry == "NEVER" # nosec bandit B105
or password_strength == "NONE" # nosec bandit B105
):
return True
return False
def dedup(event):
return event.deep_get("event", "adminid", default="<ADMIN_ID_NOT_FOUND>")
def title(event):
return (
f"[Zscaler.ZIA]: Password settings are insecure for admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Insecure.Password.Settings
Description: This rule detects when password settings are insecure.
DisplayName: ZIA Insecure Password Settings
Runbook: Set the secure password configurations.
Reference: https://help.zscaler.com/zia/customizing-your-admin-account-settings
Enabled: true
Filename: zia_insecure_password_settings.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0005:T1556.009 # Defense Evasion: Modify Authentication Process: Conditional Access Policies
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSany of:
event.postaction.authFrequencyisPERMANENT_COOKIEevent.postaction.passwordExpiryisNEVERevent.postaction.passwordStrengthisNONE
Indicators
These rows show field, operator, and value matches.
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Set the secure password configurations.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "UPDATE",
"adminid": "admin@test.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "AUTHENTICATION_SETTINGS",
"clientip": "1.2.3.4",
"errorcode": "None",
"interface": "UI",
"postaction": {
"authFrequency": "PERMANENT_COOKIE",
"autoProvision": false,
"directorySyncMigrateToScimEnabled": false,
"kerberosEnabled": false,
"mobileAdminSamlIdpEnabled": false,
"oneTimeAuth": "OTP_DISABLED",
"orgAuthType": "SAFECHANNEL_DIR",
"passwordExpiry": "NEVER",
"passwordStrength": "NONE",
"samlEnabled": false
},
"preaction": {
"authFrequency": "DAILY_COOKIE",
"autoProvision": false,
"directorySyncMigrateToScimEnabled": false,
"kerberosEnabled": false,
"mobileAdminSamlIdpEnabled": false,
"oneTimeAuth": "OTP_DISABLED",
"orgAuthType": "SAFECHANNEL_DIR",
"passwordExpiry": "NEVER",
"passwordStrength": "NONE",
"samlEnabled": false
},
"recordid": "356",
"resource": "None",
"result": "SUCCESS",
"subcategory": "AUTH_SETTINGS_PROFILE",
"time": "2024-11-04 16:29:24.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Log Streaming Disabled
#This rule detects when ZIA log streaming was disabled.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
action = event.deep_get("event", "action", default="ACTION_NOT_FOUND")
category = event.deep_get("event", "category", default="CATEGORY_NOT_FOUND")
if action == "DELETE" and category == "NSS":
return True
return False
def title(event):
cloud_connection_url = event.deep_get(
"event",
"preaction",
"cloudNssSiemConfiguration",
"connectionURL",
default="<CLOUD_CONNECTION_URL_NOT_FOUND>",
)
return (
f"[Zscaler.ZIA]: Log streaming for location [{cloud_connection_url}] "
f"was deleted by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Log.Streaming.Disabled
Description: This rule detects when ZIA log streaming was disabled.
DisplayName: ZIA Log Streaming Disabled
Runbook: Verify that this change was planned. If not, make sure to restore previous settings.
Reference: https://help.zscaler.com/zia/about-nss-feeds
Enabled: true
Filename: zia_log_streaming_disabled.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0005:T1562.008 # Disable or Modify Cloud Logs
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisDELETEevent.categoryisNSS
Indicators
These rows show field, operator, and value matches.
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
connectionURL | event.preaction.cloudNssSiemConfiguration.connectionURL |
Response runbook
Verify that this change was planned. If not, make sure to restore previous settings.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "DELETE",
"adminid": "admin@test.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "NSS",
"clientip": "1.2.3.4",
"errorcode": "None",
"interface": "UI",
"postaction": {},
"preaction": {
"cloudNss": true,
"cloudNssSiemConfiguration": {
"connectionHeaders": [
"123:123"
],
"connectionURL": "https://logs.company.net/http/a7adc684-f65c-42af-9519-0a0786656f20",
"lastSuccessFullTest": 0,
"maxBatchSize": 512,
"nssType": "NSS_FOR_WEB",
"oAuthAuthentication": false,
"siemType": "OTHER",
"testConnectivityCode": 0
},
"customEscapedCharacter": [
"ASCII_44",
"ASCII_92",
"ASCII_34"
],
"duplicateLogs": 0,
"epsRateLimit": 0,
"feedOutputFormat": "\\{ \"sourcetype\" : \"zscalernss-web\", \"event\" : \\{\"datetime\":\"%d{yy}-%02d{mth}-%02d{dd} %02d{hh}:%02d{mm}:%02d{ss}\",\"reason\":\"%s{reason}\",\"event_id\":\"%d{recordid}\",\"protocol\":\"%s{proto}\",\"action\":\"%s{action}\",\"transactionsize\":\"%d{totalsize}\",\"responsesize\":\"%d{respsize}\",\"requestsize\":\"%d{reqsize}\",\"urlcategory\":\"%s{urlcat}\",\"serverip\":\"%s{sip}\",\"requestmethod\":\"%s{reqmethod}\",\"refererURL\":\"%s{ereferer}\",\"useragent\":\"%s{eua}\",\"product\":\"NSS\",\"location\":\"%s{elocation}\",\"ClientIP\":\"%s{cip}\",\"status\":\"%s{respcode}\",\"user\":\"%s{elogin}\",\"url\":\"%s{eurl}\",\"vendor\":\"Zscaler\",\"hostname\":\"%s{ehost}\",\"clientpublicIP\":\"%s{cintip}\",\"threatcategory\":\"%s{malwarecat}\",\"threatname\":\"%s{threatname}\",\"filetype\":\"%s{filetype}\",\"appname\":\"%s{appname}\",\"app_status\":\"%s{app_status}\",\"pagerisk\":\"%d{riskscore}\",\"threatseverity\":\"%s{threatseverity}\",\"department\":\"%s{edepartment}\",\"urlsupercategory\":\"%s{urlsupercat}\",\"appclass\":\"%s{appclass}\",\"dlpengine\":\"%s{dlpeng}\",\"urlclass\":\"%s{urlclass}\",\"threatclass\":\"%s{malwareclass}\",\"dlpdictionaries\":\"%s{dlpdict}\",\"fileclass\":\"%s{fileclass}\",\"bwthrottle\":\"%s{bwthrottle}\",\"contenttype\":\"%s{contenttype}\",\"unscannabletype\":\"%s{unscannabletype}\",\"deviceowner\":\"%s{deviceowner}\",\"devicehostname\":\"%s{devicehostname}\",\"keyprotectiontype\":\"%s{keyprotectiontype}\"\\}\\}\n",
"feedStatus": "ENABLED",
"id": 2898,
"jsonArrayToggle": true,
"name": "test-feed-2",
"nssFeedType": "JSON",
"nssFilter": {
"securityFeedFilter": false
},
"nssLogType": "WEBLOG",
"timeZone": "GMT",
"userObfuscation": "DISABLED"
},
"recordid": "371",
"resource": "test-feed-2",
"result": "SUCCESS",
"subcategory": "NSS_FEED",
"time": "2024-11-04 16:34:34.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Logs Downloaded
#This rule detects when ZIA Audit Logs were downloaded.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
action = event.deep_get("event", "action", default="ACTION_NOT_FOUND")
category = event.deep_get("event", "category", default="CATEGORY_NOT_FOUND")
if action == "DOWNLOAD" and category == "AUDIT_LOGS":
return True
return False
def title(event):
return (
f"[Zscaler.ZIA]: Audit logs were downloaded by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Logs.Downloaded
Description: This rule detects when ZIA Audit Logs were downloaded.
DisplayName: ZIA Logs Downloaded
Runbook: Verify that this change was planned. If not, make sure no sensitive information was leaked.
Reference: https://help.zscaler.com/zia/about-audit-logs
Enabled: true
Filename: zia_logs_downloaded.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0007:T1654 # Log Enumeration
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisDOWNLOADevent.categoryisAUDIT_LOGS
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"DOWNLOAD" |
event.category | eq |
| field:"event.category" kind:eq value:"AUDIT_LOGS" |
event.errorcode | eq |
| field:"event.errorcode" kind:eq value:"None" |
event.result | eq |
| field:"event.result" kind:eq value:"SUCCESS" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, make sure no sensitive information was leaked.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "DOWNLOAD",
"adminid": "admin@test.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "AUDIT_LOGS",
"clientip": "1.2.3.4",
"errorcode": "None",
"interface": "UI",
"postaction": {},
"preaction": {},
"recordid": "363",
"resource": "None",
"result": "SUCCESS",
"subcategory": "AUDIT_LOGS",
"time": "2024-11-04 16:31:24.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Password Expiration
#This rule detects when password expiration was set/removed.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
action = event.deep_get("event", "action", default="ACTION_NOT_FOUND")
category = event.deep_get("event", "category", default="CATEGORY_NOT_FOUND")
password_exp_pre = event.deep_get(
"event",
"preaction",
"passwordExpirationEnabled",
default="",
)
password_exp_post = event.deep_get(
"event",
"postaction",
"passwordExpirationEnabled",
default="",
)
if action == "UPDATE" and category == "LOGIN" and password_exp_pre != password_exp_post:
return True
return False
def title(event):
return (
f"[Zscaler.ZIA]: Password expiration setting was changed by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Password.Expiration
Description: This rule detects when password expiration was set/removed.
DisplayName: ZIA Password Expiration
Runbook: Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Reference: https://help.zscaler.com/zia/configuring-password-expiration
Enabled: true
Filename: zia_password_expiration.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0007:T1201 # Password Policy Discovery
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisUPDATEevent.categoryisLOGINevent.preaction.passwordExpirationEnableddiffers from fieldevent.postaction.passwordExpirationEnabled
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"UPDATE" |
event.category | eq |
| field:"event.category" kind:eq value:"LOGIN" |
event.errorcode | eq |
| field:"event.errorcode" kind:eq value:"None" |
event.preaction.passwordExpirationEnabled | cross_field_compare |
| field:"event.preaction.passwordExpirationEnabled" kind:cross_field_compare value:"event.postaction.passwordExpirationEnabled" |
event.result | eq |
| field:"event.result" kind:eq value:"SUCCESS" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "UPDATE",
"adminid": "admin@16991311.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "LOGIN",
"clientip": "123.123.123.123",
"errorcode": "None",
"interface": "UI",
"postaction": {
"passwordExpirationEnabled": true,
"passwordExpiryDays": 180
},
"preaction": {
"passwordExpirationEnabled": false,
"passwordExpiryDays": 180
},
"recordid": "331",
"resource": "None",
"result": "SUCCESS",
"subcategory": "PASSWORD_EXPIRY",
"time": "2024-10-22 22:12:25.000000000"
},
"sourcetype": "zscalernss-audit"
}
ZIA Trust Modification
#This rule detects when SAML authentication was enabled/disabled.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Detection logic
from panther_zscaler_helpers import zia_alert_context, zia_success
def rule(event):
if not zia_success(event):
return False
action = event.deep_get("event", "action", default="ACTION_NOT_FOUND")
category = event.deep_get("event", "category", default="CATEGORY_NOT_FOUND")
saml_enabled_pre = event.deep_get("event", "preaction", "samlEnabled", default="")
saml_enabled_post = event.deep_get("event", "postaction", "samlEnabled", default="")
# Only alert if both fields are present and have different values
if (
action == "UPDATE"
and category == "ADMINISTRATOR_MANAGEMENT"
and saml_enabled_pre != ""
and saml_enabled_post != ""
and saml_enabled_pre != saml_enabled_post
):
return True
return False
def title(event):
return (
f"[Zscaler.ZIA]: SAML configuration was changed by admin with id "
f"[{event.deep_get('event', 'adminid', default='<ADMIN_ID_NOT_FOUND>')}]"
)
def alert_context(event):
return zia_alert_context(event)
Rule specification
AnalysisType: rule
RuleID: ZIA.Trust.Modification
Description: This rule detects when SAML authentication was enabled/disabled.
DisplayName: ZIA Trust Modification
Runbook: Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Reference: https://help.zscaler.com/zia/configuring-saml
Enabled: true
Filename: zia_trust_modification.py
Severity: Medium
Reports:
MITRE ATT&CK:
- TA0004:T1484.002 # Domain or Tenant Policy Modification: Trust Modification
LogTypes:
- Zscaler.ZIA.AdminAuditLog
DedupPeriodMinutes: 60
Threshold: 1
Stages and Predicates
Fires on Zscaler.ZIA.AdminAuditLog events when all of the conditions below hold.
Condition
event.errorcodeisNoneevent.resultisSUCCESSevent.actionisUPDATEevent.categoryisADMINISTRATOR_MANAGEMENTevent.preaction.samlEnabledis not""event.postaction.samlEnabledis not""event.preaction.samlEnableddiffers from fieldevent.postaction.samlEnabled
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event.action | eq |
| field:"EventType" kind:eq value:"UPDATE" |
event.category | eq |
| field:"event.category" kind:eq value:"ADMINISTRATOR_MANAGEMENT" |
event.errorcode | eq |
| field:"event.errorcode" kind:eq value:"None" |
event.preaction.samlEnabled | cross_field_compare |
| field:"event.preaction.samlEnabled" kind:cross_field_compare value:"event.postaction.samlEnabled" |
event.result | eq |
| field:"event.result" kind:eq value:"SUCCESS" |
Output fields
Fields the rule emits when it matches, drawn from the rule's alert_context.
| Field | Source |
|---|---|
action | event.action |
admin_id | event.adminid |
category | event.category |
client_ip | event.clientip |
preaction | event.preaction |
postaction | event.postaction |
Response runbook
Verify that this change was planned. If not, revert the change and ensure this doesn't happen again.
Worked example
A sample event from the rule's unit tests that triggers a match.Sample Test Event
{
"event": {
"action": "UPDATE",
"adminid": "admin@16991311.zscalerbeta.net",
"auditlogtype": "ZIA",
"category": "ADMINISTRATOR_MANAGEMENT",
"clientip": "123.123.123.123",
"errorcode": "None",
"interface": "UI",
"postaction": {
"certFilename": "abc.crt",
"productId": 0,
"samlEnabled": true
},
"preaction": {
"productId": 0,
"samlEnabled": false
},
"recordid": "332",
"resource": "None",
"result": "SUCCESS",
"subcategory": "ADMINISTRATOR_SAML",
"time": "2024-10-22 22:13:23.000000000"
},
"sourcetype": "zscalernss-audit"
}