Integer Overflow & Off-by-One
CWE-190 difficulty: orta
Integer overflow, bir tamsayının tipinin sınırını aşınca “sarması”dır (wraparound). Tek başına bellek bozmaz; ancak bir boyut/uzunluk hesabını etkilediğinde, sınır kontrolünü atlatarak buffer overflow veya küçük ayırma + büyük kopyalamaya köprü olur. Off-by-one ise çoğu zaman komşu header/RBP için yeterli tek baytlık bozulmadır.
Klasik desenler
Section titled “Klasik desenler”// 1) Boyut hesabında taşma → küçük ayırma, büyük kopyalamaunsigned short len = get_len(); // 65535'i aşınca sararchar *buf = malloc(len);memcpy(buf, src, real_len); // heap overflow
// 2) İşaretli/işaretsiz karışımı → negatif "boyut" büyük unsigned olurint n = read_int();if (n < MAX) memcpy(dst, src, n); // n negatifse kontrol atlanır, n->size_t dev olur
// 3) Off-by-one: <= yerine < / null byte taşmasıchar buf[64];for (i = 0; i <= 64; i++) buf[i] = inp[i]; // 1 bayt taşma
// 4) Toplama/çarpma taşmasısize_t total = num * size; // num*size wrap → küçük mallocmalloc(total);for (i = 0; i < num; i++) read(fd, p + i*size, size);Sayısal sınırlar (hızlı referans)
Section titled “Sayısal sınırlar (hızlı referans)”| Tip | Max (işaretsiz) | Not |
|---|---|---|
uint8_t | 255 | +1 → 0 |
uint16_t | 65535 | sık CTF |
uint32_t | 4294967295 | malloc arg |
int32_t | 2147483647 | negatif bypass |
# Wrap hesaplamaimport ctypesctypes.c_uint16(65535 + 5).value # 4ctypes.c_uint32(0x100000000 + 8).value # 8Sömürü mantığı
Section titled “Sömürü mantığı”- Boyut kontrolünü sarma ile atlat → aşırı kopyala- Off-by-one ile bitişik meta veriyi (saved rbp / chunk size) 1 bayt değiştir ("null byte poisoning" heap'te güçlüdür)- Negatif indeks/uzunluk ile sınır dışı okuma/yazma- num*size wrap → overlapping allocations / OOBZafiyet, taşan değerin sonradan bir kopyalama/ayırma/indeksleme kararında kullanılıp kullanılmadığına bağlıdır; tek başına bir overflow değil, ona giden bir adımdır.
Off-by-one → stack / heap
Section titled “Off-by-one → stack / heap”Stack off-by-one: saved RBP düşük baytı bozulur → leave; ret sonrası rsp kayar → kısmi stack pivot / ret adresine kayma
Heap null byte poison (kavramsal): sonraki chunk size: 0x101 → 0x100 (P biti etkilenir) → prev_size sahte → unlink / consolidate → arbitrary write (glibc sürümüne göre ek korumalar var)CTF metodolojisi
Section titled “CTF metodolojisi”-
Boyut yolunu bul — malloc(n) , read(0,buf,n) , memcpy(…, n) ve n ’nin nasıl hesaplandığını Ghidra’da izle (çarpma/toplama/cast).
-
Tip genişliğini not et — short / int / size_t karışımı ve
if (n < MAX)işaretli mi? -
Wrap değeri seç — kontrolü geçen ama kopyalamada büyük olan n üret; local’de ASan/GDB ile OOB’u doğrula.
-
Primitive’e çevir — oluşan overflow/UAF ile stack veya heap zincirine bağla (leak → ROP).
-
Stabilize — sınır değerler ( 0 , MAX , MAX+1 , negatif) için tablo tut; remote ile aynı genişlikte integer kullandığından emin ol.
pwntools ile sınır testi
Section titled “pwntools ile sınır testi”from pwn import *
def try_len(n): io = process('./chall') io.sendlineafter(b'len: ', str(n).encode()) io.send(b'A' * min(n if n > 0 else 0, 0x2000)) # crash / diff davranış io.close()
for n in (0, 1, 0xffff, 0x10000, 0xffffffff, -1): log.info(f'trying {n}') try_len(n)pwndbg> break mallocpwndbg> commandssilentprintf "malloc size = %p\n", $rdicontinueend