Module 3 –
🧠 Buffer Overflows – Memory Layout Basics
Before understanding buffer overflow, you need to know how memory is organized in a program.
A running program is mainly divided into 3 parts:
1️⃣ Program Code (Text Segment)
👉 This is where your actual program instructions are stored.
📌 Contains:
Compiled code (.exe instructions)
Functions
Library code (like printf, etc.)
💡 Key Point:
This part is usually read-only
It tells the CPU what to do
👉 Think of it as:
🧾 Recipe (instructions)
2️⃣ Program Data (Data + Heap)
👉 This is where variables are stored
📌 Includes:
Global variables (declared outside functions)
Static variables
Heap (dynamic memory)
🔹 Heap (Important)
👉 Used when you do:
malloc(), new
Memory is allocated at runtime
You control it manually
💡 Example:
int *ptr = malloc(10 * sizeof(int));
👉 Memory is taken from heap
👉 Think of it as:
📦 Storage area where you can request space anytime
3️⃣ Program Stack
👉 Used for function execution
📌 Contains:
Local variables
Function parameters
Return addresses
💡 Example:
void func() {
int x = 10; // stored in stack
🔹 How stack works:
Follows LIFO (Last In First Out)
Each function call creates a stack frame
👉 Think of it as:
📚 Stack of plates (last added → first removed)
💣 Now: Where Buffer Overflow Happens?
👉 Buffer overflow usually happens in:
Stack (stack overflow)
Heap (heap overflow)
🔥 Example:
char buffer[10];
gets(buffer); // no size check ❌
If user enters:
"This is a very long input"
👉 It will overflow buffer and overwrite:
Other variables
Return address
💥 Result:
Crash OR
Attacker can run malicious code
⚠️Why Understanding Memory is Important
Because attackers:
Target stack → overwrite return address
Target heap → corrupt memory
🔑 Final Quick Summary:
👉 Program Code → instructions
👉 Program Data (Heap) → global + dynamic memory
👉 Stack → function calls + local variables
👉 Buffer Overflow = writing more data than memory can hold → corrupts these regions
Heap
🧠 Step-by-step Simple Explanation
🔹 1. malloc() – Taking memory from heap
char *ptr = (char*) malloc(10);
👉 You ask for 10 bytes
👉 System gives you memory from heap
🔹 2. Hidden thing (IMPORTANT)
When memory is given, it is not just 10 bytes.
Internally it looks like:
[ HEADER | YOUR DATA ]
ptr
👉 HEADER (hidden) stores:
Size of memory
Whether it’s free/used
Info about nearby blocks
🔹 3. free() – Releasing memory
free(ptr);
👉 System:
Uses header info
Marks that block as free
So it can reuse it later
🔹 4. Why header is needed
👉 Because system must:
Know how much memory was given
Track which blocks are free or used
Manage heap efficiently
💥 Where Heap Overflow comes in
If you do:
char *ptr = malloc(10);
strcpy(ptr, "this is a very long string"); // ❌
👉 You write more than 10 bytes
👉 Result:
Data spills outside your block
Header or next block gets corrupted
💣 Why dangerous
System loses track of memory
Program may crash
Attacker can manipulate memory
🔑 One-line takeaway:
👉 Heap memory has hidden headers for management, and overflowing data can corrupt these
headers, causing serious issues.
🧠 Glibc Heap Structure (Simple Explanation)
When you use malloc() in C/C++, glibc (the standard C library) manages heap memory using blocks.
Each block has a hidden header (metadata) before your actual data.
📦 Structure of a Heap Block
[ HEADER | USER DATA ]
ptr
👉 You only see USER DATA
👉 But system uses HEADER to manage memory
🔍 What the Header Stores
1️⃣ Size of Current Block
👉 How big this memory block is
Needed when freeing memory
Helps system know how much space to manage
2️⃣ Size of Previous Block
👉 Size of the block just before this one
Helps in merging adjacent free blocks
Improves memory efficiency
3️⃣ Free or In Use
👉 Whether this block is:
✅ Allocated (in use)
❌ Free (available)
👉 Used by system to decide:
Where to give memory next
Which blocks can be reused
4️⃣ Additional Flags
👉 Extra info like:
Special conditions
Memory alignment
Status bits
💡 Why this is important
👉 All memory management depends on this header
If this gets corrupted (heap overflow):
System gets wrong info
Memory handling breaks
Can lead to crashes or exploits
💣 Simple Idea
👉 Think of each block like:
[Label: size=10, status=used | your data]
If label gets damaged ❌
👉 System gets confused
🔑 One-line takeaway:
👉 Glibc heap uses hidden headers (size, status, etc.) to manage memory, and corrupting them leads
to serious vulnerabilities.
🧠 Global & Static Data Overflows (Simple
Explanation)
🔹 1. What are Global & Static variables?
✅ Global variables
int count = 0; // global
✅ Static variables
static int x = 10;
👉 These:
Exist for the entire program lifetime
Stored in a separate memory area (data segment)
(not stack, not heap)
🔹 2. Where are they stored?
Memory layout:
[ Code ]
[ Global / Static Data ] ← THIS PART
[ Heap ]
[ Stack ]
👉 So they are permanent variables
🔹 3. What is Global/Static Overflow?
👉 If a buffer is stored in this region:
char buffer[10]; // global
and you do:
strcpy(buffer, "this is a very long string"); // ❌
👉 It will overflow and overwrite nearby global/static variables
💥 What gets affected?
👉 Nearby variables in memory, like:
char buffer[10];
int isAdmin = 0;
Overflow:
strcpy(buffer, "AAAAAAAAAAAA");
👉 Can overwrite:
isAdmin = 1; // 💣 attacker gains admin access
🔥 Why exploitation is harder here
👉 Unlike heap/stack:
No return addresses
No complex structures
👉 So attacker depends on:
What variables are nearby
How they are used
💣 When it becomes dangerous
👉 VERY dangerous if attacker overwrites:
Pointer variables
Flags (like isAdmin)
Function pointers
Example (Pointer corruption)
char buffer[10];
int *ptr;
strcpy(buffer, "AAAAAAAABBBB"); // overflow
// ptr gets corrupted → points anywhere
*ptr = 100; // writes to random memory 💣
🔑 Key Idea
👉 Exploit depends on:
Which variable gets corrupted
How that variable is used
🧾 Simple Real-Life Analogy
👉 Think of global memory like:
A fixed storage shelf
If you overflow one box:
You overwrite items in neighboring boxes
🔑 Final One-Line
👉 Global/Static overflow = overwriting nearby persistent variables, which can lead to control if
important variables (like pointers) are corrupted.
If you want next, I can explain:
👉 Stack vs Heap vs Global overflow (full comparison — very important for exams)
🧠 Stack vs Heap vs Global Overflow (Full
Comparison)
🔹 1. STACK OVERFLOW
📌 Where?
Stack memory (function calls, local variables)
📌 Example:
void func() {
char buf[10];
gets(buf); // ❌ overflow
💥 What gets corrupted?
Local variables
Return address (VERY IMPORTANT)
💣 Impact:
Attacker can control execution flow
Can run malicious code
🔥 Exploit level:
👉 Very High (most dangerous & common)
🔹 2. HEAP OVERFLOW
📌 Where?
Heap memory (malloc, new)
📌 Example:
char *ptr = malloc(10);
strcpy(ptr, "very long input"); // ❌
💥 What gets corrupted?
Adjacent memory blocks
Heap metadata (headers)
Pointers
💣 Impact:
Memory corruption
Pointer overwrite
Control execution indirectly
🔥 Exploit level:
👉 High (complex but powerful)
🔹 3. GLOBAL / STATIC OVERFLOW
📌 Where?
Data segment (global & static variables)
📌 Example:
char buf[10];
int isAdmin = 0;
strcpy(buf, "AAAAAAAAAAAA"); // ❌
💥 What gets corrupted?
Nearby global/static variables
💣 Impact:
Change program logic
Modify flags (like isAdmin)
Pointer corruption (if present)
🔥 Exploit level:
👉 Medium (depends on variables nearby)
📊 🔥 FINAL COMPARISON TABLE
Feature Stack Overflow Heap Overflow Global Overflow
Memory Area Stack Heap Data Segment
Variables Local Dynamic (malloc) Global/Static
Main Target Return address Metadata & pointers Variables
Exploit Type Direct control Indirect control Logic manipulation
Difficulty Medium Hard Easy
Impact 🚨 Very High 🚨 High ⚠️Medium
🎯 Key Differences (Easy Way to Remember)
Stack → controls execution (return address) 💣
Heap → controls memory management (pointers) ⚙️
Global → controls program logic (variables) 🧠
🔑 Final One-Line (Exam Gold)
👉 Stack overflow hijacks execution directly, heap overflow corrupts memory structures to gain
control, and global overflow manipulates program variables and logic.
If you want, I can give you:
👉 Real attack examples + diagrams (very helpful for viva/interviews)
🧠 Language-Based Vulnerabilities (C Data Types)
🔹 Definition (Write this in exam)
👉 Language-based vulnerabilities arise due to improper use or misunderstanding of programming
language features such as data types, leading to unexpected behavior, overflows, or security
issues.
🔍 Basic Idea
👉 In C, different data types store data differently:
Size (how many bytes)
Range (min–max values)
Signed/Unsigned behavior
👉 If used incorrectly ❌
👉 It can cause:
Overflow
Wrong calculations
Security bugs
📦 Types of Data Types in C
1️⃣ Character Types
char
signed char
unsigned char
📌 Difference:
char → may be signed/unsigned (depends on system)
signed char → -128 to 127
unsigned char → 0 to 255
⚠️Vulnerability:
unsigned char x = 255;
x = x + 1; // becomes 0 ❌ overflow
💥 Result:
Unexpected behavior
2️⃣ Integer Types
short int
int
long int
long long int
📌 Difference:
Different sizes (2, 4, 8 bytes etc.)
⚠️Vulnerability:
int x = 2147483647;
x = x + 1; // overflow ❌
💥 Result:
Becomes negative
Logic errors
3️⃣ Floating Types
float
double
long double
Also:
float _Complex
double _Complex
⚠️Vulnerability:
Precision errors
Rounding issues
float x = 0.1 + 0.2;
printf("%f", x); // may not be exactly 0.3 ❌
4️⃣ Bit (Signed/Unsigned)
👉 Integers can be:
Signed → allow negative values
Unsigned → only positive
⚠️Vulnerability:
int a = -1;
unsigned int b = a;
printf("%u", b); // huge positive number ❌
💥 Result:
Type conversion bugs
Security issues
5️⃣ Type Aliasing (typedef)
typedef unsigned int uint32_t;
Common types:
int8_t → 8-bit signed
uint8_t → 8-bit unsigned
int32_t → 32-bit signed
uint32_t → 32-bit unsigned
📌 Why used?
👉 To ensure fixed size and consistency
⚠️Vulnerability:
👉 If wrong type is used:
Size mismatch
Overflow
Data misinterpretation
💣 Why this is dangerous (Security View)
👉 Attackers exploit type issues to:
Cause integer overflow
Bypass checks
Manipulate memory
🧾 Simple Example
int size = -1;
if (size < 100) {
malloc(size); // ❌ converted to large unsigned value
💥 Result:
Huge memory allocation
Crash or exploit
🔑 Final One-Line (Exam Ready)
👉 Language-based vulnerabilities occur due to incorrect handling of data types, leading to
overflow, type conversion errors, and unexpected program behavior.
🧠 Quick Revision Points
Signed vs unsigned issues
Integer overflow
Type conversion bugs
Precision errors
Wrong data size
🧠 Arithmetic Boundary Conditions
🔹 Definition (Write this in exam)
👉 Arithmetic boundary conditions occur when arithmetic operations (addition, subtraction,
multiplication) produce values outside the valid range of a data type, leading to overflow or
underflow.
🔍 Basic Idea
👉 Every variable has a fixed size and range
Example:
unsigned int (4 bytes) → range:
0 to 2^32 - 1
👉 If result goes:
Above max → Overflow
Below min → Underflow
💥 1️⃣ Numeric Overflow
❌ Example:
unsigned int a;
a = 0xE0000000;
a = a + 0x20000020;
👉 Actual result:
0x100000040 (too large ❌)
👉 But variable can't store it → extra bits lost
💣 Result:
Value becomes incorrect
Wraps around
🔥 Simple Understanding
👉 Think of a container:
Max capacity = fixed
If you add more → overflow
💥 2️⃣ Numeric Underflow
❌ Example:
unsigned int a = 0;
a = a - 1;
👉 Expected:
-1 ❌ (not possible in unsigned)
👉 Actual result:
4294967295 (very large number)
🔥 Simple Understanding
👉 Going below minimum → wraps to maximum
🧠 Modular Arithmetic (VERY IMPORTANT)
🔹 Definition
👉 Modular arithmetic means working with remainders after division
🔍 Example:
100 % 11 = 1
100 / 11 = 9
👉 Because:
100 = 11 × 9 + 1
📌 General Formula (IMPORTANT)
a=b×q+r
where 0 ≤ r < b
a = number
b = divisor
q = quotient
r = remainder
🔥 Connection to Overflow
👉 Computers use modular arithmetic internally
Example:
If value exceeds limit → wraps around (modulo behavior)
unsigned int x = MAX;
x = x + 1; // becomes 0
👉 Same as:
(x + 1) % (max_value + 1)
🎯 Real Use of Modular Arithmetic
1️⃣ Hash Tables
index = hash % 100;
👉 Ensures:
Value always between 0 to 99
2️⃣ Bounded Values
👉 Keeps numbers within range
⚠️Security Impact
👉 Overflow/underflow can:
Bypass checks
Cause buffer overflow
Lead to vulnerabilities
🧾 Simple Real-Life Analogy
👉 Think of a clock (12 hours):
After 12 → goes back to 1
👉 That’s modular arithmetic
🔑 Final One-Line (Exam Ready)
👉 Arithmetic boundary conditions occur when values exceed limits of data types, causing
overflow/underflow, and computers handle this using modular arithmetic (wrap-around behavior).
🧠 Quick Revision Points
Overflow → value too large
Underflow → value too small
Modular arithmetic → remainder system
Used in hash tables
Causes wrap-around behavior
If you want next, I can explain:
👉 Integer Overflow exploitation (VERY IMPORTANT for cybersecurity)
Byte Order Conversion
1. The Two Formats
Big-Endian: Stores the Most Significant Byte (the left-most, largest value part) in the lowest
memory address. It stores data exactly how humans read it (left to right).
Little-Endian: Stores the Least Significant Byte (the right-most, smallest value part) in the
lowest memory address. It stores data in reverse.
Example from your 3rd image:
Data: 00001010 (MSB) | 00010111 | 00001110 | 00000110 (LSB)
Big-Endian Memory: 00001010 goes into Address A (the first slot).
Little-Endian Memory: 00000110 goes into Address A (the first slot).
2. Network Byte Order Functions (2nd Image)
Different computer processors use different formats (e.g., Intel processors use Little-Endian).
However, network protocols (the internet) universally use Big-Endian. If a Little-Endian computer
sends data over the internet without translating it, the receiving computer will read the bytes
backward, completely corrupting the data.
To prevent this, programmers use built-in C functions to safely translate the bytes before sending or
receiving. The functions are named using a simple shorthand:
h = host (your local computer's format)
n = network (the internet's format, which is Big-Endian)
s = short (16-bit number / 2 bytes)
l = long (32-bit number / 4 bytes)
Examples:
htonl(): Host TO Network Long. Converts a 32-bit number from your computer's native
format into the network's format before transmitting it.
ntohs(): Network TO Host Short. Converts a 16-bit number received from the network back
into your computer's native format.
Type Conversion Vulnerabilities.
In programming, data types are like boxes of specific shapes and sizes. A vulnerability occurs when
the computer is forced to move a number from one type of box into a completely different type of
box, or when it tries to do math with two different boxes. The computer tries to be helpful by silently
"converting" the numbers, but it often does so in a way that breaks security logic.
Here is the breakdown of how these conversions work and why they are dangerous, especially during
comparisons.
1. Signed vs. Unsigned Types
Signed numbers can be positive or negative (e.g., -5, 10). They use their very first binary bit
to declare their sign (0 for positive, 1 for negative).
Unsigned numbers can only be positive (e.g., 0, 10, 255). Because they don't need a sign bit,
they can store much larger positive numbers.
The Danger: If you try to compare a Signed -1 to an Unsigned 5, the computer gets confused. It will
secretly convert the -1 into an Unsigned number. In the binary world, a signed -1 translated into an
unsigned number becomes an absolutely massive number (like 4,294,967,295).
2. Sign Extension
When you move a small number (like an 8-bit number) into a larger box (like a 16-bit box), there is
empty space. The computer must pad that space.
If the original number is positive, it pads the empty space with 0s.
If the original number is negative, it pads the space with 1s to ensure the number stays
negative.
The Danger: If a hacker passes a negative number into a system, and the system applies sign
extension (filling it with 1s) but then later reads that data as an unsigned positive number, those
padded 1s turn into a giant, unintended positive value.
3. Truncation
Your text describes decimal truncation (like the trunc() function chopping 3.5 down to 3.0).
However, in Code Auditing, the more dangerous version is Integer Truncation. This happens when
you try to stuff a large number into a box that is too small.
If you have a 32-bit number and force it into a 16-bit variable, the computer literally chops
off the top half of the binary code.
The Danger: A hacker might send an oversized file size that bypasses a security check, but
when it gets truncated, the chopped-up number looks small and safe to the system.
4. Arithmetic Conversions (The "Upgrade" Rule)
When you write code that does math or compares two different types of variables (like an int and a
float), the compiler follows a strict ranking system to make them match.
The rule is simple: The lower-ranked type is always upgraded to the higher-ranked type.
If you mix a float and a double, the float becomes a double.
If you mix a Signed int and an Unsigned int, the Signed integer is upgraded to Unsigned.
Shellcode -
1. What is Shellcode?
Shellcode is a small, custom piece of raw machine code (executable instructions) that an attacker
injects into a vulnerable program's memory.
Its name comes from its traditional purpose: to open a "shell" (a command-line terminal like /bin/sh
in Linux or [Link] in Windows) so the attacker can type commands directly into the compromised
system.
2. How it works with Buffer Overflows
As we covered earlier, a buffer overflow allows an attacker to write data past the boundaries of a
memory buffer.
In a standard program, right next to that buffer is a critical piece of memory called the Return
Address. This address acts as a signpost telling the CPU exactly where to go to execute the next
instruction.
The exploit works in two steps:
1. Inject: The attacker pushes the shellcode into the memory buffer.
2. Redirect: The attacker intentionally overflows the buffer until they overwrite the Return
Address. They change that address so it points directly to the location where they just
injected their shellcode. When the CPU reads the altered Return Address, it blindly follows
the signpost and executes the attacker's code.
3. The Provided C Code
char *args[] = { "/bin/sh", NULL };
execve("/bin/sh", args, NULL);
This is standard C code used to spawn a terminal in UNIX/Linux systems. However, a CPU cannot read
C code directly. The attacker must compile this specific C code into raw binary (machine code) and
use that binary sequence as their injected shellcode.
4. Staged Shellcode ("Stubs")
Often, the vulnerable memory buffer is too small to hold a large, complex piece of shellcode.
To solve this, attackers inject a Stub. A stub is a microscopic piece of shellcode that does only one
thing: it opens a network connection back to the attacker's computer, downloads the rest of the
larger malicious payload directly into memory, and then runs it.
Here is a Memory Flow Simulator to help you visualize exactly how an attacker manipulates the
Return Address to force the CPU to execute injected Shellcode instead of the normal program.
Show me the visualisation