Jul 22, 2026BTA Team9 min read

How CVE-2026-6875 Turns /assessment_thanks.do Into Pre-Auth RCE: A ServiceNow Sandbox Escape Built on One Shared JavaScript Global

We stopped what we were doing at BlueTeamAutomation when Searchlight Cyber's write-up landed. The ServiceNow bug they described did not lean on a memory-corruption stunt, an obscure deserializer, or a novel encoder. It leaned on one shared JavaScript global. CVE-2026-6875 lets an unauthenticated HTTP POST reach a script sink at /assessment_thanks.do, and from inside the outer sandbox an attacker rewrites a single method on Object.prototype that gets honored inside the less restricted context ServiceNow uses to load Script Includes. That second context ends up compiling and running whatever string the attacker parked, which yields Rhino engine execution equivalent to platform administrator. Roughly 85% of the Fortune 500 runs ServiceNow, and by Friday afternoon researchers at Defused were watching a second, independent gadget chain reach the same primitive.

Searchlight reported the bug in early April 2026. ServiceNow rolled hardening to hosted instances within a day and shipped patches for self-hosted customers on July 13 under KB3137947. Public exploitation started July 18 and continued through the weekend, five days after the patch.

The pre-auth surface at /assessment_thanks.do

ServiceNow's assessments module renders a thank-you page after an anonymous survey submission. The dispatcher at /assessment_thanks.do reads the sysparm_assessable_type query parameter, resolves it into a GlideRecord query, and evaluates a snippet of platform script associated with the assessment record. No login is required to hit the endpoint because the whole point of an assessment link is that non-employees can complete it. Searchlight reproduced the sink end to end in their disclosure, showing that a crafted sysparm_assessable_type value walks straight into the platform's server-side script evaluator with no authentication check in front of it.

The evaluator is not JavaScript running on the actual JVM. It is a sandboxed subset that runs on Rhino, ServiceNow's embedded JavaScript engine, wrapped by a two-context filter that is supposed to blunt the most obvious server-side abuses.

Two sandbox contexts, one shared global scope

The outer sandbox is where attacker-supplied code executes. It blocks the primitives you would reach for first: no eval, no new Function(), no top-level function declarations. Rhino enforces those blocks at parse time, so a payload that tries any of them dies before it runs.

The inner context is where privileged core code runs. Script Includes, business rules, and the ServiceNow API bindings all resolve there. Sandboxed code reaches that context through helpers like gs.include('ItemViewElementsProvider'), which loads and evaluates the named Script Include in the less restricted realm so its calls to Java classes and platform APIs work. The two contexts were separated on purpose so untrusted scripts could never call eval themselves while still letting trusted platform libraries do what they need to.

They share JavaScript globals. That is the whole bug.

The Object.clone override that turns gs.include into eval

When Script Includes load in the inner context, some of them clone objects using Object.clone. That method resolves against Object.prototype, which lives in the shared global scope both contexts read from. Attacker code running in the outer sandbox can assign a new value to Object.clone, a plain property write that Rhino sees as neither eval nor new Function(), and can also write attacker-controlled data into AbstractAjaxProcessor.prototype, another shared object. Both writes are legal inside the sandbox because they are just property assignments.

The pivot happens at gs.include('ItemViewElementsProvider'). That Script Include runs in the inner context, and somewhere in its normal execution path it invokes Object.clone(something). Only Object.clone no longer points at the safe cloner. It now points at the JavaScript Function constructor the attacker aliased in from a global fetch of this.Function. The invocation becomes Function(payload), where payload is the attacker string previously parked on AbstractAjaxProcessor.prototype. That call returns a callable function object compiled inside the inner context (unsandboxed) which the loader then invokes. At that moment the attacker's arbitrary JavaScript executes with Rhino engine access equivalent to platform administrator, and the sandbox is out of the picture, as Cyberpress traced in its analysis of the gadget chain.

ServiceNow's response to this class was a hardening feature called Guarded Script, which restricts sandbox scripts to single simple expressions and blocks variable declarations, control flow, and multi-statement payloads. That removes the ability to stage the writes the exploit needs, and it is on by default in the patched family.

Why almost nothing at your edge sees this land

Every part of the request looks legitimate on the wire. It is a POST to a public ServiceNow endpoint that real workflows use. There are no shell metacharacters in the URL, no obvious traversal, no easy signature keyword. The script payload rides in a parameter (sysparm_assessable_type) that carries structured platform identifiers on any healthy day, so a WAF rule keyed on "SQL keywords in a query string" or "PowerShell in a POST body" has nothing to bite. The exploit does not spawn a process on the host until after the escape completes, so an EDR agent watching only the platform user's shell activity sees clean traffic followed by a burst of Java-hosted script work that resembles a business rule firing. Because the Rhino engine runs inside the ServiceNow Java process, any command execution the attacker layers on top (against a MID Server, for example) originates from a signed platform binary your allowlist already trusts.

The MID Server is the piece that keeps most defenders up. MID Servers sit inside customer DMZs or internal networks and hold a persistent authenticated tunnel back to the ServiceNow instance so the platform can reach on-prem targets. Once the attacker owns the instance, that tunnel is now theirs, and any command they issue against a MID Server originates from the trusted platform side of the connection. Lateral movement, credential theft from platform-integrated systems, and pivots into the internal network all become one-hop operations from a source that looks legitimate to almost every downstream control.

What to hunt in ServiceNow logs, EDR, and MID Server

Patch first. CVE-2026-6875 is fixed in Zurich Patch 7b and Patch 9, Yokohama Patch 12 Hot Fix 1b and Patch 13, Australia Patch 2, and Brazil EA and GA. Hosted instances were auto-patched by July 14. For self-hosted deployments, confirm the actual on-disk version rather than trusting a dashboard reading, since a compromised instance can lie about its family in the UI. If you cannot patch immediately, block external network access to /assessment_thanks.do at the reverse proxy or WAF for unauthenticated sessions, and audit the assessments module for any anonymously reachable configurations you do not actively need.

For hunts, three queries pay for themselves. First, pull every request to /assessment_thanks.do from external IPs for the last 30 days, joined against the sysparm_assessable_type value, and inspect any where the parameter is unusually long or contains characters a real assessable type identifier never would (parentheses, quotes, semicolons, percent-encoded control bytes). Second, from sys_user_has_role and sys_user, list every account granted the admin role since July 13, 2026 and confirm each was created by a real admin change; Searchlight showed that admin creation is the first thing most attackers reach for after the escape. Third, on your MID Server hosts, alert on any child process of the MID Server JVM whose command line does not match the small set of known integrations you deploy, and correlate against outbound network connections initiated by that process to non-platform destinations.

Mapped to MITRE ATT&CK, this is T1190 Exploit Public-Facing Application for initial access, T1059 Command and Scripting Interpreter for the in-sandbox to unsandboxed transition, T1136 Create Account for the admin creation move, and T1090.001 Internal Proxy the moment lateral movement rides the MID Server tunnel. If a hunt returns positive, rotate every credential the platform can reach (integration accounts, API keys stored in sys_properties, MID Server credentials, and any downstream service the MID Server authenticates to), invalidate active sessions, and re-verify the on-disk platform version afterward, because a shell that landed before you patched can persist through the upgrade.

Once you have patched and cleaned, run the actual exploit path against the patched instance and watch your telemetry pipeline light up the way you designed it to. Replaying the real attack against your live stack and correlating the noise back through EDR and SIEM into SOAR is the loop BASzy and the rest of BlueTeamAutomation automate end to end, so a validated detection stops being a snapshot you took the day the CVE dropped.

Turn every advisory into a validated detection

BASzy replays real exploit traffic against your live stack, correlates the noise into your EDR and SIEM, and hands your SOC a signed record that the fix held. See how the whole loop runs.

Explore BASzy →