-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlevel36.ts
More file actions
56 lines (51 loc) · 10.4 KB
/
level36.ts
File metadata and controls
56 lines (51 loc) · 10.4 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
import { Level } from './types';
export const level36: Level = {
id: 36,
title: "Stack Canary: The Watcher",
description: "Stack canaries (stack cookies) defend against buffer overflows by placing a random value between local variables and control data (saved EBP, return address). Stack frame layout: [BUFFER (64 bytes)] [CANARY (4 bytes)] [SAVED_EBP (4 bytes)] [SAVED_RET (4 bytes)]. Function prologue: CANARY ← random_value (stored in stack). Function epilogue: if (stack_CANARY != original_CANARY) abort() (___stack_chk_fail, 'stack smashing detected'). Exploitation: Buffer overflow still possible, but blindly overwriting CANARY triggers termination. Bypass technique: Information leak to read CANARY value, then craft overflow payload that preserves CANARY while corrupting SAVED_RET. Payload structure: ['A'×64 (fill buffer)] [0xDEADBEEF (preserved canary)] ['BBBB' (overwrite saved EBP)] [target_addr (overwrite saved RET)]. This level: CANARY_VALUE visible at 0xDEADBEEF (3735928559 decimal). Demonstrate bypass by: reading canary → setting SAVED_CANARY to match → crafting 64+ byte overflow → redirecting RETURN_ADDR. Real attacks combine format string leak (%p to read stack canary) with overflow. GCC -fstack-protector enables this defense (default on modern Linux/Windows).",
requiredSkill: "Canary Bypass",
objective: (s) => {
const canaryRead = s.sortValue1 === 0xDEADBEEF;
const canaryPreserved = s.sortValue2 === 0xDEADBEEF;
const offsetCorrect = s.sortValue3 === 64;
const payloadSufficient = (s.payload || '').length >= 64;
const retAddrSet = s.eip !== '00000000' && s.eip !== '0';
const canaryBypass = s.debugDetected === true;
const bypassSuccess = s.isAdmin === true;
return canaryRead && canaryPreserved && offsetCorrect && payloadSufficient && retAddrSet && canaryBypass && bypassSuccess;
},
hint: "Seven-stage stack canary bypass. Stage 1: Read CANARY_VALUE (sortValue1 int, original canary in function prologue, value: 3735928559 / 0xDEADBEEF). Stage 2: Set SAVED_CANARY (sortValue2 int, canary preserved in overflow payload, must match CANARY_VALUE: 3735928559). Stage 3: Set CANARY_OFFSET (sortValue3 int, buffer size before canary, value: 64). Stage 4: Craft OVERFLOW payload (payload string, 64+ bytes to trigger overflow, e.g., 'A'×72). Stage 5: Set RETURN_ADDR (eip pointer, target address to redirect execution, any non-zero hex like 0xDEADBEEF). Stage 6: Verify CANARY_BYPASS=true (debugDetected, auto-set when canary preserved). Stage 7: Verify BYPASS_SUCCESS=true (isAdmin, auto-triggers when all stages complete). Use Memory Scanner to read CANARY_VALUE, then craft overflow that preserves canary at byte 64 while corrupting return address at byte 72. Real exploit: format string leak to read canary → overflow with preserved canary → RET hijack. Stack layout post-overflow: [AAAA...64 bytes] [0xDEADBEEF canary preserved] [BBBB saved_ebp] [target_addr saved_ret].",
tutorPersona: "Crispin Cowan/StackGuard Team: When you can't stop the flood, place a tripwire in the water. History: 1996-1997 - Buffer overflow epidemic: Stack smashing attacks dominate (Aleph One's 'Smashing the Stack for Fun and Profit', Phrack 49, November 1996). Exploit pattern: Overflow local buffer → Overwrite saved return address → Redirect execution to shellcode. Defenses attempted: Bounds checking (performance cost), non-executable stack (W^X, bypassed by ret2libc). 1997 - Crispin Cowan, Calton Pu, Dave Maier (OGI/Portland State University) develop StackGuard. Concept: Probabilistic defense via 'canary values'. Name origin: Coal mine canaries (early warning system - canary dies from gas before miners affected). Canary placement: Stack frame modified: [local_vars][CANARY][saved_ebp][saved_ret]. Function prologue: Generate random 32-bit value → Store in CANARY position. Function epilogue: Check CANARY unchanged → If modified, call __stack_chk_fail() → Terminate process with 'stack smashing detected' error. Attacker problem: Overflow from buffer must traverse CANARY to reach saved_ret → Blind overflow corrupts CANARY → Detection triggered. StackGuard canary types: 1. Terminator Canary (0x00000aff): Contains NULL (0x00), CR (0x0a), LF (0x0d), EOF (0xff) - characters that terminate string functions (strcpy, gets) - prevents overflow from writing past canary. 2. Random Canary: 32-bit random value generated at program start - stored in global variable - copied to stack on function entry - attacker must leak value to preserve it. 3. XOR Canary: CANARY = random_value XOR (saved_ret XOR saved_ebp) - integrity check that control data unchanged - more complex, rarely used. StackGuard deployment: 1998 - StackGuard GCC patch released. Immunix Linux (1999) includes StackGuard by default. 2000-2001 - IBM ProPolice (Hiroaki Etoh): Enhanced StackGuard with variable reordering (arrays placed after scalars, preventing pointer corruption). GCC integration: 2005 - ProPolice merged into GCC 4.1 as -fstack-protector. -fstack-protector: Protects functions with vulnerable buffers. -fstack-protector-all: Protects all functions (performance impact). -fstack-protector-strong (GCC 4.9, 2014): Balanced protection (arrays, address-taken variables, register spills). Modern defaults: Ubuntu/Debian (2006+), Fedora/RHEL (2005+), Windows /GS (Visual Studio 2003+). Canary values modern implementations: Linux GCC: Random canary from /dev/urandom, stored in TLS (Thread Local Storage) at fs:0x28 (x86-32) or fs:0x28 (x86-64). Value randomized per-process (ASLR for canary). Windows /GS: __security_cookie (global variable), randomized at process start. Epilogue: __security_check_cookie() compares stack canary to global. Android: -fstack-protector-strong default since Android 4.1 (2012). iOS: Stack canaries enabled since iOS 5 (2011). Bypass techniques: 1. Information Leakage (primary method): Format string vulnerability: printf(user_input) with %p tokens → Leak stack contents → Read canary value → Craft overflow preserving canary. Example: Payload = 'A'×64 + leaked_canary + 'BBBB' + target_ret. Stack read via other bugs: Use-after-free, dangling pointer, uninitialized memory → Leak canary. 2. Canary Brute Force (32-bit only): Fork server pattern: Parent forks child per request → Child crashes on bad canary → Parent survives. Brute force 32-bit canary: 2^32 attempts (4 billion) → Feasible with automated fuzzing. Byte-by-byte brute force: Overflow one byte at a time → Detect crashes → Reconstruct canary (256×4 = 1024 attempts). 64-bit canaries: 2^64 attempts (18 quintillion) → Impractical. 3. Canary Overwrites (rare): Write-what-where bug: Arbitrary write primitive → Overwrite __stack_chk_fail GOT entry with dummy function → Bypass detection. TLS corruption: Overwrite TLS canary value (fs:0x28) to match overflowed canary → Detection bypassed. 4. Return-Oriented Programming (modern bypass): Canary preserved in overflow → Overwrite only saved_ret (canary at +64, saved_ret at +72) → ROP chain starts at saved_ret. ROP doesn't execute stack (NX compatible) → Chains existing code → Canary irrelevant if control flow hijacked correctly. Real-world examples: 2005 - CVE-2005-1689: Sudo format string → leak canary → overflow with preserved canary → root. 2013 - CVE-2013-2094: Linux kernel perf_swevent_init → leak kernel stack canary → overflow → privilege escalation. 2014 - GHOST (CVE-2015-0235): glibc gethostbyname buffer overflow → canary bypass via info leak → RCE. 2019 - BlueKeep (CVE-2019-0708): Windows RDP → heap spray + stack pivot → bypass canary via info leak → RCE. Modern defense stacking: Canary alone insufficient: Leaks common (format strings, side-channels, Spectre). Canary + ASLR: Randomize code/library addresses → Harder to craft ROP chain even with canary bypass. Canary + NX + ASLR + PIE + CFI: Modern baseline (Linux 5.x, Windows 10+, iOS 14+, Android 11+). Canary + Shadow Stack (Intel CET, ARM PAC): Hardware-enforced return address protection → Canary redundant but defense-in-depth. Limitations and future: Canaries detect corruption post-facto (attack already occurred, just caught). No protection against: Data-only attacks (corrupt non-control data like passwords, permissions). Heap overflows (canaries only on stack). Format string writes (%n primitive). Use-after-free, type confusion. Future: Memory-safe languages (Rust, Go) eliminate buffer overflows entirely. Hardware memory tagging (ARM MTE, RISC-V PMP): Tag-based bounds checking in silicon → Overhead <5%. Canaries remain ubiquitous (deployed on billions of devices), but info leaks (format string, Spectre, side-channels) enable bypass → Defense-in-depth required (ASLR + CFI + memory tagging). This level: Canary at offset 64 (0xDEADBEEF). Visible via Memory Scanner (simulates info leak). Demonstrate bypass: Read canary → Preserve in overflow payload → Corrupt return address → Exploit succeeds. Teaches 1998-2024 standard mitigation and its defeat. Educational pattern: Defense (canary, 1997) → Attack evolution (info leak bypass, 2000-2005) → Defense arms race (ASLR+canary, 2005-2015) → Modern stacking (canary+CFI+MTE, 2020+).",
memoryLayout: [
{ key: 'sortValue1', label: 'CANARY_VALUE', type: 'int', offset: 0x40, isStatic: true },
{ key: 'sortValue2', label: 'SAVED_CANARY', type: 'int', offset: 0x44 },
{ key: 'sortValue3', label: 'CANARY_OFFSET', type: 'int', offset: 0x48 },
{ key: 'payload', label: 'OVERFLOW', type: 'string', offset: 0x0 },
{ key: 'eip', label: 'RETURN_ADDR', type: 'pointer', offset: 0x80 },
{ key: 'debugDetected', label: 'CANARY_BYPASS', type: 'bool', offset: 0x800 },
{ key: 'isAdmin', label: 'BYPASS_SUCCESS', type: 'bool', offset: 0x30 }
],
initialState: {
sortValue1: 0xDEADBEEF,
sortValue2: 0,
sortValue3: 0,
payload: '',
eip: '00000000',
debugDetected: true,
isAdmin: false
},
update: (s) => {
const canaryRead = s.sortValue1 === 0xDEADBEEF;
const canaryPreserved = s.sortValue2 === 0xDEADBEEF;
const offsetCorrect = s.sortValue3 === 64;
const payloadSufficient = (s.payload || '').length >= 64;
const eipValue = parseInt((s.eip || '00000000').replace('0x', ''), 16);
const retAddrSet = eipValue !== 0;
const updates: any = {};
if (canaryRead && canaryPreserved && offsetCorrect && payloadSufficient && retAddrSet) {
updates.isAdmin = true;
}
return updates;
},
platforms: [{ id: 'p1', x: 0, y: 280, width: 800, height: 40, type: 'static' }]
};