Executing Shellcode on Stack

Previously we saw how to overwrite the return address of stack by passing extra bytes to an unbounded buffer. But not everytime we will have a convinient function address to overwrite. The next attempt is to place the code we want to execute directly on the stack itself. Embedding Shellcode in a Buffer Overflow 1. What is Shellcode? Shellcode is machine instructions (usually written in assembly) that perform some action — often spawning a shell (/bin/sh), but can be anything. It is called “shellcode” not because it must open a shell, but because historically it did. 2. Why embed shellcode on the stack? Sometimes you don’t know the address of any useful existing function. Or the binary doesn’t have functions like system("/bin/sh"). In these cases, the attacker places their own code (shellcode) inside the same buffer that overflows. Then somehow point $rip register to the location of shellcode on stack so that CPU starts executing it. 3. Requirements for embedding shellcode 1. Executable stack The stack must have executable permissions. Many modern systems have NX (Non-Executable) protection → stack is not executable. CPU provides setting read, write and executable permissions at individual page level which is enforced at hardware level. This feature has been there since 80286 CPU. Executing shellcode on stack will be impossible just by marking stack pages as non-executable. For learning, we usually compile with flags -z execstack which will make the stack executable. gcc -fno-pie -no-pie -fno-stack-protector -z execstack main.c -o vuln 2. Enough space in the buffer Shellcode must fit entirely inside the buffer or adjacent space. 3. No null bytes When injecting shellcode via string functions like gets, scanf("%s"), strcpy, a null byte will terminate input. Shellcode must avoid 0x00, 0x0A, etc. 4. Stack-Based Shellcode Execution: A Simple Case Without ASLR To build the payload of overflowed string we need to consider various factors, let’s understand it with a simple program: ...

December 1, 2025 · 13 min · Sanketh

CTF – 3 : 45exiles Shuffle

CTF Writeup: 45exiles Shuffle Challenge Problem Overview Challenge Name: 45exiles Shuffle Core Mechanism: Program reads user input Shuffles the input using rand() with a known seed Compares the shuffled input against a target string stored in memory If they match, you get the flag Key Insight: You can read the target (shuffled) string from memory, but that’s NOT the answer you need to input. You must reverse the shuffle to find the original input. ...

November 14, 2025 · 5 min · Sanketh

Stack Based Buffer Overflow Attacks

Stack Based Buffer Overflow Attacks A buffer overflow occurs when a program writes more data into a fixed-size buffer than it was designed to hold. A buffer is a contiguous block of memory allocated to store data (e.g., an array/string whose length is defined at compile time). Causes of Buffer Overflow Attacks in C When we talk about buffer overflows, its almost always about buffer overflows in C programs because of the way the following things are designed in C: ...

November 11, 2025 · 30 min · Sanketh

CTF – 2 : Good Kitty

CTF Challenge: Good Kitty - Writeup Challenge Overview This is a reverse engineering CTF challenge where we need to find the correct password by analyzing a binary that: Calculates a value based on Project Euler problem #3 Encodes it using a custom algorithm Compares user input against the encoded value Initial Analysis Decompiled Code Structure undefined8 main(void) { byte bVar1; ssize_t bytes_read; long input_len; int iVar2; long in_FS_OFFSET; double dVar3; undefined1 local_be; byte is_correct; uint index; long flag; undefined8 local_b0; undefined8 local_a8 [4]; undefined8 local_88; undefined8 uStack_80; undefined8 local_78; char user_input [72]; long local_20; local_20 = *(long *)(in_FS_OFFSET + 0x28); flag = ppeuler_3(); dVar3 = cbrt((double)flag); flag = (long)dVar3; flag = factorial(flag); input_len = 0; do { bVar1 = *(byte *)((long)&flag + input_len); if ((0x19 < (byte)((bVar1 & 0xdf) + 0xbf)) && (9 < (byte)(bVar1 - 0x30))) { bVar1 = bVar1 % 0x3e; if ((byte)(bVar1 - 10) < 0x1a) { *(byte *)((long)&flag + input_len) = bVar1 + 0x37; } else if ((byte)(bVar1 + 0x30) < 0x54) { *(byte *)((long)&flag + input_len) = bVar1 + 0x30; } else { *(byte *)((long)&flag + input_len) = bVar1 + 0x3d; } } input_len = input_len + 1; } while (input_len != 8); // ... rest of code validates input } Key Concepts Learned 1. Understanding Pointer Arithmetic on Stack Variables Question: flag is declared as long flag; (not an array), so what does &flag + index mean? ...

November 9, 2025 · 5 min · Sanketh

GDB Notes – Basics & Practical Usage

GDB Notes — Basics, Navigation & Memory Inspection These are concise notes on how to use GDB (GNU Debugger) effectively for analyzing ELF binaries and debugging at both C source and assembly levels. 1. Starting GDB Basic invocation gdb ./a.out gdb -q ./program # Quiet mode (no banner) With arguments gdb --args ./program arg1 arg2 From inside GDB (gdb) run arg1 arg2 2. Compiling for Debugging Compile with the -g flag to include debug symbols: ...

November 9, 2025 · 5 min · Sanketh

GDB print Command

GDB Print Command Reference Guide Basic Print Command print variable_name p variable_name # Short form Print with Format Specifiers Use /format after print to specify output format: Hexadecimal print/x variable # Hex (lowercase) print/x value # Example: 0x5 p/x 255 # Output: 0xff Decimal print/d variable # Signed decimal print/u variable # Unsigned decimal p/d 0xff # Output: 255 Octal print/o variable # Octal format p/o 64 # Output: 0100 Binary print/t variable # Binary (t = "two") p/t 5 # Output: 101 Character print/c variable # As ASCII character p/c 65 # Output: 'A' Floating Point print/f variable # Floating point p/f 3.14159 Address/Pointer print/a variable # As address p/a 0x555555555189 # Shows as address String print/s pointer # Interpret as C string p/s argv[1] Working with Pointers print pointer # Shows address print *pointer # Dereference (shows value) print &variable # Shows address of variable # Example print argv # Address of argv array print *argv # First element (argv[0]) print argv[1] # Second element print *argv[1] # First char of argv[1] Array and String Operations # Print entire array print buffer_one print buffer_two # Print specific elements print buffer_one[0] print buffer_one[3] # Print array slice (if supported) print buffer_one@8 # Print 8 elements starting at buffer_one # View string with length x/8c buffer_one # First 8 chars x/s buffer_one # Until null terminator Type Casting # Cast to different types print (int *)buffer_one # Treat as int pointer print *(int *)buffer_one # Dereference as int print (unsigned char)value # Cast to unsigned char # Example: View buffer as integers print *(int *)&buffer_one print *(long *)&buffer_two Expressions and Calculations # Arithmetic print value + 10 print sizeof(buffer_one) print strlen(buffer_one) # Address calculations print &buffer_one - &buffer_two print (long)&value - (long)&buffer_one # Pointer arithmetic print argv[0] print *(argv + 1) Display Commands (Auto-Print) Set up variables to display automatically after each step: ...

November 9, 2025 · 4 min · Sanketh

GDB Layouts

Debugging Souce Code with Assembly If we compile C program with -g flag, it tells GCC to include debugging symbols inside the resulting binary (a.out by default). These symbols live in a special section of the ELF file (like .debug_info, .debug_line, .debug_str, etc.) and contain metadata that maps machine instructions back to your original source code. This allows us to see corresponding source along with assembly while debugging with GDB. ...

November 4, 2025 · 4 min · Sanketh

GDB x Command

Examining Memory with x Command The x (examine) command is more powerful for raw memory: x/[count][format][size] address Size Modifiers b = byte (1 byte) h = halfword (2 bytes) w = word (4 bytes) g = giant word (8 bytes) Format Modifiers x = hexadecimal d = decimal u = unsigned decimal o = octal t = binary c = character s = string i = instruction (disassembly) count: how many units to display ...

November 4, 2025 · 1 min · Sanketh

Radare2 Basic Commands

Reverse Engineering with Radare2 A powerful framework for reverse engineering. Core Analysis Commands r2 Command Description r2 ./binary Open the binary for analysis. aaa Analyze All Automatically. Finds functions, symbols, etc. afl Analyze Function List. Shows all identified functions. s <address/name> Seek to a specific address or function name (e.g., s main). pdf Print Disassembled Function. Shows assembly code. pdg Print Decompiled Ghidra. Shows decompiled C-like code. afv Analyze Function Variables. Shows local variables, arguments, and their stack offsets. Additional Useful Commands r2 Command Description i Show general information about the binary (imports, exports, strings). ps Print a summary of the binary’s sections. V Enter visual mode for interactive navigation. VV Enter visual graph mode to see the control flow graph. ? / ?? Get help on commands.

November 4, 2025 · 1 min · Sanketh

CTF – 1 : Matryoshka

Reverse Engineering and CTF Challenge Notes Reverse engineering is the process of understanding how software works without access to its original source code. In security challenges (CTFs), the goal is often to recover hidden data or logic by dissecting a binary. This walkthrough documents my first attempt at such a challenge, focusing on ELF-based reverse engineering and the reasoning process behind each step. 1. Static Analysis Static analysis means inspecting the binary without running it. ...

November 1, 2025 · 4 min · Sanketh