-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlevel25.ts
More file actions
98 lines (91 loc) · 9.1 KB
/
level25.ts
File metadata and controls
98 lines (91 loc) · 9.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { Level } from './types';
export const level25: Level = {
id: 25,
title: "Buffer Overflow: The Stack Smashing Art",
description: "Classic stack buffer overflow exploitation. Function ProcessInput() allocates 16-byte buffer on stack, calls unsafe strcpy() without bounds checking. Input exceeding buffer size overwrites adjacent stack memory: local variables, saved Base Pointer (EBP), Return Address (EIP/RIP), causing control flow hijack. Stack layout from high to low address: [Return Address (4 bytes) @ offset +24] | [Saved EBP (4 bytes) @ offset +20] | [Stack Canary (4 bytes, 0xDEADBEEF) @ offset +16] | [Buffer[16 bytes] @ offset 0] | [Local Variables]. Initial state: CANARY_INTACT=1 (guard value 0xDEADBEEF intact), RET_ADDR_ORIGINAL=0 (EIP not corrupted), BUFFER_SIZE=16 bytes, OVERFLOW_OFFSET=0 (no overflow). Goal: Overflow buffer to overwrite Return Address with controlled value 0x41414141. Challenges: Stack Canary corruption triggers __stack_chk_fail() abort. Precise offset calculation required - must write ≥24 bytes to reach RET. Payload constraints: avoid null bytes that terminate strcpy(). Set OVERFLOW_OFFSET≥20 to corrupt canary (offset 16-19), set OVERFLOW_OFFSET≥24 to overwrite RET. Set EIP_VALUE=0x41414141 (1094795585 decimal), CANARY_INTACT=0 (corrupted), RET_ADDR_CORRUPTED=1 (hijacked). Use Fuzzer to discover crash offset.",
requiredSkill: "Stack Buffer Overflow Exploitation & EIP Hijacking",
objective: (s) => {
const canaryCorrupted = s.sortValue1 === 0; // CANARY_INTACT (0 = corrupted, canary overwritten)
const retAddrHijacked = s.sortValue2 === 1; // RET_ADDR_CORRUPTED (1 = EIP overwritten with controlled value)
const overflowOccurred = s.sortValue3 >= 20; // OVERFLOW_OFFSET (must write ≥20 bytes to reach RET past 16-byte buffer + 4-byte canary)
const espNum = typeof s.esp === 'string' ? parseInt(s.esp, 16) : s.esp;
const eipControlled = espNum === 0x41414141; // EIP_VALUE (must set to 0x41414141 or other controlled value, proving control)
return canaryCorrupted && retAddrHijacked && overflowOccurred && eipControlled;
},
hint: "Four-stage exploit. Stage 1: Use Fuzzing Chaos Engine (Havoc mode) to send patterns and find crash offset (watch for EIP=0x41414141). Stage 2: Calculate precise offset where Return Address is overwritten (typically 16 bytes buffer + 4 bytes canary + 4 bytes EBP = 24 bytes to reach EIP). Stage 3: Set OVERFLOW_OFFSET=20+ to trigger overflow past buffer boundary. Stage 4: Set EIP_VALUE=0x41414141 (1094795585 decimal), RET_ADDR_CORRUPTED=1, CANARY_INTACT=0 (corruption detected). Use Memory Scanner to set: OVERFLOW_OFFSET (sortValue3), RET_ADDR_CORRUPTED (sortValue2), CANARY_INTACT (sortValue1), EIP_VALUE (esp). Fuzzer will show crash analysis with exact offset.",
tutorPersona: "The Exploit Developer: Stack buffer overflow is original sin of software security. History: 1988 - Morris Worm exploits fingerd via gets() overflow, first internet worm. 2001 - Code Red exploits IIS via IndexServer overflow, infects 359,000 servers. 2003 - SQL Slammer exploits SQL Server via UDP packet overflow, fastest spreading worm (75,000 hosts in 10 minutes). 2014 - Heartbleed (CVE-2014-0160) buffer over-read in OpenSSL, leaks memory contents. These shaped modern security. How it works: Stack grows downward (high address → low). Function prologue: push rbp; mov rbp,rsp; sub rsp,N (allocate locals). Function epilogue: leave; ret (restore rbp, pop return address into RIP, jump). Vulnerable code: void ProcessInput() { char buffer[16]; strcpy(buffer, user_input); }. No bounds check. strcpy copies until null byte. Input 'AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIII' (40 bytes) → buffer[16] holds first 16 bytes AAAABBBBCCCCDDDD, next 4 bytes EEEE overwrite canary (if present), next 4 bytes FFFF overwrite saved EBP, next 4 bytes GGGG overwrite Return Address (EIP/RIP), next 12 bytes HHHHIIII continue into previous stack frame. When function returns, CPU executes: pop rip (loads GGGG into RIP), jmp [rip] → redirects execution to address 0x47474747 (ASCII 'GGGG'). Attacker controls instruction pointer. Game over. Stack Canary mitigation: Compiler inserts random guard value (GCC -fstack-protector uses __stack_chk_guard) between buffer and control data. Prologue: mov rax, fs:[0x28]; mov [rbp-0x8], rax (place canary on stack). Epilogue: mov rdx, [rbp-0x8]; xor rdx, fs:[0x28]; je epilogue_ok; call __stack_chk_fail (compare canary, abort if corrupted). Bypass: Leak canary via format string bug or info leak, then include correct canary in overflow payload at right offset. Or use partial overwrite (overwrite only 1 byte of RET, preserve canary). ASLR mitigation: Kernel randomizes base addresses (.text, libraries, stack, heap). /proc/sys/kernel/randomize_va_space=2. Bypass: Info leak (print pointer, calculate offset to gadget), brute force (32-bit has limited entropy), ROP NOP sled (spray with ret gadgets). DEP/NX mitigation: Mark stack/heap pages NX (not executable). Hardware NX bit in page table entry. Bypass: Return-to-libc (redirect to system(), execve() in libc), ROP chains (chain gadgets ending in RET). Exploitation workflow: 1) Fuzzing - Send cyclic patterns (de Bruijn sequence: Aa0Aa1Aa2...) to discover crash offset. Pattern_offset calculates EIP overwrite position. 2) Debugger - Confirm EIP control (registers show 0x41414141), analyze stack frame. 3) Payload crafting - [NOP sled 0x90][Shellcode 25-100 bytes][Padding][New RET addr]. 4) Delivery - Inject via network socket, file input, environment variable. Real-world defenses: Modern systems layer protections - Stack Canaries + ASLR + DEP/NX + RELRO (GOT hardening) + CFI (Control Flow Integrity). Defeating all requires: Info leak + Canary bypass + ROP chain + heap grooming. Educational value: Understand stack frames, calling conventions (cdecl, stdcall), assembly (push, pop, call, ret), memory corruption mechanics, defense-in-depth principle. This level simulates discovery phase - fuzzing to identify offset, demonstrating EIP control. Real exploit would follow with shellcode injection or ROP chain. Safe functions: Use strncpy, strlcpy, snprintf with size limits. Compiler flags: -fstack-protector-all, -D_FORTIFY_SOURCE=2, -fPIE. Runtime: DEP/ASLR enabled by default on modern OS.",
memoryLayout: [
{ key: 'sortValue1', label: 'CANARY_INTACT', type: 'int', offset: 0xA0 },
{ key: 'sortValue2', label: 'RET_ADDR_CORRUPTED', type: 'int', offset: 0xA4 },
{ key: 'sortValue3', label: 'OVERFLOW_OFFSET', type: 'int', offset: 0xA8 },
{ key: 'esp', label: 'EIP_VALUE', type: 'int', offset: 0x100 },
{ key: 'health', label: 'BUFFER_SIZE', type: 'int', offset: 0x10 },
{ key: 'score', label: 'CRASH_COUNT', type: 'int', offset: 0x50 }
],
initialState: {
sortValue1: 1, // CANARY_INTACT (1 = canary 0xDEADBEEF intact, 0 = corrupted)
sortValue2: 0, // RET_ADDR_CORRUPTED (0 = original RET, 1 = hijacked)
sortValue3: 0, // OVERFLOW_OFFSET (bytes written, must reach 20+ to overwrite RET)
esp: '00401020', // EIP_VALUE (current return address, goal: 0x41414141)
health: 16, // BUFFER_SIZE (allocated stack buffer size)
score: 0, // CRASH_COUNT (number of crashes detected by fuzzer)
fuzzer: {
isRunning: false,
casesTested: 0,
crashesFound: 0,
coverage: 0,
mutationRate: 0.3,
log: [
'[FUZZER] Initialized. Target: ProcessInput() @ 0x00401000',
'[FUZZER] Buffer size: 16 bytes. Stack canary: 0xDEADBEEF @ offset +16',
'[FUZZER] Saved EBP @ offset +20. Return Address @ offset +24',
'[INFO] Start fuzzing to discover crash offset...'
],
strategy: 'havoc'
}
},
update: (s) => {
const offset = s.sortValue3 || 0;
if (offset > 16 && offset < 20) {
return {
sortValue1: 0,
score: (s.score || 0) + 1
};
}
if (offset >= 20 && offset < 24) {
return {
sortValue1: 0,
sortValue2: 0,
score: (s.score || 0) + 1
};
}
if (offset >= 24) {
const crashes = (s.score || 0) + 1;
const newLog = [...(s.fuzzer?.log || [])];
if (crashes === 1 || crashes % 5 === 0) {
const espRaw: string | number = s.esp as any;
const espValue = typeof espRaw === 'string' ? espRaw : espRaw.toString(16);
newLog.unshift(`[CRASH #${crashes}] SIGSEGV @ ProcessInput+0x1B (ret instruction)`);
newLog.unshift(`[ANALYSIS] EIP overwritten at offset ${offset}. EIP=0x${espValue.toUpperCase()}`);
newLog.unshift(`[STACK] Buffer[16] -> Canary[4] @ +16 -> EBP[4] @ +20 -> RET[4] @ +24`);
}
if (newLog.length > 25) newLog.splice(25);
return {
sortValue1: 0,
sortValue2: 1,
score: crashes,
fuzzer: {
isRunning: false,
casesTested: s.fuzzer?.casesTested || 0,
crashesFound: crashes,
coverage: s.fuzzer?.coverage || 0,
mutationRate: s.fuzzer?.mutationRate || 0.3,
strategy: s.fuzzer?.strategy || 'havoc',
log: newLog
}
};
}
return {};
},
platforms: [{ id: 'p1', x: 0, y: 280, width: 800, height: 40, type: 'static' }]
};