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:
...