-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlevel34.ts
More file actions
66 lines (59 loc) · 8.67 KB
/
level34.ts
File metadata and controls
66 lines (59 loc) · 8.67 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
import { Level } from './types';
export const level34: Level = {
id: 34,
title: "Ret2Libc: The Collective Unconscious",
description: "DEP/NX enabled - stack and heap are non-executable (W^X policy). Shellcode injection impossible. Enter return-to-libc: instead of injecting code, redirect execution to existing library functions. libc.so (C standard library) loaded at base address 0xB7E00000 (randomized per-process, simulated ASLR). Critical functions: system() at LIBC_BASE+0x40190 (0xB7E40190), executes shell commands. /bin/sh string at LIBC_BASE+0x160A24 (0xB7F60A24), argument for system(). Stack overflow still possible - overwrite SAVED_RET_ADDR with system() address. Stack frame post-overflow: FAKE_RET (exit gracefully, use 0xAAAAAAAA) | ARG1_PTR (pointer to '/bin/sh' string). CPU executes: RET → POP system_addr into EIP → CALL system → POP fake_ret (return address for system) → POP arg1_ptr → execve('/bin/sh'). Result: root shell without writing a single byte of custom code. 2003-era technique, foundation for modern ROP.",
requiredSkill: "ROP / Ret2Libc",
objective: (s) => {
const libcBaseValid = s.libcBase === 'B7E00000' || s.libcBase === '0xB7E00000';
const systemAddrValid = s.sortValue1 === 0xB7E40190;
const binshAddrValid = s.sortValue2 === 0xB7F60A24;
const retAddrValid = (s.eip || '').toLowerCase().includes('b7e40190');
const chainBuilt = s.activeROPChain.length >= 2 &&
s.activeROPChain.some(addr => addr.toLowerCase().includes('b7e40190')) &&
s.activeROPChain.some(addr => addr.toLowerCase().includes('b7f60a24'));
const nxEnabled = s.debugDetected === true;
const exploitSuccess = s.isAdmin === true;
return libcBaseValid && systemAddrValid && binshAddrValid && retAddrValid && chainBuilt && nxEnabled && exploitSuccess;
},
hint: "Six-stage ret2libc bypass of NX protection. Stage 1: Read LIBC_BASE address (libcBase, randomized per-process, base: 0xB7E00000). Stage 2: Calculate SYSTEM_ADDR = LIBC_BASE + 0x40190 (sortValue1 decimal, system() function offset, result: 3085173136). Stage 3: Calculate BINSH_ADDR = LIBC_BASE + 0x160A24 (sortValue2 decimal, '/bin/sh' string offset, result: 3086354980). Stage 4: Build ROP_CHAIN (activeROPChain array, add both addresses as hex strings: '0xB7E40190', '0xB7F60A24'). Stage 5: Redirect RETURN_ADDR=0xB7E40190 (eip hex string, overflow saved RET to point to system()). Stage 6: Verify NX_ENABLED=true and EXPLOIT_SUCCESS=true (auto-set when stack frame correct). Use Memory Scanner to read LIBC_BASE, then ExploitWorkshop/MemoryScanner to set addresses. Convert hex to decimal for sortValue fields. Real exploit stack layout: [BUFFER_OVERFLOW_JUNK] + [0xB7E40190 system()] + [0xAAAAAAAA fake_ret] + [0xB7F60A24 arg1='/bin/sh'].",
tutorPersona: "Solar Designer/Nergal: When they take away your pen, use their library. History: 1997 - Solar Designer publishes 'Getting around non-executable stack (and fix)' on Bugtraq. First public documentation of return-to-libc attack. Context: Openwall Linux kernel patch (1997) introduced non-executable stack. Solar Designer immediately demonstrated bypass. Concept: Stack overflow still possible, but instead of shellcode injection (JMP ESP, execute opcodes from stack), attacker chains existing functions. Example: Overflow buffer → Overwrite RET with address of system() in libc.so → Setup stack to mimic function call: [system_addr][fake_return][arg1_ptr] → When vulnerable function executes RET, CPU jumps to system() → system() reads arg1_ptr from stack (ESP+8) → Executes command pointed to by arg1_ptr → If arg1_ptr points to '/bin/sh' string in libc.so, spawns shell. 2000 - Nergal 'The advanced return-into-lib(c) exploits (PaX case study)' (Phrack 58). Extends ret2libc to chain multiple functions. PaX introduced ASLR and NX. Nergal showed: Chain strcpy() to write '/bin/sh' to known address → Chain setuid(0) to escalate privileges → Chain system() to execute. Demonstrates function chaining precursor to ROP. 2001 - ret2libc becomes standard technique against OpenBSD, PaX, ExecShield (RedHat NX). 2003 - Windows XP SP2 introduces DEP (Data Execution Prevention), hardware NX via PAE/NX bit. ret2libc adapted to Windows: ROP to VirtualProtect() (mark stack RWX) → Execute shellcode, or chain WinExec()/system(). Modern evolution: ASLR randomizes library addresses (challenge: info leak required to find system()). ret2plt (use PLT/GOT for library resolution). ROP (Return Oriented Programming, 2007) - generalized ret2libc using gadgets (instruction sequences ending in RET). Gadget anatomy: POP REG; POP REG; RET (load values into registers), XOR EAX, EAX; RET (zero register), INT 0x80; RET (syscall). Chain gadgets to build arbitrary computation from existing code. Ret2libc Technical Deep Dive (x86 32-bit Linux): Vulnerable code: void vuln() { char buf[64]; gets(buf); }. Normal execution: call vuln → PUSH ret_addr → vuln prologue → SUB ESP, 64 (allocate buffer) → gets(buf) reads input → LEAVE (MOV ESP, EBP; POP EBP) → RET (POP EIP, jump to ret_addr) → continue main(). Exploit: Payload = 'A'×68 (64 buffer + 4 saved EBP) + p32(system_addr) + p32(fake_ret) + p32(binsh_ptr). After gets(buf) overflow: Stack layout (HIGH to LOW): [main's stack] [0xB7F60A24 →'/bin/sh'] [0xAAAAAAAA fake_ret] [0xB7E40190 →system()] [AAAA... overflow junk] [ESP]. Execution: vuln epilogue: LEAVE → RET. RET instruction: POP [ESP] into EIP → EIP = 0xB7E40190 (system function) → ESP += 4 (now points to fake_ret). CPU jumps to system(): system prologue: PUSH EBP; MOV EBP, ESP → Reads arguments: [ESP+4] = fake_ret (system's return address), [ESP+8] = 0xB7F60A24 (arg1, command string). system() dereferences arg1: Reads '/bin/sh' from 0xB7F60A24 → Calls execve('/bin/sh', ...) → Shell spawns with vuln's privileges (if SUID root, get root shell). Finding addresses: strings -a -tx /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh → offset 0x160A24. nm -D /lib/i386-linux-gnu/libc.so.6 | grep system → offset 0x00040190. Read LIBC_BASE from /proc/self/maps or Memory Scanner → Add offsets. Bypassing ASLR (modern systems): Requires information leak (e.g., format string vulnerability to read GOT entry, stack leak to read saved libc pointer). Example: printf(user_input) → Leak __libc_start_main address from stack → Calculate LIBC_BASE = leaked_addr - known_offset → Calculate system/binsh dynamically. Limitations: NX alone insufficient (ret2libc works). NX + ASLR stronger (need info leak). NX + ASLR + RELRO (GOT hardening) + Stack Canary = modern baseline. Modern defenses require ROP + info leak + canary bypass. This level: NX enabled, ASLR disabled (static libc base 0xB7E00000). Simulates 2003-2007 era Linux (pre-ASLR by default). Ubuntu 8.04-10.04, Debian Etch/Lenny timeframe. DEP introduced but not randomized. Educational baseline for understanding libc function reuse before full ROP chains (Level 37+).",
memoryLayout: [
{ key: 'libcBase', label: 'LIBC_BASE', type: 'pointer', offset: 0x0, isStatic: true },
{ key: 'sortValue1', label: 'SYSTEM_ADDR', type: 'int', offset: 0x4 },
{ key: 'sortValue2', label: 'BINSH_ADDR', type: 'int', offset: 0x8 },
{ key: 'eip', label: 'RETURN_ADDR', type: 'pointer', offset: 0x80 },
{ key: 'activeROPChain', label: 'ROP_CHAIN', type: 'array', offset: 0x300 },
{ key: 'debugDetected', label: 'NX_ENABLED', type: 'bool', offset: 0x800 },
{ key: 'isAdmin', label: 'EXPLOIT_SUCCESS', type: 'bool', offset: 0x30 }
],
initialState: {
libcBase: 'B7E00000',
sortValue1: 0,
sortValue2: 0,
eip: '00000000',
activeROPChain: [],
debugDetected: true,
isAdmin: false
},
update: (s) => {
const libcBase = parseInt(s.libcBase?.replace('0x', '') || 'B7E00000', 16);
const systemOffset = 0x40190;
const binshOffset = 0x160A24;
const expectedSystemAddr = libcBase + systemOffset;
const expectedBinshAddr = libcBase + binshOffset;
const systemAddrValid = s.sortValue1 === expectedSystemAddr;
const binshAddrValid = s.sortValue2 === expectedBinshAddr;
const retAddr = (s.eip || '00000000').toLowerCase().replace('0x', '');
const retOverwritten = retAddr === expectedSystemAddr.toString(16).toLowerCase();
const chainValid = s.activeROPChain.length >= 2 &&
s.activeROPChain.some(addr => addr.toLowerCase().includes(expectedSystemAddr.toString(16))) &&
s.activeROPChain.some(addr => addr.toLowerCase().includes(expectedBinshAddr.toString(16)));
const updates: any = {};
if (systemAddrValid && binshAddrValid && retOverwritten && chainValid) {
updates.isAdmin = true;
}
return updates;
},
platforms: [{ id: 'p1', x: 0, y: 280, width: 800, height: 40, type: 'static' }]
};