daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

servicenow.md (6547B)


      1 ---
      2 title: "ServiceNow"
      3 section: "Network Services"
      4 sectionSlug: "network-services-pentesting"
      5 sourcePath: "src/network-services-pentesting/pentesting-web/servicenow.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/servicenow.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # ServiceNow
     14 
     15 ## Quick notes
     16 
     17 Useful paths during recon:
     18 
     19 - `/login.do`
     20 - `/api/now/sp/widget/<widget_id>`
     21 - `/api/now/table/<table_name>`
     22 - `/stats.do`
     23 
     24 The important ServiceNow mistake is often **not a single CVE**, but **public or weak ACLs** reached through a widget or API. Test **widgets** and the **Table REST API** as distinct request paths, while remembering that both ultimately depend on the effective roles plus applicable table, field, and endpoint controls.<sup>[[1]](#references)[[3]](#references)[[5]](#references)</sup>
     25 
     26 ## Bootstrap an unauthenticated session
     27 
     28 ServiceNow commonly issues a usable **anonymous session** before authentication. Request `/login.do`, keep the cookies (for example `JSESSIONID`), and extract the `g_ck` value used as `X-UserToken`. Reuse that anonymous session for every probe.<sup>[[1]](#references)</sup>
     29 
     30 If `/login.do` immediately redirects to SSO/IdP, anonymous bootstrapping may fail, but still inspect the redirect flow (`oauth_redirect.do`) in Burp because some deployments still leak a usable cookie/token pair there.
     31 
     32 ## Treat Service Portal widgets as backend APIs
     33 
     34 A public widget is also a **JSON API**. Service Portal widgets can contain both client-side and server-side logic, so call the exposed endpoint directly instead of assessing only the portal UI.<sup>[[6]](#references)</sup>
     35 
     36 ```http
     37 POST /api/now/sp/widget/WIDGET_ID HTTP/1.1
     38 Host: target.service-now.com
     39 X-UserToken: <public_g_ck>
     40 Cookie: JSESSIONID=<public_session>
     41 Content-Type: application/json
     42 
     43 {"payload":{"start":0,"end":1}}
     44 ```
     45 
     46 Interesting built-in widgets to probe:
     47 
     48 - `ticket-attachments`
     49 - `kb-article-page`
     50 - `kb-search`
     51 - `kb-category-list`
     52 - `sc-category`
     53 - `widget-simple-list`
     54 
     55 Don't stop at defaults. Mature instances often contain **custom widgets** with organization-specific ACL assumptions, so enumerate installed widget IDs and test them with the same anonymous session.
     56 
     57 ## Abuse `widget-simple-list` as a table oracle
     58 
     59 If `widget-simple-list` is public, it can become a **generic table-query primitive**. The attacker supplies a table (`t`) and optionally a display field (`f`), while the widget queries backend records with the permissions of the anonymous session.<sup>[[1]](#references)[[3]](#references)</sup>
     60 
     61 ```http
     62 POST /api/now/sp/widget/widget-simple-list?t=incident&f=number HTTP/1.1
     63 Host: target.service-now.com
     64 X-UserToken: <public_g_ck>
     65 Cookie: JSESSIONID=<public_session>
     66 Content-Type: application/json
     67 ```
     68 
     69 Use curated and environment-specific table/field pairs such as:
     70 
     71 - `sys_user.email`
     72 - `incident.number`
     73 - `kb_knowledge.short_description`
     74 - `cmn_department.name`
     75 - `oauth_entity.name`
     76 
     77 If `display_value` is `null` but the widget still reports a positive count, treat it as a signal that the **table is reachable** and the chosen field is the wrong one or field-level ACLs differ.
     78 
     79 ## Test the Table REST API separately
     80 
     81 Widget server scripts and the Table REST API are different access paths and can produce different outcomes. A target may block one widget but still expose rows through `/api/now/table/*`; ServiceNow documents that the Table API caller must have sufficient roles for the requested data.<sup>[[5]](#references)</sup>
     82 
     83 ```bash
     84 curl -s 'https://target.service-now.com/api/now/table/sys_user?sysparm_limit=1' \
     85   -H 'X-UserToken: <public_g_ck>' \
     86   -H 'Cookie: JSESSIONID=<public_session>'
     87 ```
     88 
     89 Prioritize high-value tables such as `sys_user`, `incident`, `kb_knowledge`, `sc_cat_item`, `cmn_department`, `cmdb_ci`, and `oauth_entity`.
     90 
     91 ## Count-only responses are blind inference oracles
     92 
     93 If ServiceNow refuses to return rows but still reveals **how many records matched** attacker-controlled filters, you have a **count oracle**, not a clean denial. Vary `filterText`, prefixes, or boolean predicates and compare counts to infer protected data incrementally.<sup>[[4]](#references)</sup>
     94 
     95 Treat this as **blind data inference**, not direct row disclosure. Report the oracle separately from full record exposure.
     96 
     97 ## `snowpick`
     98 
     99 [`snowpick`](https://github.com/BishopFox/snowpick) automates the anonymous-session bootstrap, widget discovery, `widget-simple-list` table enumeration, and optional Table REST API probing.<sup>[[1]](#references)[[2]](#references)</sup>
    100 
    101 ```bash
    102 go install github.com/BishopFox/snowpick@latest
    103 snowpick -target target.service-now.com
    104 snowpick -target target.service-now.com -table-api
    105 snowpick -target target.service-now.com -discover -discover-limit 200
    106 snowpick -targets hosts.txt -concurrency 5 -proxy http://127.0.0.1:8080 -rate 200ms
    107 ```
    108 
    109 Useful behaviors:
    110 
    111 - Distinguishes **`exposed`** rows from **`count_oracle`** findings
    112 - Preserves **bounded evidence** (`record_count`, small samples, reproducible request details)
    113 - Flags public `/stats.do` access
    114 - Supports JSON output for triage and replay
    115 
    116 ## Detection / validation notes
    117 
    118 From a defender or purple-team perspective, review logs for:<sup>[[1]](#references)</sup>
    119 
    120 - Anonymous requests to `/api/now/sp/widget/*`
    121 - Anonymous requests to `/api/now/table/*`
    122 - Systematic variation of `t`, `f`, table names, field names, or `filterText`
    123 - Public access to `/stats.do`
    124 
    125 When validating impact, prefer **bounded evidence**: keep the total count, a minimal sample, and a reproducible request instead of bulk-exporting every accessible row.
    126 
    127 ## References
    128 
    129 - [1] [Bishop Fox - Introducing snowpick: Testing ServiceNow for Public Data Exposure](https://bishopfox.com/blog/introducing-snowpick-testing-servicenow-for-public-data-exposure)
    130 - [2] [BishopFox/snowpick](https://github.com/BishopFox/snowpick)
    131 - [3] [Data Exposure and ServiceNow: The Elephant in the ITSM Room](https://www.enumerated.ie/servicenow-data-exposure)
    132 - [4] [Varonis - Count(er) Strike: Data Inference Vulnerability in ServiceNow](https://www.varonis.com/blog/counter-strike-servicenow)
    133 - [5] [ServiceNow - Table API reference](https://www.servicenow.com/docs/r/api-reference/rest-apis/c_TableAPI.html)
    134 - [6] [ServiceNow - Service Portal widget API reference](https://www.servicenow.com/docs/r/platform-user-interface/service-portal/widget-api-reference.html)