1209 Words

Executive Summary

This paper breaks down a casual telemetry subversion experiment on a production web implementation of a Shulte Grid challenge (toolonline.net). By exploiting global reactive footprint exposures, bypassing runtime-inserted anti-debugging traps, mapping out AST-level parameter dependencies, and analyzing strongly typed .NET CLR parsing behaviors, we successfully committed an absolute optimal time parameter of 1 ms ($10^{-3}$ seconds) to the remote persistent storage, achieving absolute dominance on the global leaderboard.

1. Neutralizing the V8 Execution Trap (Anti-Debugging Bypass)

The target asset employs a high-frequency execution barrier inside a dedicated runtime asset (SlideCaptcha.js). It leverages an anonymous IIFE triggering an explicit debugger statement via a non-persistent virtual machine allocation (VMxxxx). When Chrome DevTools is open, the V8 engine is forced into a persistent Paused state, throttling console input and script inspection.

(function anonymous() {
    debugger
})

Root Cause Analysis & Mitigation

Rather than modifying the script source or attempting script injection primitives, the trap was mitigated at the Chromium developer tools architecture layer. By registering a Conditional Breakpoint configured with a negation state (false) or utilizing the DevTools contextual menu “Never pause here” on the statement block, the V8 engine’s debugging agent was instructed to bypass the interrupt instruction vector.

2. Global Scope Footprint Leakage & AST Subversion

The asset’s front-end compilation pipeline utilizes an obfuscator framework featuring control flow flattening, string array rotations, and hexadecimal member encoding. However, the production deployment exposed a critical architectural anti-pattern: the reactive Vue 3 core application instance was bound to a globally mutable identifier (var vueObj).

By auditing the de-obfuscated properties of vueObj, the application state was exposed:

Property Description
vueObj.currentNumber An integer monitoring current game tracking progress
vueObj.numberArr A randomized 25-element matrix representing target validation cells

Bypassing the Sequential Match Engine

A naïve mutation of vueObj.currentNumber = 24 fails upon interaction with the DOM. The underlying execution handler clickNumberBlock(index) runs a strict parity check against the memory array allocation:

if (this['numberArr'][index] == this['currentNumber']) { ... }

If a user clicks visual block 25 while the state engine expects 24, the conditional branch falls through to the error penalty routine (this['errorCount']++), aborting state finalization.

Exploiting the Vulnerability

To resolve this, we executed an atomic multi-variable write inside the runtime engine to realign the state tree and the array expectation simultaneously:

vueObj.currentNumber = 25;
vueObj.numberArr = Array(25).fill(25);

By populating the entire array buffer with 25, any subsequent pointer event dispatched to the grid container yields an absolute truth valuation (25 == 25), forcing immediate execution of the game-termination routine.

3. Cryptographic Signature Tampering & CLR Deserialization Faults

Upon final index confirmation, the front-end triggers a POST network transaction to the target gateway endpoint:

https://toolonline.net/api/NlxlRankingList/Add

The transport protocol is hardened using a defensive cryptographic layout inside the request headers:

POST /api/NlxlRankingList/Add HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1...
Nonce: 23975157607381
Timestamp: 1781252397515
Sign: 82f70d9cddc14a27751ec74dc3bade64

The server-side gateway validates request integrity using a multi-stage defensive chain:

  1. Replay Protection: The Nonce is checked against a distributed cache (e.g., Redis) for idempotency, while the Timestamp enforces a $\pm 5\text{s}$ drift window.
  2. Cryptographic Commitment: The Sign header is a one-way cryptographic hash matching:

$$\text{Sign} = \text{MD5}(\text{time} \parallel \text{timestamp} \parallel \text{nonce} \parallel \text{ServerSalt})$$

If an attacker captures the payload via a standard network proxy and mutates the JSON body parameter ("time": 21070"time": 500), the remote gateway computes a signature mismatch and throws a 402 / 403 Forbidden response. Furthermore, a hardcoded front-end conditional guard (_0x480edb > 0x5dc) implicitly drops any network dispatch where the local delta calculation is under 1500 ms.

Intercepting the Execution Pipeline

To defeat both protections, we must execute the mutation upstream of the cryptographic hashing operation but downstream of the local calculation block. By utilizing JavaScript’s first-class function characteristics, we injected a proxy interceptor hook directly into the globally accessible gateway driver module (c.game.addRankingList):

const rawAddRanking = c.game.addRankingList;

c.game.addRankingList = function(payload) {
    // Injecting floating-point telemetry mutation
    payload.time = 0.1;
    rawAddRanking(payload);
};

Probing the CLR Serialization System

The remote endpoint rejected this transaction with a highly structured HTTP 400 Bad Request schema payload compliant with RFC 9110 specifications:

{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "$.time": [
      "The JSON value could not be converted to System.Nullable`1[System.Int32]. Path: $.time"
    ]
  }
}

This stack trace leaked critical structural dependencies of the remote application layer:

Finding Detail
Runtime Target The framework is built on ASP.NET Core / .NET Core CLR
Type Constraint The binding model maps the incoming JSON key time to a strictly typed, 32-bit signed nullable integer primitive (System.Nullable<System.Int32> or int?)
Parser Fault Passing a floating-point IEEE 754 double precision value (0.1) triggers a systemic deserialization break inside the native System.Text.Json namespace before the request ever reaches the internal controller business logic

4. Exploit Finalization: The 1 ms Boundary Attack

Knowing that the backend endpoint parses data using integer boundaries, the numerical unit of measurement is confirmed to be milliseconds (ms).

To execute a maximum-velocity breach, we must supply the absolute minimum positive integer value allowed by the type definition, which is 1. The value 0 was determined to be filtered by a boundary rule on the C# controller layer, but 1 falls within the logical acceptance envelope.

We revised our active interceptor patch in the running environment:

c.game.addRankingList = function(payload) {
    payload.time = 1; // Commit structural minimum integer (1ms)
    rawAddRanking(payload);
};

The Transaction Flow Lifecycle

[DOM Click Block 25]
[Compute Local Delta] ──> (Bypassed via Matrix Injection)
[c.game.addRankingList Hook] ──> (Forced payload.time = 1)
[Native Crypto Signer] ──> Computes MD5(1 + CurrentTimestamp + NewNonce + Salt)
[HTTP POST Transport] ──> Dispatched with mathematically synchronized 'Sign' header
[.NET CLR Controller] ──> Validation Pass: Sign matches, Time type-checks as Int32(1)
[Database Write] ──> Persistent state altered successfully.

By routing the execution flow through this sequence, the native client-side signature component wrapped our forged 1 ms metric with a mathematically perfect Sign hash, alongside an unexpired Timestamp and an unused Nonce.

The transaction returned an unmitigated validation victory from the server:

{ "code": 1, "message": "成功" }

The database transaction committed seamlessly. The public global leaderboard updated immediately, anchoring LearyLiang at Rank 1 with an unassailable record velocity of 0.001 seconds.

5. Architectural Remediation

To elevate this asset to a zero-trust model, the client application must be completely stripped of its authoritative timekeeping privileges.

  1. Cryptographic Inversion: The execution state duration must be derived implicitly on the server. The client dispatches an initial initialization payload (POST /api/game/start), causing the backend to cache a highly precise timestamp inside a non-persistent session store (e.g., Redis).

  2. Telemetry Sequence Verification: Upon puzzle completion, the client transmits an encrypted tracking array denoting the precise execution coordinates and individual stroke offsets (List<int> clickIntervals).

  3. Algorithmic Boundary Auditing: The server evaluates completion time via total backend delta calculation ($T_{\text{end}} - T_{\text{start}}$) and parses the stroke interval matrix. If the chronological distance between elements systematically undercuts human neuromuscular propagation delays ($\le 75\text{ms}$ processing windows), the request violates behavioral invariants and must be dropped instantly.