-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlevel31.ts
More file actions
73 lines (65 loc) · 9.58 KB
/
level31.ts
File metadata and controls
73 lines (65 loc) · 9.58 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
import { Level } from './types';
export const level31: Level = {
id: 31,
title: "Stack Canary Evasion: Precision Overflow Engineering",
description: "ACT 2 OPENER - Production stack smashing with GCC Stack-Smashing Protector (SSP). Stack layout: BUFFER[16] | CANARY | SAVED_EBP | RETURN_ADDR. Modern compilers insert random canary values (0xDEADBEEF) between local variables and control data. On function return, __stack_chk_fail() validates canary integrity. Corruption triggers immediate termination. Required: Calculate precise overflow offset (28 bytes total), preserve CANARY unchanged (0xDEADBEEF at offset 16-19), preserve SAVED_EBP (original frame pointer), overwrite RETURN_ADDR to win() function (0x08049666 at offset 24-27). Canary corruption = instant detection. Use Memory Scanner for surgical precision.",
requiredSkill: "Stack Canary Bypass + Precision Overflow Calculation",
objective: (s) => {
const bufferSizeValid = s.sortValue1 === 16;
const overflowLengthValid = s.sortValue2 === 28;
const canaryPreserved = s.sortValue3 === 0xDEADBEEF;
const ebpPreserved = s.health === 0xBFFFDC00;
const retAddr = (s.pointerChainBase || '').toLowerCase();
const retAddrOverwritten = retAddr === '08049666' || retAddr === '8049666';
const canaryIntact = s.debugDetected === false;
const exploitSuccess = s.isAdmin === true;
return bufferSizeValid && overflowLengthValid && canaryPreserved && ebpPreserved && retAddrOverwritten && canaryIntact && exploitSuccess;
},
hint: "Seven-stage precision overflow with canary bypass. Stage 1: Set BUFFER_SIZE=16 (sortValue1, allocation size). Stage 2: Calculate OVERFLOW_LENGTH=28 total bytes (sortValue2, buffer + canary + ebp + ret). Stage 3: Preserve CANARY_VALUE=0xDEADBEEF (sortValue3, sentinel at offset 16-19, must stay 3735928559 decimal). Stage 4: Preserve SAVED_EBP=0xBFFFDC00 (health, original frame pointer 3221216256 decimal). Stage 5: Overwrite RETURN_ADDR=0x08049666 (pointerChainBase hex string, win function). Stage 6: Verify CANARY_INTACT=false (debugDetected, no corruption detected). Stage 7: Trigger EXPLOIT_SUCCESS=true (isAdmin, all validations pass). Use Memory Scanner to calculate precise offsets and preserve stack integrity.",
tutorPersona: "Morpheus/Aleph One: The stack is not a void. It is a carefully ordered sequence of trust. BUFFER trusts CANARY. CANARY trusts EBP. EBP trusts RETURN_ADDR. RETURN_ADDR trusts the caller. Break this chain, but leave no trace. History: 1996 - Aleph One publishes 'Smashing The Stack For Fun And Profit' in Phrack 49. Revolutionizes exploitation. Classic stack overflow: Overflow local buffer, overwrite saved return address, redirect EIP to shellcode. Simple. Effective. Devastated 1990s software. 1998 - StackGuard (Cowan et al.) introduces canary-based protection. Compiler inserts random 32-bit value (canary) between local variables and control data (EBP/RET). Function prologue: PUSH canary to stack. Function epilogue: CMP stack_value, original_canary; if mismatch, __stack_chk_fail() terminates process immediately. Canary names: 'Canary' (coal mine warning bird dies first), 'Stack Cookie' (Microsoft terminology), 'Sentinel Value' (academic papers). 2001 - ProPolice (IBM/Hiroaki Etoh) extends StackGuard. Variable reordering: Arrays placed below scalar variables, preventing overflow from corrupting adjacent locals. Pointer protection: Function pointers stored below arrays. Frame pointer protection: Canary placed before saved EBP. 2005 - GCC 4.1 integrates Stack-Smashing Protector (-fstack-protector). Three modes: -fstack-protector (protect functions with char arrays/alloca), -fstack-protector-strong (protect functions with local arrays of any type), -fstack-protector-all (protect ALL functions, significant overhead). Default canary: Random value XORed with frame pointer, seeded from /dev/urandom or RDRAND instruction. Validation: __stack_chk_guard global contains master canary. On return: XOR [EBP-8], __stack_chk_guard; JE valid; CALL __stack_chk_fail. Stack layout x86 (32-bit): [High Addresses] Caller's Stack Frame | RETURN_ADDR (4 bytes, offset +24 from buffer) | SAVED_EBP (4 bytes, offset +20) | STACK_CANARY (4 bytes, offset +16, 0xDEADBEEF) | Local Variables (buffer[16], offset 0-15) | [Low Addresses] <-- ESP. Stack grows downward (PUSH decrements ESP), so overflow from buffer[0] toward higher addresses overwrites canary, then EBP, then RET. x86-64 differences: 8-byte canary (0xDEADBEEF00000000), 8-byte RET/RBP, different offsets. Canary bypass techniques: (1) Canary leaking - Format string '%p%p%p%p' leaks stack memory containing canary copy. Information disclosure (CVE-2014-0160 Heartbleed) reads canary from heap/stack. Once leaked, attacker includes leaked value in overflow payload at correct offset, validation passes. (2) Brute force - 32-bit canary = 4,294,967,296 possibilities. Forking servers (Apache pre-fork) maintain same canary across forks. Crash-resistant servers allow repeated attempts. Byte-by-byte brute force: Try 256 values for canary[0], detect crash vs success, iterate. 256*4 = 1024 attempts average. Impractical for ASLR+PIE (crash changes addresses). (3) Overwrite __stack_chk_guard - Global canary seed stored in .bss section. Arbitrary write vulnerability patches __stack_chk_guard to known value, then overflow uses same value. Requires write-what-where primitive. (4) Exception handler hijacking - Windows SEH (Structured Exception Handling) chain stored on stack above canary. Overflow SEH record, trigger exception (divide by zero), custom handler executes before canary validation. Mitigated by SafeSEH/SEHOP. (5) Precision overwrite - Preserve canary bytes unchanged during overflow. Read canary via secondary bug (UAF, format string), replay exact value in overflow payload. This level's approach. (6) Stack pivoting - Overwrite saved ESP instead of RET. On function return, ESP points to attacker-controlled memory (heap/data section), subsequent POPs load attacker values into registers, final RET jumps to attacker address. Bypasses canary entirely (ESP modified before validation). Real-world CVEs: CVE-2014-9295 NTPd - Buffer overflow in crypto_recv() function. 680-byte buffer, canary at offset 684, RET at 692. Attack: Leak canary via timing side-channel, overflow with preserved canary, overwrite RET to system(). CVE-2018-6789 Exim - base64 decode overflow. 16KB buffer, canary bypass via length miscalculation. Attacker controls exact overflow length, writes canary-preserving payload. CVE-2019-14287 Sudo - Buffer overflow in pwfeedback mode. Overflow preserves canary (read via secondary vuln), overwrites RET to shell. Modern defenses stacking: ASLR randomizes stack base, canary randomizes control flow protection, NX prevents shellcode execution, CFI validates RET targets. Combined effectiveness: Single bypass insufficient. Exploitation requires: Canary leak + ROP chain + ASLR leak + heap spray. This level simulates first step: Precision canary bypass. Exploit engineering: Calculate exact offsets. Buffer starts offset 0 (ESP). Canary offset 16 (ESP+16). EBP offset 20 (ESP+20). RET offset 24 (ESP+24). Payload structure: [16 bytes 'A'] + [4 bytes 0xDEADBEEF] + [4 bytes original_EBP] + [4 bytes 0x08049666]. Total: 28 bytes. Verification: Canary unchanged = no __stack_chk_fail(). EBP preserved = no crash on LEAVE instruction. RET overwritten = EIP redirects to win(). Philosophical: The stack is a record of causality. Function calls create layers of context. Each layer trusts the layer below. Trust is enforced by canaries - sentinels watching for corruption. To exploit trust, you must first understand what is trusted. The canary is not an obstacle. It is a teacher showing you the boundaries of acceptable corruption. Stay within the boundaries, and trust persists. Exceed them surgically, and control flows to you. This is the art of precision exploitation. Calculate. Preserve. Redirect. Three actions. Seven validations. Zero tolerance for error. Welcome to Act 2.",
memoryLayout: [
{ key: 'sortValue1', label: 'BUFFER_SIZE', type: 'int', offset: 0x0 },
{ key: 'sortValue2', label: 'OVERFLOW_LENGTH', type: 'int', offset: 0x4 },
{ key: 'sortValue3', label: 'CANARY_VALUE', type: 'int', offset: 0x10 },
{ key: 'health', label: 'SAVED_EBP', type: 'int', offset: 0x14 },
{ key: 'pointerChainBase', label: 'RETURN_ADDR', type: 'pointer', offset: 0x18 },
{ key: 'debugDetected', label: 'CANARY_INTACT', type: 'bool', offset: 0x800 },
{ key: 'isAdmin', label: 'EXPLOIT_SUCCESS', type: 'bool', offset: 0x30 }
],
initialState: {
sortValue1: 0,
sortValue2: 0,
sortValue3: 0,
health: 0xBFFFDC00,
pointerChainBase: '00400500',
debugDetected: true,
isAdmin: false
},
update: (s) => {
const bufferSize = s.sortValue1 || 0;
const overflowLen = s.sortValue2 || 0;
const canaryVal = s.sortValue3 || 0;
const ebpVal = s.health || 0;
const retAddr = (s.pointerChainBase || '00400500').toLowerCase();
const bufferValid = bufferSize === 16;
const overflowValid = overflowLen === 28;
const canaryPreserved = canaryVal === 0xDEADBEEF;
const ebpPreserved = ebpVal === 0xBFFFDC00;
const retOverwritten = retAddr === '08049666' || retAddr === '8049666';
if (canaryPreserved && bufferValid && overflowValid) {
const updates: any = { debugDetected: false };
if (ebpPreserved && retOverwritten) {
updates.isAdmin = true;
}
return updates;
}
if (canaryVal !== 0 && canaryVal !== 0xDEADBEEF) {
return {
debugDetected: true,
isAdmin: false
};
}
return {};
},
platforms: [{ id: 'p1', x: 0, y: 280, width: 800, height: 40, type: 'static' }]
};