From 240b291b9f60228d2aab2b46e2b35ceff5074b49 Mon Sep 17 00:00:00 2001 From: Oleg Lazari Date: Mon, 20 Apr 2026 21:10:12 -0400 Subject: [PATCH] imagine rit stuff --- imagine-rit-adminwriteup.md | 72 ++++++++++++++ src/app/imagine-rit/[id]/page.tsx | 11 +++ src/app/imagine-rit/layout.tsx | 25 +++++ src/app/imagine-rit/page.tsx | 97 +++++++++++++++++++ src/components/AppShell/ImagineRitSidebar.tsx | 56 +++++++++++ .../panels/InputPanel/inputs/TextHexInput.tsx | 6 +- src/exercises/imagine-rit/baby-rop.ts | 57 +++++++++++ src/exercises/imagine-rit/index.ts | 14 +++ src/exercises/imagine-rit/rit-01-stack.ts | 40 ++++++++ src/exercises/imagine-rit/rit-02-overflow.ts | 39 ++++++++ src/exercises/imagine-rit/rit-03-hijack.ts | 41 ++++++++ src/exercises/imagine-rit/rit-04-aslr.ts | 45 +++++++++ src/exercises/registry.ts | 2 + 13 files changed, 502 insertions(+), 3 deletions(-) create mode 100644 imagine-rit-adminwriteup.md create mode 100644 src/app/imagine-rit/[id]/page.tsx create mode 100644 src/app/imagine-rit/layout.tsx create mode 100644 src/app/imagine-rit/page.tsx create mode 100644 src/components/AppShell/ImagineRitSidebar.tsx create mode 100644 src/exercises/imagine-rit/baby-rop.ts create mode 100644 src/exercises/imagine-rit/index.ts create mode 100644 src/exercises/imagine-rit/rit-01-stack.ts create mode 100644 src/exercises/imagine-rit/rit-02-overflow.ts create mode 100644 src/exercises/imagine-rit/rit-03-hijack.ts create mode 100644 src/exercises/imagine-rit/rit-04-aslr.ts diff --git a/imagine-rit-adminwriteup.md b/imagine-rit-adminwriteup.md new file mode 100644 index 0000000..d038f42 --- /dev/null +++ b/imagine-rit-adminwriteup.md @@ -0,0 +1,72 @@ +# Imagine RIT — Admin Writeup + +Solutions for all 5 exercises in the Imagine RIT workshop. + +--- + +## Exercise 01: The Stack Frame + +No input needed. Just click **Step** 4 times to watch the stack frame build up. + +--- + +## Exercise 02: The Overflow + +Send more than 20 bytes to overwrite the go-back address with garbage and crash the program. + +``` +41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 +``` + +(24 bytes of 0x41 — fills the 16-byte buffer, overwrites 4-byte bookmark, trashes the go-back address) + +--- + +## Exercise 03: Hijack Execution + +Overflow the buffer and overwrite the go-back address with win() at `0x08048150`. + +``` +41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 42 42 42 42 50 81 04 08 +``` + +Breakdown: +- `41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41` — 16 bytes buffer padding +- `42 42 42 42` — 4 bytes overwrite the bookmark (junk) +- `50 81 04 08` — win() address `0x08048150` in little-endian + +--- + +## Exercise 04: Randomized Addresses + +Addresses change every run. Read the leaked main() address from the log, add `0x150` to get win(). + +Example: if main is leaked as `0x08248000`, then win = `0x08248000 + 0x150 = 0x08248150`. + +``` +41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 42 42 42 42 [win address in little-endian] +``` + +Use the hex calculator in the exercise to compute the address, then enter it backwards (least significant byte first). + +--- + +## Exercise 05: Baby's First ROP + +One gadget at `0x08048300` that does: `pop eax; pop ebx; mov [ebx], eax; ret` + +Goal: write `0xdeadbeef` into `flag_check` at `0x0804a040`, then jump to win() at `0x08048150`. + +``` +41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 42 42 42 42 00 83 04 08 ef be ad de 40 a0 04 08 50 81 04 08 +``` + +Breakdown: +- `41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41` — 16 bytes buffer padding +- `42 42 42 42` — 4 bytes overwrite the bookmark +- `00 83 04 08` — gadget address `0x08048300` (overwrites go-back address) +- `ef be ad de` — `0xdeadbeef` (gadget pops this into eax) +- `40 a0 04 08` — `0x0804a040` (gadget pops this into ebx) +- `50 81 04 08` — win() address `0x08048150` (gadget's `ret` jumps here after writing eax to [ebx]) + +The gadget writes 0xdeadbeef to flag_check, then returns into win(), which sees the correct value and prints the flag. diff --git a/src/app/imagine-rit/[id]/page.tsx b/src/app/imagine-rit/[id]/page.tsx new file mode 100644 index 0000000..a7fa6db --- /dev/null +++ b/src/app/imagine-rit/[id]/page.tsx @@ -0,0 +1,11 @@ +import ExercisePageClient from '@/app/exercise/[id]/ExercisePageClient'; + +const IMAGINE_RIT_IDS = ['rit-01', 'rit-02', 'rit-03', 'rit-04', 'rit-rop']; + +export function generateStaticParams() { + return IMAGINE_RIT_IDS.map((id) => ({ id })); +} + +export default function ImagineRitExercisePage({ params }: { params: Promise<{ id: string }> }) { + return ; +} diff --git a/src/app/imagine-rit/layout.tsx b/src/app/imagine-rit/layout.tsx new file mode 100644 index 0000000..816f5da --- /dev/null +++ b/src/app/imagine-rit/layout.tsx @@ -0,0 +1,25 @@ +'use client'; + +import { ExerciseContextProvider } from '@/state/ExerciseContext'; +import ImagineRitSidebar from '@/components/AppShell/ImagineRitSidebar'; +import SuccessBanner from '@/components/shared/SuccessBanner'; + +export default function ImagineRitLayout({ children }: { children: React.ReactNode }) { + return ( + +
+
+

0xVRIG

+
+
+
+ +
+ {children} +
+
+ +
+
+ ); +} diff --git a/src/app/imagine-rit/page.tsx b/src/app/imagine-rit/page.tsx new file mode 100644 index 0000000..afefb96 --- /dev/null +++ b/src/app/imagine-rit/page.tsx @@ -0,0 +1,97 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { loadProgress } from '@/state/persistence'; + +const EXERCISES = [ + { id: 'rit-01', title: '01: The Stack Frame', desc: 'Watch how the computer organizes memory — like a stack of sticky notes.' }, + { id: 'rit-02', title: '02: The Overflow', desc: 'Type too much and crash the program. Yes, it\'s that easy.' }, + { id: 'rit-03', title: '03: Hijack Execution', desc: 'Make the program run a secret function it was never supposed to call.' }, + { id: 'rit-04', title: '04: Randomized Addresses', desc: 'The computer scrambles its memory — use a leaked hint to beat it.' }, + { id: 'rit-rop', title: '05: Baby\'s First ROP', desc: 'Bypass the final defense by jumping to code that already exists.' }, +]; + +export default function ImagineRitPage() { + const router = useRouter(); + const [completed, setCompleted] = useState>(new Set()); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setCompleted(loadProgress()); + setMounted(true); + }, []); + + if (!mounted) return null; + + const doneCount = EXERCISES.filter(ex => completed.has(ex.id)).length; + + return ( +
+

+ 0xVRIG — Imagine RIT +

+

+ Learn how hackers exploit programs — in 5 hands-on exercises +

+

+ No coding experience needed. Each exercise builds on the last. +

+ +
+ Progress: {doneCount}/{EXERCISES.length}{' '} + + {'█'.repeat(doneCount)}{'░'.repeat(EXERCISES.length - doneCount)} + +
+ +
+ {EXERCISES.map((ex, i) => { + const done = completed.has(ex.id); + const isNext = !done && EXERCISES.slice(0, i).every(e => completed.has(e.id)); + return ( +
router.push(`/imagine-rit/${ex.id}`)} + style={{ + padding: '0.75rem 1rem', + border: `1px solid ${isNext ? 'var(--green)' : 'var(--panel-border)'}`, + cursor: 'pointer', + opacity: done ? 0.6 : 1, + }} + > +
+ {done && } + {isNext && } + {ex.title} +
+
{ex.desc}
+
+ ); + })} +
+ + {doneCount === EXERCISES.length && ( +
+
+ Workshop Complete! +
+
+ You learned how buffer overflows work, hijacked program execution, bypassed ASLR, and built a ROP chain. Nice work! +
+
+ )} +
+ ); +} diff --git a/src/components/AppShell/ImagineRitSidebar.tsx b/src/components/AppShell/ImagineRitSidebar.tsx new file mode 100644 index 0000000..a933081 --- /dev/null +++ b/src/components/AppShell/ImagineRitSidebar.tsx @@ -0,0 +1,56 @@ +'use client'; + +import { useRouter, usePathname } from 'next/navigation'; +import { useExerciseContext } from '@/state/ExerciseContext'; + +const IMAGINE_RIT_EXERCISES = [ + { id: 'rit-01', title: 'The Stack Frame' }, + { id: 'rit-02', title: 'The Overflow' }, + { id: 'rit-03', title: 'Hijack Execution' }, + { id: 'rit-04', title: 'Randomized Addresses' }, + { id: 'rit-rop', title: "Baby's First ROP" }, +]; + +export default function ImagineRitSidebar() { + const router = useRouter(); + const pathname = usePathname(); + const { state } = useExerciseContext(); + + const activeId = pathname?.split('/imagine-rit/')[1] ?? ''; + + return ( + + ); +} diff --git a/src/components/panels/InputPanel/inputs/TextHexInput.tsx b/src/components/panels/InputPanel/inputs/TextHexInput.tsx index 547474e..e225081 100644 --- a/src/components/panels/InputPanel/inputs/TextHexInput.tsx +++ b/src/components/panels/InputPanel/inputs/TextHexInput.tsx @@ -8,7 +8,7 @@ import { generateExecSteps, execCurrentStep, ExecStep } from '@/engine/execution export default function TextHexInput() { const { state, dispatch, stackSim, currentExercise } = useExerciseContext(); const [payload, setPayload] = useState(''); - const [mode, setMode] = useState<'text' | 'hex'>(state.inputMode); + const [mode, setMode] = useState<'text' | 'hex'>(currentExercise?.mode === 'input-hex' ? 'hex' : state.inputMode); const [execSteps, setExecSteps] = useState(null); const [execIndex, setExecIndex] = useState(0); const [ropState] = useState<{ ropEax?: number; ropEbx?: number; ropFlagValue?: number }>({}); @@ -151,7 +151,7 @@ export default function TextHexInput() { return (
- {!isTextMode && ( + {isTextMode && (