> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/03.-integer-overflow-md.md).

# Integer Overflow in Binary Exploitation

> Numbers have limits. When you push past those limits, weird things happen — and weird things are exploitable. This guide explains integer overflow from absolute zero.

***

## 🤔 What Is an Integer Overflow?

In real life, numbers can be as big as you want. In computers, integers have a **fixed size** — they're stored in a limited number of bits. When you go past the maximum value that fits in those bits, the number **wraps around** back to the start.

**Real-world analogy:**

> Think of a car's odometer that only has 6 digits. After 999,999 km, the next km doesn't read 1,000,000 — it wraps back to **000,000**. The counter overflowed.

In code:

```c
unsigned char x = 255;
x = x + 1;
// x is now... 0. Not 256. Zero.
```

That's integer overflow. And it causes **wildly unexpected behavior** that attackers love to abuse.

***

## 📦 Integer Types and Their Limits

Every integer type has a fixed size in memory. Here are the most common ones:

### Unsigned Integers (no negative numbers, 0 to max)

| Type                 | Size              | Min | Max                            |
| -------------------- | ----------------- | --- | ------------------------------ |
| `unsigned char`      | 1 byte (8 bits)   | 0   | **255**                        |
| `unsigned short`     | 2 bytes (16 bits) | 0   | **65,535**                     |
| `unsigned int`       | 4 bytes (32 bits) | 0   | **4,294,967,295**              |
| `unsigned long long` | 8 bytes (64 bits) | 0   | **18,446,744,073,709,551,615** |

### Signed Integers (can be negative, split range in half)

| Type        | Size    | Min                            | Max                           |
| ----------- | ------- | ------------------------------ | ----------------------------- |
| `char`      | 1 byte  | **-128**                       | **127**                       |
| `short`     | 2 bytes | **-32,768**                    | **32,767**                    |
| `int`       | 4 bytes | **-2,147,483,648**             | **2,147,483,647**             |
| `long long` | 8 bytes | **-9,223,372,036,854,775,808** | **9,223,372,036,854,775,807** |

> 💡 **Rule of thumb:** An `n`-bit unsigned integer holds values from `0` to `2ⁿ - 1`. When you go one past the max, it wraps to 0.

***

## 🔄 How the Wrapping Works (The Math)

Computers store integers in binary. There are only so many bits. When those bits all flip to 1 and you add 1 more, they all flip back to 0.

### Unsigned Overflow (wraps to 0)

```
  1111 1111   (255 in 8-bit unsigned)
+ 0000 0001   (add 1)
-----------
1 0000 0000   (256... but we only have 8 bits!)
  0000 0000   ← the carry bit is LOST. Result: 0
```

```c
unsigned char a = 255;
a++;            // a is now 0

unsigned char b = 200;
b = b + 100;    // 200 + 100 = 300, but 300 % 256 = 44. b is now 44.
```

### Signed Overflow (wraps from max positive to max negative)

```c
int a = 2147483647;   // INT_MAX
a++;                  // a is now -2147483648  ← jumps to most negative!
```

This is because signed integers use **two's complement** representation. The most significant bit (the leftmost) is the sign bit — 0 means positive, 1 means negative. Flipping it by overflow makes the number go wildly negative.

### Underflow (going below 0 on unsigned)

```c
unsigned int x = 0;
x--;    // x is now 4294967295 (0xFFFFFFFF) — wraps to the maximum!
```

This is the mirror image — going below 0 on an unsigned integer wraps to the maximum value.

***

## 🔬 Why Does This Matter for Exploitation?

Integer overflows are dangerous because they let you **corrupt calculations** the program relies on — especially calculations that determine:

* **How much memory to allocate**
* **How many bytes to copy**
* **Whether you're allowed to do something** (authentication checks)
* **Array indices** (which memory location to access)

Let's look at each.

***

## 💥 Type 1: Allocation Size Overflow

This is the most classic exploitable pattern.

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    unsigned short count;     // 16-bit: max 65535
    unsigned short size;      // 16-bit: max 65535
    
    printf("How many items? ");
    scanf("%hu", &count);
    
    printf("How big is each item? ");
    scanf("%hu", &size);
    
    // Calculate total size for allocation
    unsigned short total = count * size;   // ← OVERFLOW HERE
    
    printf("Allocating %u bytes...\n", total);
    char *buf = malloc(total);
    
    // Now read that many bytes from user
    fread(buf, 1, total, stdin);    // ← if total is tiny, we overflow the heap!
    
    return 0;
}
```

### The Attack

Say `count = 1000` and `size = 100`. That *should* allocate `100,000` bytes. But:

```
1000 * 100 = 100,000
100,000 % 65,536 = 34,464   ← what a 16-bit unsigned actually stores
```

So `malloc(34464)` is called — a **much smaller** buffer. But then `fread` reads `34,464` bytes into it — which is still too much... wait.

Even worse scenario — `count = 256` and `size = 256`:

```
256 * 256 = 65536
65536 % 65536 = 0   ← total is ZERO
```

`malloc(0)` returns a tiny pointer (implementation defined — often 1 byte or a special pointer). Then the program tries to read **0 bytes** into it...

Or consider: `count = 65535` and `size = 2`:

```
65535 * 2 = 131070
131070 % 65536 = 65534   ← only 65534 bytes allocated
```

But now what if the actual data for 65535 items × 2 bytes overflows that buffer? **Heap overflow**. Adjacent heap chunks get corrupted. That's your exploit primitive.

***

## 💥 Type 2: Authentication Bypass via Overflow

```c
#include <stdio.h>

int is_admin(int user_level) {
    // Only level >= 100 is admin
    if (user_level >= 100) {
        return 1;
    }
    return 0;
}

int main() {
    int level;
    printf("Enter your level: ");
    scanf("%d", &level);
    
    // "Safety" check — make sure level isn't crazy
    unsigned char safe_level = (unsigned char) level;  // cast to 8-bit!
    
    if (is_admin(safe_level)) {
        puts("Welcome, admin!");
        // give admin access...
    } else {
        puts("Access denied.");
    }
    
    return 0;
}
```

### The Attack

You want to pass `is_admin()` with a value ≥ 100. But `safe_level` is a `unsigned char` — max 255.

What if you enter **256**?

```
(unsigned char) 256 = 0    ← wraps to 0. Not admin.
```

What about **355**?

```
(unsigned char) 355 = 355 - 256 = 99   ← still not ≥ 100.
```

What about **356**?

```
(unsigned char) 356 = 356 - 256 = 100  ← YES! Passes the check!
```

You entered 356 as a regular user and got admin access. The overflow made 356 look like 100.

***

## 💥 Type 3: Array Index Overflow / Out-of-Bounds

```c
#include <stdio.h>

int scores[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int main() {
    unsigned int index;
    printf("Which score do you want? (0-9): ");
    scanf("%u", &index);
    
    printf("Score: %d\n", scores[index]);   // ← no bounds check!
    return 0;
}
```

### The Attack

The array has indices 0–9. But there's no check that `index` is in that range.

If you enter `index = 20`, the program reads `scores[20]` — which is **outside the array**, reading arbitrary memory.

With a signed integer, you can even go **negative** (which becomes a huge positive number for the pointer math), reading memory *before* the array.

This is an **out-of-bounds read** — great for leaking memory (addresses, canaries, secrets).

Combine this with a **write** instead of a read, and you can corrupt arbitrary memory.

***

## 💥 Type 4: Signedness Confusion

This happens when the code mixes signed and unsigned types.

```c
#include <stdio.h>
#include <string.h>

void copy_data(char *dst, char *src, int len) {
    // Developer checks: don't copy too much!
    if (len > 64) {
        puts("Too much data!");
        return;
    }
    memcpy(dst, src, len);   // ← len is used as unsigned by memcpy!
}

int main() {
    char buf[64];
    int length;
    
    printf("Enter length: ");
    scanf("%d", &length);
    
    copy_data(buf, "AAAA...", length);
    return 0;
}
```

### The Attack

The check is `if (len > 64)`. `len` is a signed `int`.

What if you enter **-1**?

```
-1 > 64?   → FALSE (in signed comparison, -1 is less than 64)
```

The check passes! But `memcpy(dst, src, -1)` — `memcpy` takes a `size_t` (unsigned) for the length. `-1` as unsigned is:

```
-1 as unsigned int = 4294967295 (0xFFFFFFFF)
```

`memcpy` now tries to copy **4 gigabytes** of data. That's a massive heap/stack overflow from a negative number sneaking through a bounds check.

***

## 🧮 Visualizing the Wrap-Around

Think of integers like a clock face, but for a specific range:

```
Unsigned 8-bit (0 to 255):

         0
       /   \
    255     1
    |         |
    254     2
       \   /
        ...

Going past 255 → wraps to 0
Going below 0  → wraps to 255
```

```
Signed 8-bit (-128 to 127):

         0
       /   \
    127    -128   ← these are ADJACENT in memory!
    |         |
    126    -127
       \   /
        ...

Going past 127  → wraps to -128
Going below -128 → wraps to 127
```

***

## 🔍 Finding Integer Overflows

### In Source Code

Look for:

```bash
# Multiplications used for sizes
grep -n "\* " *.c | grep -i "size\|len\|count\|num"

# Casts that truncate
grep -n "(char)\|(short)\|(uint8\|uint16" *.c

# Mixed signed/unsigned comparisons
grep -n "if.*len\|if.*size\|if.*count" *.c
```

**Red flags to look for:**

* User-controlled value used in a multiplication → size calculation → allocation
* User-controlled value cast to a smaller type
* Signed variable compared with `>` then passed to a function expecting unsigned
* Addition/subtraction on user input without bounds checking

### In Binaries (No Source)

```bash
# Look for multiplication instructions near malloc/memcpy calls
objdump -d ./binary | grep -B10 -A10 "call.*malloc\|call.*memcpy"

# Open in Ghidra — look for imul/mul instructions near allocation calls
```

### Dynamic Testing

Try sending:

* `0` — what happens with zero?
* `255`, `256`, `257` — crossing 8-bit boundary
* `65535`, `65536` — crossing 16-bit boundary
* `2147483647`, `2147483648` — crossing signed 32-bit boundary
* `-1` — underflow / signedness confusion
* Very large numbers

***

## 🐍 Exploiting with pwntools

### Example: Trigger allocation overflow to get heap overflow

```python
from pwn import *
import struct

p = process("./vuln")

# We want count * size to overflow to something small
# 65536 items * 1 byte each = 65536 bytes on a 32-bit int
# but if size field is 16-bit: 65536 % 65536 = 0!

# Or: 256 * 256 = 65536 → 0 on 16-bit
count = 256
size = 256

p.sendlineafter(b"items? ", str(count).encode())
p.sendlineafter(b"each item? ", str(size).encode())

# Now malloc got called with 0 (or very small value)
# We can send a big payload to overflow the heap buffer
payload = b"A" * 1000    # way more than what was allocated
p.send(payload)

p.interactive()
```

### Example: Authentication bypass

```python
from pwn import *

p = process("./auth_vuln")

# unsigned char cast: we need value that becomes >= 100 after truncation
# 356 % 256 = 100 → passes check
magic_level = 100 + 256   # = 356

p.sendlineafter(b"level: ", str(magic_level).encode())
p.recvline()   # should say "Welcome, admin!"
p.interactive()
```

***

## 🔐 How Integer Overflows Are Prevented

### 1. Use the Right Types

```c
// BAD: 16-bit multiplication that overflows
unsigned short total = count * size;

// GOOD: use size_t (pointer-sized, usually 64-bit)
size_t total = (size_t)count * (size_t)size;
```

### 2. Check Before You Calculate

```c
// Check for overflow BEFORE multiplying
if (count > 0 && size > SIZE_MAX / count) {
    puts("Overflow detected!");
    exit(1);
}
size_t total = count * size;
```

### 3. Use Safe Integer Libraries

```c
// GCC built-ins for overflow detection
size_t total;
if (__builtin_mul_overflow(count, size, &total)) {
    puts("Overflow!");
    exit(1);
}
```

### 4. Avoid Signed/Unsigned Mixing

```c
// BAD: len is signed, memcpy expects unsigned
void copy(char *dst, char *src, int len) {
    memcpy(dst, src, len);   // -1 becomes huge unsigned!
}

// GOOD: use the right type throughout
void copy(char *dst, char *src, size_t len) {
    if (len > MAX_LEN) return;
    memcpy(dst, src, len);
}
```

### 5. Compiler Sanitizers (for finding bugs)

```bash
# Compile with UndefinedBehaviorSanitizer
gcc -fsanitize=undefined,signed-integer-overflow -o vuln vuln.c

# Run it — it will print a warning and crash on overflow instead of silently wrapping
./vuln
# runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
```

***

## 🗺️ Integer Overflow Taxonomy

```
Integer Overflow / Underflow
├── Overflow (exceed maximum)
│   ├── Unsigned: wraps to 0
│   └── Signed: wraps to most negative value
│
├── Underflow (go below minimum)
│   ├── Unsigned: wraps to maximum (0 - 1 = 4294967295)
│   └── Signed: wraps to most positive value
│
├── Truncation (cast to smaller type)
│   ├── 0x1FF stored in uint8_t → 0xFF (top bits dropped)
│   └── Used to bypass bounds checks
│
└── Signedness Confusion
    ├── Negative signed passes a positive check
    └── Passed to function expecting unsigned → huge value
```

***

## 🚩 CTF Challenge Patterns to Recognize

### Pattern 1: "How many items do you want?"

```
→ Try: count that causes count * item_size to overflow
→ Goal: trigger heap overflow via undersized allocation
```

### Pattern 2: "Enter your privilege level"

```
→ Try: value + 256 (or multiples) to bypass truncation check
→ Goal: get a value that looks like admin after casting
```

### Pattern 3: "Enter the index"

```
→ Try: negative numbers, numbers > array size
→ Goal: out-of-bounds read/write to leak or corrupt memory
```

### Pattern 4: "Enter the length"

```
→ Try: -1 or other negative numbers
→ Goal: negative bypasses signed check, huge value used by memcpy
```

***

## 📝 Quick Reference Cheat Sheet

```
── OVERFLOW VALUES TO TRY ──────────────────────────
128          → signed char overflow (127 + 1)
255, 256     → unsigned char boundary
32768        → signed short overflow
65535, 65536 → unsigned short boundary
2147483648   → signed int overflow
4294967295   → unsigned int max
-1           → underflow (becomes max unsigned)
-128         → signed char min

── MULTIPLICATION TARGETS ──────────────────────────
256 * 256  = 65536  → 0 on uint16
256 * 257  = 65792  → 256 on uint16
65536 * 2  = 131072 → 0 on uint32... no, 131072
Try: n * m where n * m % TYPE_MAX == small_number

── PYTHON HELPERS ──────────────────────────────────
# Check what a value becomes after truncation
value = 356
result = value % 256      # simulates unsigned char cast
print(result)             # 100

# Simulate 16-bit overflow
value = 65536 + 100
result = value % 65536    # 100
print(result)

── PWNTOOLS SNIPPET ────────────────────────────────
from pwn import *
p = process("./vuln")
p.sendlineafter(b"Enter: ", str(2**16).encode())   # 65536
p.interactive()
```

***

## 📚 Practice Resources

| Resource                                       | What to Look For                                  |
| ---------------------------------------------- | ------------------------------------------------- |
| [pwn.college](https://pwn.college)             | "Dealing with Memory" modules                     |
| [picoCTF](https://picoctf.org)                 | Search challenges tagged "binary" or "overflow"   |
| [CTFlearn](https://ctflearn.com)               | Integer overflow / binary challenges              |
| [exploit.education](https://exploit.education) | Phoenix series — great integer overflow examples  |
| OWASP Integer Overflow page                    | Defense-oriented but teaches you what to look for |

***

> 💡 **The Core Insight:** Integers in C don't protect you from going out of range. The number silently wraps, and the program keeps going — now with a completely wrong value. Your job as an attacker is to find where that wrong value causes the program to do something useful for you: allocate too little memory, skip a security check, or access the wrong address.

> 🔁 **The mental model:** Always ask — *"what happens if I give this program a value of 0, -1, or the max for its type?"* Those boundary cases are where integer overflows hide.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/03.-integer-overflow-md.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
