An ACL rule with no condition and no script grants its operation to everyone who holds the role, which on a table such as hr_case usually means every authenticated employee. The rule looks configured, appears in the list with a role attached, and passes a casual review — the missing condition is what removes the restriction. The read-only detection script below lists them on your instance, ordered by how sensitive the table is.
What This Is
An Access Control List (ACL) rule in ServiceNow controls who can perform what operation (read, write, create, delete) on which table or field. Every time a user or API call accesses a record, the ACL engine evaluates applicable rules in a specific order to determine whether access is granted.
An ACL with an empty condition field and no script (or a script that simply returns true) grants access to every user who holds the required role. If no role is specified either, the ACL grants access to any authenticated user — effectively making the table public to your entire organization.
On tables containing personally identifiable information (PII), financial data, HR records, or security-critical configuration, this is one of the most common and most dangerous misconfigurations in ServiceNow. This is not a theoretical risk — it is the single most documented ServiceNow vulnerability class in independent security research, with multiple firms independently confirming exploitation at scale (see Independent Research Validates the Severity below).
How ACL Evaluation Works (Critical Context)
ServiceNow evaluates ACLs in this order:
- Table-level ACL (e.g.,
hr_case) — checked first - Field-level ACL (e.g.,
hr_case.salary) — checked if table-level passes - If no ACL exists for a table, the
glide.sm.default_modeproperty determines the outcome: in standard mode (default), access is granted only tosecurity_admin; in high security mode (hs), access is denied to everyone (→ High Security Plugin (HSP) and Security Hardening Settings) - If multiple ACLs match, the system evaluates them with AND logic — all must pass
- Wildcard ACLs (
*table) serve as catch-all defaults
The danger: when a table-level ACL has an empty condition and a broad role like itil, it becomes the permissive gate that all field-level ACLs sit behind. Even well-configured field-level ACLs are irrelevant if the table-level ACL already grants full READ access.
ACL Components
| Component | What It Controls | Danger When Empty/Broad |
|---|---|---|
| Type | Record, field, client-callable script, REST endpoint | Record-level = broadest impact |
| Operation | read, write, create, delete | Empty = applies to ALL operations |
| Name | Table or table.field | * = all tables |
| Role | Required role(s) to pass | Empty = any authenticated user |
| Condition | Encoded query filter | Empty = all records match |
| Script | Server-side JavaScript | return true = always passes |
Independent Research Validates the Severity
Empty-condition ACLs are the most studied ServiceNow vulnerability class in public security research. Multiple independent firms have confirmed exploitation at enterprise scale:
| Research | Year | Finding | Direct Relevance to Empty Condition ACLs on Sensitive Tables |
|---|---|---|---|
| AppOmni — ACL Misconfiguration (source) | 2022-2023 | Over 70% of enterprise ServiceNow instances leaked sensitive data via ACL misconfigurations. Over 90% leaked some form of data. Attackers abused the SimpleListWidget — a widget set to public by default — to query tables where ACLs had no role, no condition, and no script. |
This is exactly Empty Condition ACLs on Sensitive Tables. AppOmni's research targeted the precise misconfiguration this document describes: ACLs with empty conditions on tables containing internal users, installed applications, service catalog items, asset items, and CMDB entries. |
| AppOmni — Knowledge Base Exposure (source) | 2023-2024 | ~45% of enterprise instances (~1,000+ tested) leaked corporate Knowledge Base data to unauthenticated users. KB articles often contain internal procedures, architecture details, and credentials. | Demonstrates that empty-condition ACLs on kb_knowledge expose draft and retired articles containing sensitive operational information. |
| Obsidian Security — Public ACL Exposure (source) | 2023 | When ACLs have no role, no condition, and no script, unauthenticated users can access data via public Service Portal widgets. Obsidian identified that the Widget Allow List permits any allowed widget to access any table the ACL allows — even if the widget wasn't designed for that table. | Confirms that empty-condition ACLs extend exposure beyond authenticated users to the public internet via Service Portal. |
| Varonis Threat Labs — "Count(er) Strike" / CVE-2025-3648 (source) | 2024-2025 | Data inference via conditional ACL rules and range queries (CVSS 8.2). Even with conditional ACLs, attackers can infer data via record count elements. Self-registered anonymous users can exploit this. | Empty-condition ACLs make Varonis's inference technique unnecessary — when the ACL grants full access, the attacker doesn't need to infer data through counts. But this research demonstrates that even after fixing empty conditions, the replacement logic must also defend against inference attacks. |
| Assetnote — CVE-2024-4879 / CVE-2024-5217 / CVE-2024-5178 (source) | 2024 | Three chained vulnerabilities for full database access. Unauthenticated Jelly template injection → RCE → file read. CVSS 9.3. | When platform-level vulnerabilities bypass authentication, empty-condition ACLs mean there is no secondary defense. An attacker who exploits CVE-2024-4879 and lands in a session context gets full READ on every table with an empty-condition ACL. |
| Kudelski Security — Range Query ACL Bypass (source) | 2025 | Unauthorized data exposure via range queries exploiting conditional ACL evaluation. ServiceNow responded by introducing Query ACLs, Security Data Filters, and Deny-Unless ACLs in Xanadu/Yokohama. | ServiceNow's response to this research introduced new ACL mechanisms that directly address empty-condition ACLs: Deny-Unless ACLs invert the default-permit behavior that makes empty conditions dangerous. |
| AppOmni — "BodySnatcher" / CVE-2025-12420 (source) | 2025 | Virtual Agent API flaw (CVSS 9.3) allows unauthenticated impersonation of any user via email address, bypassing MFA/SSO. Attacker can impersonate admin and create backdoor accounts. | Empty-condition ACLs on sys_user and sys_user_has_role mean an attacker who exploits BodySnatcher can immediately enumerate all users and role assignments — the first step in privilege escalation. |
Key insight: This is the only ServiceNow misconfiguration class with six independent research confirmations from different security firms. ServiceNow's own emergency response in November 2023 — performing proactive maintenance across all customer instances to amend ACLs with missing fields — confirms the vendor considers this a platform-level systemic risk, not an individual customer configuration issue (Arctic Wolf, 2023).
Why This Is Dangerous
Attack Scenario: HR Data Exfiltration
Precondition: ACL on hr_case table has READ operation with role itil and empty condition.
Attack chain:
Reconnaissance: Attacker (or curious employee) with
itilrole discovers thathr_caseis accessible. In most enterprises,itilis granted to hundreds or thousands of users (help desk agents, IT staff, developers in sub-production).UI Enumeration: Navigate to
hr_case_list.do— the list view loads all HR cases the user can read. With an empty condition ACL, this means ALL HR cases across the organization.API Bulk Extraction: For large-scale extraction, the attacker uses the Table API:
GET /api/now/table/hr_case ?sysparm_fields=number,short_description,assigned_to,opened_by, category,subcategory,description,comments_and_work_notes &sysparm_limit=10000 &sysparm_offset=0Without rate limiting (see Table API Unrestricted Exposure), the entire table can be extracted in a single paginated session.
PII Harvesting: HR cases typically contain:
- Employee names and internal IDs
- Salary dispute details and compensation information
- Harassment and discrimination complaints (with names of accusers and accused)
- Medical accommodation requests (disability information, health conditions)
- Termination proceedings and performance improvement plans
- Personal contact information
- Immigration and visa status details
Lateral Exploitation: Extracted data enables:
- Social engineering attacks using knowledge of internal HR situations
- Blackmail or extortion using sensitive complaint data
- Insider trading if HR data reveals executive departures or restructuring
- Identity theft using PII from employee profiles
Impact scale: A single misconfigured ACL on hr_case in an organization of 10,000 employees could expose hundreds of thousands of HR records spanning years of operational data.
Real-world corroboration: AppOmni's 2022-2023 research confirmed this exact attack chain is exploitable at scale. Their testing found over 70% of enterprise ServiceNow instances had ACL configurations that allowed data extraction from sensitive tables — including user records, CMDB entries, and service catalog items. The SimpleListWidget, set to public by default, enabled unauthenticated users to query any table where ACLs lacked roles or conditions (AppOmni, 2023). Obsidian Security independently confirmed that the Widget Allow List permits any public widget to access any table the ACL allows, extending the exposure to unauthenticated external attackers — not just internal users with itil roles (Obsidian Security, 2023).
Attack Scenario: Widget-Based Data Extraction (Unauthenticated)
Precondition: ACL on a sensitive table (e.g., sys_user, cmdb_ci, kb_knowledge) has READ operation with no role, no condition, and no script. Instance has a public Service Portal.
Attack chain:
Public portal discovery: Attacker identifies the organization's Service Portal URL (typically
https://<instance>.service-now.com/sp). No authentication required.Widget enumeration: The
SimpleListWidget— included in ServiceNow's default Service Portal configuration — can be pointed at any table via URL parameters. The widget itself has no role restriction by default.Table API via widget: Attacker crafts requests to the widget endpoint, specifying the target table. The widget's server-side script executes a GlideRecord query, and the ACL engine evaluates access. With an empty-condition, no-role ACL, the evaluation passes for the unauthenticated
guestuser.Bulk extraction: The attacker paginates through all records in the table. No authentication token, no session cookie, no API key required.
Scale: AppOmni tested this against hundreds of enterprise instances and found that over 90% were leaking some form of sensitive information through this exact vector. Information on internal users, installed applications, service catalog items, asset items, and CMDB entries was disclosed by default.
Impact: Complete table data exposure to the public internet. This is not an insider threat — any external attacker with knowledge of ServiceNow portal mechanics can extract data without any credentials.
ServiceNow's response: In November 2023, ServiceNow performed emergency proactive maintenance across all customer instances, amending role-less ACLs and changing the behavior of specific platform widgets. This confirms the vendor treated this as a platform-level emergency, not an individual misconfiguration (Arctic Wolf, 2023; Threatpost).
Attack Scenario: CMDB Reconnaissance for Infrastructure Attack
Precondition: ACL on cmdb_ci or cmdb_ci_server has READ with empty condition.
Attack chain:
- Attacker extracts full CMDB via Table API: server names, IP addresses, OS versions, patch levels, application stack details
- Maps internal network topology without any network scanning (bypasses IDS/IPS)
- Identifies high-value targets: domain controllers, database servers, certificate authorities
- Identifies unpatched systems or EOL operating systems
- Plans targeted attack using perfect knowledge of the environment
This is equivalent to handing an attacker a complete network diagram — without them triggering a single security alert.
Real-world corroboration: Resecurity documented a global reconnaissance campaign in 2024 exploiting ServiceNow instances via CVE-2024-4879 and CVE-2024-5217. The campaign's primary objective was user list extraction and infrastructure mapping — exactly the data exposed by empty-condition ACLs on cmdb_ci and sys_user. Attackers used automated tools to scan for vulnerable instances, exploit the Jelly template injection chain (Assetnote, CVE-2024-4879), and dump database contents. Empty-condition ACLs on CMDB tables meant no secondary defense existed once authentication was bypassed (Resecurity, 2024; Assetnote, 2024).
Attack Scenario: Credential Harvesting from Change Records
Precondition: ACL on change_request has READ with empty condition.
Attack chain:
- Extract all change records via Table API
- Search
descriptionandwork_notesfields for patterns: passwords, connection strings, API keys, certificates - Developers and ops teams routinely paste credentials in change request fields during implementations
- Attacker now has valid credentials for multiple systems
Real-world corroboration: This attack chain is amplified by CVE-2025-12420 ("BodySnatcher"), discovered by AppOmni in 2025 (CVSS 9.3). An attacker who exploits the Virtual Agent API flaw can impersonate any user using only their email address, bypassing MFA and SSO. Once impersonating a user with itil role, empty-condition ACLs on change_request grant full READ access to all change records — including any credentials embedded in work notes. The combination of BodySnatcher + empty ACLs on change records = unauthenticated credential harvesting at scale (AppOmni, 2025; The Hacker News, 2026).
Attack Scenario: Knowledge Base Data Leak to the Public Internet
Precondition: ACL on kb_knowledge has READ with empty condition. Knowledge Base articles include draft and retired articles not intended for public access.
Attack chain:
Public KB discovery: Organization's Service Portal includes a Knowledge Base widget. Unauthenticated users can search and browse published articles by design.
Draft/retired article exposure: With an empty-condition ACL, the query returns ALL knowledge articles — including drafts with internal procedures, retired articles with outdated credentials, and restricted articles containing architecture diagrams.
Search-based extraction: Attacker searches for high-value keywords: "password", "connection string", "admin", "root", "API key", "credentials", "architecture", "network diagram".
Bulk download: Using the Table API or widget pagination, the attacker downloads the entire KB including attachments.
Impact: AppOmni's 2023-2024 follow-up research confirmed this exact scenario. They found over 1,000 individual enterprise instances (~45% of those tested) were unintentionally exposing KB data to unauthenticated users (AppOmni, 2023-2024). BleepingComputer covered the disclosure, noting the scale of the exposure across Fortune 500 companies (BleepingComputer, 2024).
Sensitive Tables to Audit
The following tables should be priority-checked for empty condition ACLs. This list is organized by risk category and informed by independent research findings. AppOmni's 2022-2023 research specifically documented data leakage from sys_user, cmdb_ci, sc_cat_item, and alm_asset tables across 70%+ of enterprise instances (AppOmni, 2023). Their KB follow-up added kb_knowledge to the critical list (AppOmni, 2023-2024). Varonis's Count(er) Strike research (CVE-2025-3648) demonstrated that even tables with conditional ACLs can leak data via inference — tables with empty-condition ACLs are fully exposed without needing the inference technique (Varonis, 2024-2025).
Tier 1: CRITICAL — Contains PII or Authentication Data
| Table | Content | PII Risk | Common Misconfiguration |
|---|---|---|---|
hr_case |
HR cases, complaints, disputes | Salary, medical, harassment details | Empty condition with itil role |
sn_hr_core_profile |
Employee HR profiles | SSN/NI numbers, bank details, DOB | Inherited from task table ACL |
sn_hr_core_case_workforce |
Workforce HR cases | Performance reviews, termination | Often no dedicated ACL at all |
sys_user |
All user accounts | Email, phone, manager, cost center | Overly broad READ for portal users |
sys_user_has_role |
Role assignments | Who has admin/security roles | Exposes privilege mapping |
sys_user_group |
Group memberships | Organizational structure | Often fully open for catalog purposes |
customer_contact |
External contact records | Customer PII | Open for CSM workflows |
ast_contract |
Contracts | Financial terms, vendor details | Open for asset management |
fm_expense_line |
Expense records | Financial transactions | Often no dedicated ACL |
Tier 2: HIGH — Infrastructure and Security Data
| Table | Content | Risk | Common Misconfiguration |
|---|---|---|---|
cmdb_ci |
All configuration items | Network topology, server details | Open for ITSM workflows |
cmdb_ci_server |
Server records | IPs, OS versions, patch levels | Inherited from cmdb_ci |
cmdb_ci_db_instance |
Database instances | DB types, connection details | Inherited from cmdb_ci |
discovery_credentials |
Discovery credentials | Passwords, SSH keys | Should be security_admin only |
sys_certificate |
SSL/TLS certificates | Private key references | Should be security_admin only |
ecc_queue |
MID Server queue | Integration payloads, credentials | Often overlooked entirely |
sys_properties |
System properties | Security settings, API keys | Open for admin convenience |
syslog |
System logs | Error details, stack traces | May expose system internals |
Tier 3: MEDIUM — Operational Data
| Table | Content | Risk | Common Misconfiguration |
|---|---|---|---|
change_request |
Change records | May contain credentials in notes | Open for ITSM workflows |
incident |
Incidents | May contain sensitive details | Broad itil access |
sc_req_item |
Service requests | May contain access requests | Open for portal |
kb_knowledge |
Knowledge articles | Internal procedures, workarounds | Draft/retired articles exposed — AppOmni confirmed ~45% of instances leak KB data |
sys_attachment |
File attachments | Any file type attached to records | ACL follows parent record |
Research note: AppOmni's 2022 disclosure specifically named
sys_user, installed application tables, service catalog (sc_cat_item,sc_category), asset tables (alm_asset,alm_hardware), and CMDB tables as the most commonly exposed. Obsidian Security's independent research added that the Widget Allow List mechanism extends exposure to any table the ACL permits — even tables not explicitly configured on any widget (Obsidian Security, 2023). Addsc_cat_item,sc_category,alm_asset, andalm_hardwareto your audit scope if not already included.
How to Detect
Quick Manual Check
Navigate to the ACL list in your instance:
URL: https://<instance>.service-now.com/sys_security_acl_list.do
Filter:
Active = true
AND Condition is empty
AND Script is empty
AND Operation = read
Sort by: Name (table name)
This gives you every active READ ACL that grants access based solely on role — no additional conditions or scripts.
External tool comparison: AppOmni and Obsidian Security offer SaaS security platforms that continuously scan ServiceNow instances for empty-condition/role-less ACLs — the same class of misconfiguration that triggered ServiceNow's emergency remediation in November 2023. ServiceNow's own Instance Scan framework includes community-contributed checks for missing ACLs on custom tables (ServiceNow DevProgram — Instance Scan Checks). ServiceNow Security Center (SSC) also flags some empty-condition ACLs, but its coverage is not comprehensive — AppOmni found widespread exposure in instances that had SSC enabled. The scripts below provide deeper analysis including role enumeration, tier classification, and admin override detection that external tools do not expose.
Comprehensive Detection Script
Run this as a background script (System Definition > Scripts - Background). Requires admin or security_admin role.
/*
* ACL-001 Detection Script
* Finds empty-condition ACLs on sensitive tables
*
* Output: List of ACLs that grant broad access without conditions
* Run as: Background script with admin role
* Impact: Read-only, safe to run in production
*
* ServiceNow versions: Orlando+
*/
// Tier 1: CRITICAL — PII and authentication data
var tier1 = [
'hr_case', 'sn_hr_core_profile', 'sn_hr_core_case_workforce',
'sys_user', 'sys_user_has_role', 'sys_user_group',
'customer_contact', 'ast_contract', 'fm_expense_line',
'sn_hr_le_case', 'sn_hr_le_investigation'
];
// Tier 2: HIGH — Infrastructure and security data
var tier2 = [
'cmdb_ci', 'cmdb_ci_server', 'cmdb_ci_db_instance',
'cmdb_ci_appl', 'cmdb_ci_win_server', 'cmdb_ci_linux_server',
'discovery_credentials', 'sys_certificate', 'ecc_queue',
'sys_properties', 'syslog', 'syslog_transaction',
'sa_credential', 'mid_server'
];
// Tier 3: MEDIUM — Operational data
var tier3 = [
'change_request', 'incident', 'problem',
'sc_req_item', 'sc_request', 'kb_knowledge',
'sys_attachment', 'sys_email', 'sys_journal_field'
];
var allTables = {
'CRITICAL': tier1,
'HIGH': tier2,
'MEDIUM': tier3
};
var findings = [];
for (var severity in allTables) {
var tables = allTables[severity];
for (var i = 0; i < tables.length; i++) {
var tableName = tables[i];
var gr = new GlideRecord('sys_security_acl');
gr.addQuery('active', true);
gr.addEncodedQuery(
'nameSTARTSWITH' + tableName +
'^conditionISEMPTY'
);
gr.query();
while (gr.next()) {
var hasScript = gr.getValue('script') &&
gr.getValue('script').trim() !== '' &&
gr.getValue('script').trim() !== 'return true;' &&
gr.getValue('script').trim() !== 'return true';
if (!hasScript) {
var roleName = '';
var roleGr = new GlideRecord('sys_security_acl_role');
roleGr.addQuery('sys_security_acl', gr.getUniqueValue());
roleGr.query();
var roles = [];
while (roleGr.next()) {
roles.push(roleGr.getDisplayValue('sys_user_role'));
}
roleName = roles.length > 0 ? roles.join(', ') : 'NO ROLE (any authenticated user)';
findings.push({
severity: severity,
table: gr.getValue('name'),
operation: gr.getValue('operation'),
roles: roleName,
sys_id: gr.getUniqueValue(),
admin_override: gr.getValue('admin_overrides') == 'true'
});
}
}
}
}
// Output results
gs.info('=== ACL-001 AUDIT RESULTS ===');
gs.info('Total findings: ' + findings.length);
gs.info('');
var critCount = 0, highCount = 0, medCount = 0;
for (var f = 0; f < findings.length; f++) {
var finding = findings[f];
if (finding.severity === 'CRITICAL') critCount++;
else if (finding.severity === 'HIGH') highCount++;
else medCount++;
gs.info('[' + finding.severity + '] ' +
'Table: ' + finding.table +
' | Op: ' + finding.operation +
' | Roles: ' + finding.roles +
' | Admin Override: ' + finding.admin_override +
' | SysID: ' + finding.sys_id);
}
gs.info('');
gs.info('Summary: ' + critCount + ' CRITICAL, ' + highCount + ' HIGH, ' + medCount + ' MEDIUM');
gs.info('');
gs.info('NEXT STEPS:');
gs.info('1. Review each CRITICAL finding immediately');
gs.info('2. For each finding, add appropriate conditions (see ACL-001 remediation)');
gs.info('3. Check if Table API exposes these tables (see API-001)');
gs.info('4. Check if field-level ACLs protect PII columns (see ACL-005)');
gs.info('5. Review access logs for potential unauthorized access during exposure window');
Automated Monitoring Script (Scheduled Job)
Create a scheduled job that runs daily to detect new empty-condition ACLs:
/*
* ACL-001 Monitoring — Scheduled Job
* Runs daily, creates incident if new empty-condition ACLs found
*
* Setup: System Definition > Scheduled Jobs > New
* Run as: System
* Schedule: Daily at 06:00
*/
var sensitiveTablePatterns = [
'hr_case', 'sn_hr_', 'sys_user', 'cmdb_ci',
'discovery_credential', 'sys_certificate',
'customer_contact', 'ast_contract'
];
var gr = new GlideRecord('sys_security_acl');
gr.addQuery('active', true);
gr.addEncodedQuery('conditionISEMPTY^scriptISEMPTY');
gr.addQuery('sys_created_on', '>=', gs.daysAgoStart(1));
gr.query();
var newFindings = [];
while (gr.next()) {
var aclName = gr.getValue('name');
for (var p = 0; p < sensitiveTablePatterns.length; p++) {
if (aclName.indexOf(sensitiveTablePatterns[p]) === 0) {
newFindings.push(aclName + ' (' + gr.getValue('operation') + ')');
break;
}
}
}
if (newFindings.length > 0) {
var inc = new GlideRecord('incident');
inc.initialize();
inc.setValue('short_description',
'[SECURITY] ACL-001: ' + newFindings.length +
' new empty-condition ACL(s) detected on sensitive tables');
inc.setValue('description',
'The daily ACL security scan detected new empty-condition ACLs ' +
'on sensitive tables. These ACLs grant access based solely on role ' +
'without additional conditions, potentially exposing sensitive data.\n\n' +
'Affected ACLs:\n' + newFindings.join('\n') +
'\n\nRemediation: See ACL-001 playbook.');
inc.setValue('category', 'security');
inc.setValue('priority', 1);
inc.setValue('assignment_group', 'Security Operations');
inc.insert();
}
Remediation
Step 1: Assess Impact Before Changing
Before modifying any ACL, understand who currently uses the access:
/*
* Pre-remediation impact check
* Identifies who is currently accessing the table
* Run in background script
*/
var tableName = 'hr_case'; // Change to your target table
// Check transaction logs for recent access
var txn = new GlideRecord('syslog_transaction');
txn.addQuery('url', 'CONTAINS', tableName);
txn.addQuery('sys_created_on', '>=', gs.daysAgoStart(30));
txn.orderByDesc('sys_created_on');
txn.setLimit(100);
txn.query();
var accessors = {};
while (txn.next()) {
var user = txn.getValue('user');
if (!accessors[user]) {
accessors[user] = { count: 0, lastAccess: '' };
}
accessors[user].count++;
if (!accessors[user].lastAccess) {
accessors[user].lastAccess = txn.getValue('sys_created_on');
}
}
gs.info('=== Users accessing ' + tableName + ' in last 30 days ===');
for (var user in accessors) {
gs.info(user + ': ' + accessors[user].count +
' accesses, last: ' + accessors[user].lastAccess);
}
Step 2: Define Appropriate Conditions
Replace empty conditions with role-based and ownership-based conditions:
Pattern A: HR Tables — Owner/Assigned/Manager Only
// ACL Condition Script for hr_case READ
(function() {
// HR roles — full access
if (gs.hasRole('sn_hr_core.admin') ||
gs.hasRole('sn_hr_core.manager') ||
gs.hasRole('sn_hr_core.case_writer')) {
return true;
}
// Case participants — own records only
var userId = gs.getUserID();
if (current.getValue('opened_for') == userId ||
current.getValue('opened_by') == userId ||
current.getValue('assigned_to') == userId) {
return true;
}
// Direct manager of the case subject
var openedFor = current.getValue('opened_for');
if (openedFor) {
var userGr = new GlideRecord('sys_user');
if (userGr.get(openedFor) && userGr.getValue('manager') == userId) {
return true;
}
}
return false;
})()
Pattern B: CMDB Tables — Role-Based with Scope
// ACL Condition Script for cmdb_ci READ
(function() {
if (gs.hasRole('itil_admin') || gs.hasRole('cmdb_editor')) {
return true;
}
if (gs.hasRole('itil')) {
var userGroups = gs.getUser().getMyGroups();
var supportGroup = current.getValue('support_group');
if (supportGroup && userGroups.indexOf(supportGroup) >= 0) {
return true;
}
if (current.getValue('assigned_to') == gs.getUserID()) {
return true;
}
return false;
}
return false;
})()
Pattern C: Change/Incident Tables — Participant-Based
// ACL Condition Script for change_request READ
(function() {
if (gs.hasRole('itil')) {
return true; // Standard for ITSM — consider tightening
}
var userId = gs.getUserID();
return (current.getValue('requested_by') == userId ||
current.getValue('assigned_to') == userId ||
current.getValue('opened_by') == userId);
})()
Step 2b: Leverage New ACL Types (Xanadu / Yokohama+)
In direct response to the research by Varonis Threat Labs (CVE-2025-3648) and Kudelski Security, ServiceNow introduced new ACL mechanisms in Xanadu and Yokohama releases. These new controls are specifically designed to address the class of issues that empty-condition ACLs represent. When remediating on Xanadu+, deploy these controls alongside condition-based ACL scripts:
Query ACLs (query_range / query_match): A new ACL operation type that restricts which records appear in list queries and aggregation results. Unlike traditional record-level ACLs, Query ACLs inject filter conditions at the database query level — preventing the count-based data inference attacks documented by Varonis. After adding conditions to a table-level READ ACL, add a corresponding Query ACL to prevent range query exploitation.
Navigation: System Security > Access Control (ACL) > New
Type: Record
Operation: query_range (or query_match)
Name: [table_name]
Deny-Unless ACLs: A new evaluation mode where access is denied by default unless an explicit allow rule matches. This directly inverts ServiceNow's historical behavior where access defaults to granted when no ACL exists or when ACLs have empty conditions. Enable Deny-Unless for tables containing PII or sensitive data.
Security Data Filters: Instance-level filters that restrict data visibility independent of ACL evaluation. These provide defense-in-depth: even if an ACL is misconfigured with an empty condition, a Security Data Filter can enforce row-level restrictions.
Reference: ServiceNow's May 2025 platform-wide update applied default deny behavior for
query_rangeACLs across all instances. Verify that your instance received this update and that no customquery_rangeACLs override the default deny with empty conditions (ServiceNow Yokohama Patch Notes).
Step 3: Add Field-Level ACLs for PII Columns
Even with good table-level ACLs, add field-level ACLs for high-sensitivity columns:
Table: hr_case
- hr_case.description → sn_hr_core.case_writer, sn_hr_core.manager
- hr_case.work_notes → sn_hr_core.case_writer, sn_hr_core.manager
- hr_case.comments → sn_hr_core.case_writer, sn_hr_core.manager, opened_for
Table: sn_hr_core_profile
- sn_hr_core_profile.social_security_number → sn_hr_core.admin only
- sn_hr_core_profile.date_of_birth → sn_hr_core.admin, sn_hr_core.manager
- sn_hr_core_profile.bank_account → sn_hr_core.admin only
- sn_hr_core_profile.salary → sn_hr_core.admin, sn_hr_core.manager
Table: sys_user
- sys_user.home_phone → user_admin, self
- sys_user.mobile_phone → user_admin, self, caller_manager
- sys_user.home_address → user_admin, self
Step 4: Post-Remediation Verification
/*
* Post-remediation verification script
* Tests ACL effectiveness by impersonating different user types
*/
var tableName = 'hr_case';
var testUsers = [
{ name: 'test.itil.user', expected: 'limited', desc: 'Standard ITIL user' },
{ name: 'test.hr.manager', expected: 'full', desc: 'HR Manager' },
{ name: 'test.basic.user', expected: 'none', desc: 'Basic authenticated user' }
];
for (var t = 0; t < testUsers.length; t++) {
var testUser = testUsers[t];
var impUser = new GlideRecord('sys_user');
impUser.addQuery('user_name', testUser.name);
impUser.query();
if (impUser.next()) {
var impersonator = new GlideImpersonate();
impersonator.impersonate(impUser.getUniqueValue());
var gr = new GlideRecord(tableName);
gr.query();
var count = gr.getRowCount();
gs.info('User: ' + testUser.name + ' (' + testUser.desc + ')' +
' | Records visible: ' + count +
' | Expected: ' + testUser.expected);
impersonator.unimpersonate();
}
}
gs.info('');
gs.info('IMPORTANT: Also test via REST API with each user credential:');
gs.info('GET /api/now/table/' + tableName + '?sysparm_limit=1');
gs.info('Verify API returns same restricted results as UI');
Step 5: Document the Change
For each ACL modification, create a change record documenting:
- Which ACL was changed (sys_id, table, operation)
- What the condition was before (empty) and after (new condition/script)
- Justification (security audit finding Empty Condition ACLs on Sensitive Tables)
- Impact assessment (which users lost access, expected)
- Rollback procedure (revert to empty condition — conscious security downgrade)
- Testing performed (impersonation tests above)
Regulatory Impact
NIS2 Mapping
| Article | Requirement | How Empty ACLs Violate It | Evidence of Compliance After Fix |
|---|---|---|---|
| Art.21§2(a) | Risk analysis and information system security policies | No access control policy enforced on sensitive data; risk of unauthorized access not assessed or mitigated. Independent research by AppOmni (2022-2023) and Obsidian Security (2023) confirmed this misconfiguration affects 70%+ of enterprise instances — a systemic risk that should have been identified in any competent risk analysis | Documented ACL policy, condition-based access, audit trail of ACL reviews, automated detection via Empty Condition ACLs on Sensitive Tables scripts |
| Art.21§2(i) | Human resources security, access control policies, asset management | Excessive access to HR data; no least-privilege enforcement; no access review process. AppOmni found over 90% of tested organizations leaking data through this exact vulnerability class — demonstrating that industry-wide access control policies for ServiceNow are inadequate | Role-based conditions, ownership-based access, quarterly ACL review schedule, Query ACLs (Xanadu+) to prevent count-based inference (CVE-2025-3648) |
DORA Mapping
| Article | Requirement | How Empty ACLs Violate It | Evidence of Compliance After Fix |
|---|---|---|---|
| Art.9§1 | ICT risk management framework including access controls | Platform access controls not aligned with ICT risk framework; no access governance for financial entity data | Documented ACL baseline, automated monitoring, access review process |
| Art.9§4(c) | Mechanisms to detect anomalous activities | No ability to distinguish normal from abnormal access patterns when everyone has full READ. Varonis Threat Labs' research (CVE-2025-3648) demonstrated that even with anomaly detection, count-based data inference cannot be distinguished from normal list view activity when ACLs are permissive | Restricted baseline enables anomaly detection in access logs; Query ACLs prevent count-based inference |
ISO 27001:2022 Mapping
| Control | Requirement | How Empty ACLs Violate It | Evidence of Compliance After Fix |
|---|---|---|---|
| A.5.15 | Access control | Access not restricted based on business and information security requirements | Condition-based ACLs aligned with business role definitions |
| A.8.3 | Information access restriction | Access to information and application functions not restricted by access control policy | Table and field-level ACLs with documented conditions |
| A.8.4 | Access to source code | Source code and related items not appropriately restricted | Script include and business rule ACLs reviewed |
GDPR Mapping
| Article | Requirement | How Empty ACLs Violate It | Impact |
|---|---|---|---|
| Art.32§1(b) | Ability to ensure ongoing confidentiality of processing systems | PII accessible beyond authorized data processors. AppOmni's research confirmed that empty-condition ACLs expose PII tables to unauthenticated users via public portal widgets — this is not limited to internal users. The 70%+ exposure rate across enterprise instances constitutes a systemic failure of technical measures | Must notify DPO; potential Art.33 breach notification if access was exploited. Given publicly available research documenting this exact vulnerability class since 2022, failure to remediate may constitute gross negligence under Art.83§2(d) (degree of responsibility considering technical measures) |
| Art.5§1(f) | Integrity and confidentiality principle | Personal data not processed with appropriate security | Demonstrates inadequate technical measures |
| Art.25§1 | Data protection by design and default | System default provides maximum exposure rather than minimum necessary | Must implement privacy-by-default ACL configurations |
Independent Research References
The following publicly available security research directly documents exploitation of empty-condition ACLs — the exact vulnerability class described in this article. These are the strongest external evidence sources available for regulatory filings, audit responses, and executive risk reporting.
Primary Research (Direct Empty-Condition ACL Exploitation)
| # | Source | Title | Year | CVE | CVSS | URL |
|---|---|---|---|---|---|---|
| 1 | AppOmni (AO Labs) | Data Exposure and ServiceNow: The Elephant in the ITSM Room | 2022 | — | — | Link |
| 2 | AppOmni (AO Labs) | A Technical Analysis and Lessons From The Recent ServiceNow Misconfiguration Risks | 2023 | — | — | Link |
| 3 | AppOmni (AO Labs) | Handling SaaS Data Exposure Risks Due to Potential ServiceNow Misconfigurations | 2023 | — | — | Link |
| 4 | AppOmni (AO Labs) | Enterprise ServiceNow Knowledge Bases at Risk | 2023-2024 | — | — | Link |
| 5 | Obsidian Security | Are Your ServiceNow ACLs Publicly Exposing Data? | 2023 | — | — | Link |
| 6 | Arctic Wolf | Data Exposure Misconfiguration Issue in ServiceNow | 2023 | — | — | Link |
| 7 | Varonis Threat Labs | Count(er) Strike — Data Inference Vulnerability in ServiceNow | 2024-2025 | CVE-2025-3648 | 8.2 | Link |
| 8 | Kudelski Security | Unauthorized Data Exposure via Range Queries in ServiceNow ACLs | 2025 | CVE-2025-3648 | 8.2 | Link |
Supporting Research (Platform-Level Vulnerabilities That Compound Empty ACLs)
| # | Source | Title | Year | CVE | CVSS | URL |
|---|---|---|---|---|---|---|
| 9 | Assetnote | Chaining Three Bugs to Access All Your ServiceNow Data | 2024 | CVE-2024-4879, CVE-2024-5217, CVE-2024-5178 | 9.3 | Link |
| 10 | Resecurity | CVE-2024-4879 and CVE-2024-5217 — ServiceNow RCE Exploitation in a Global Reconnaissance Campaign | 2024 | CVE-2024-4879, CVE-2024-5217 | 9.3 | Link |
| 11 | CYFIRMA | ServiceNow RCE (CVE-2024-4879) Vulnerability Analysis and Exploitation | 2024 | CVE-2024-4879 | 9.3 | Link |
| 12 | AppOmni (AO Labs) | BodySnatcher (CVE-2025-12420): A Broken Authentication and Agentic Hijacking Vulnerability in ServiceNow | 2025 | CVE-2025-12420 | 9.3 | Link |
Vendor & Third-Party Hardening Guides
| # | Source | Title | URL |
|---|---|---|---|
| 13 | ServiceNow | Instance Security Best Practices Guide (PDF) | Link |
| 14 | ServiceNow | Instance Security Hardening Settings (Washington DC) | Link |
| 15 | ServiceNow | Access Control Hardening (Vancouver) | Link |
| 16 | ServiceNow DevProgram | Example Instance Scan Checks (GitHub) | Link |
| 17 | ServiceNow | CVE Security Advisories Landing Page | Link |
| 18 | HowToHarden | ServiceNow Hardening Guide | Link |
Media Coverage (For Executive Reporting)
| # | Source | Title | URL |
|---|---|---|---|
| 19 | Threatpost | Most ServiceNow Instances Misconfigured, Exposed | Link |
| 20 | CIO | Warning to ServiceNow admins: Fix your access control lists now | Link |
| 21 | BleepingComputer | Over 1,000 ServiceNow instances found leaking corporate KB data | Link |
| 22 | The Hacker News | ServiceNow Flaw CVE-2025-3648 Could Lead to Data Exposure via Misconfigured ACLs | Link |
| 23 | SC Media | ServiceNow issues CVE for high-severity ACL bug | Link |
| 24 | The Hacker News | ServiceNow Patches Critical AI Platform Flaw Allowing Unauthenticated User Impersonation | Link |
| 25 | CSO Online | ServiceNow BodySnatcher flaw highlights risks of rushed AI integrations | Link |
| 26 | Vinayak Agrawal (Medium) | ServiceNow Data Exposure: A Misconfiguration Story | Link |
Expert Notes
Practitioner annotations pending — article content has been technically validated.