← All security articles
CRITICALWashington+

Empty Condition ACLs on Sensitive Tables

Domain 1: Access Control & Identity Security·

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:

  1. Table-level ACL (e.g., hr_case) — checked first
  2. Field-level ACL (e.g., hr_case.salary) — checked if table-level passes
  3. If no ACL exists for a table, the glide.sm.default_mode property determines the outcome: in standard mode (default), access is granted only to security_admin; in high security mode (hs), access is denied to everyone (→ High Security Plugin (HSP) and Security Hardening Settings)
  4. If multiple ACLs match, the system evaluates them with AND logic — all must pass
  5. 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:

  1. Reconnaissance: Attacker (or curious employee) with itil role discovers that hr_case is accessible. In most enterprises, itil is granted to hundreds or thousands of users (help desk agents, IT staff, developers in sub-production).

  2. 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.

  3. 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=0
    

    Without rate limiting (see Table API Unrestricted Exposure), the entire table can be extracted in a single paginated session.

  4. 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
  5. 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:

  1. Public portal discovery: Attacker identifies the organization's Service Portal URL (typically https://<instance>.service-now.com/sp). No authentication required.

  2. 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.

  3. 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 guest user.

  4. Bulk extraction: The attacker paginates through all records in the table. No authentication token, no session cookie, no API key required.

  5. 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:

  1. Attacker extracts full CMDB via Table API: server names, IP addresses, OS versions, patch levels, application stack details
  2. Maps internal network topology without any network scanning (bypasses IDS/IPS)
  3. Identifies high-value targets: domain controllers, database servers, certificate authorities
  4. Identifies unpatched systems or EOL operating systems
  5. 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:

  1. Extract all change records via Table API
  2. Search description and work_notes fields for patterns: passwords, connection strings, API keys, certificates
  3. Developers and ops teams routinely paste credentials in change request fields during implementations
  4. 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:

  1. Public KB discovery: Organization's Service Portal includes a Knowledge Base widget. Unauthenticated users can search and browse published articles by design.

  2. 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.

  3. Search-based extraction: Attacker searches for high-value keywords: "password", "connection string", "admin", "root", "API key", "credentials", "architecture", "network diagram".

  4. 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). Add sc_cat_item, sc_category, alm_asset, and alm_hardware to 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_range ACLs across all instances. Verify that your instance received this update and that no custom query_range ACLs 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:

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.