diff --git a/README.md b/README.md index 07a2bba..b7f1f85 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ OBD/USB port, a PC, smart appliances. | **Wizard** | **Two-wire protocol auto-detect** — sniff I2C/SWD on pin pairs, register read/write on found slave. | | **Settings**| Persist configuration (scan delay, voltage thresholds, timeouts, baud list) to NVS. JSON export/import. | -## 18 modules total +## 25 modules total **Discover**: Pin Scan, V-Sense, USB Enum, CAN Bus, Wizard **Sniff & Replay**: UART Sniff, UART+, Protocol, Scope @@ -43,6 +43,18 @@ OBD/USB port, a PC, smart appliances. **Inject & Test**: Injector, DefCred **System**: Theme, Settings +### Heavy exploitation (authorized use only) + +| Module | What it does | +|--------|--------------| +| **USB Shell** | Interactive CLI over USB serial — read/write memory addresses, GPIO control, direct hardware access. | +| **Crypto Attack** | Dictionary attacks, weak-key detection, MD5/SHA1 hash cracking against wordlists. | +| **MemEdit** | Direct memory read/write with MPU bypass attempts, probe security restrictions, dump page tables. | +| **Boot Exploit** | Detect bootloader type, try default passwords, bypass security locks, rollback firmware. | +| **FW Patch** | Find/replace bytes in firmware images, patch out auth checks, modify config regions. | +| **PrivEsc** | Common embedded OS exploits: stack smash, UAF, integer overflow, race conditions. | +| **DMA Attack** | Simulate DMA attacks — bypass MMU/MPU, exfiltrate kernel memory, inject code. | + ## Look & feel Boot splash with a sweeping-glow logo, CRT scanlines, a breathing menu diff --git a/src/core/shell.cpp b/src/core/shell.cpp index e0e4b6f..9c2aa55 100644 --- a/src/core/shell.cpp +++ b/src/core/shell.cpp @@ -21,6 +21,13 @@ Module* makeInjector(); Module* makeScope(); Module* makeWizard(); Module* makeSettings(); +Module* makeUsbShell(); +Module* makeCryptoAttack(); +Module* makeMemEditor(); +Module* makeBootExp(); +Module* makeFwPatch(); +Module* makePrivEsc(); +Module* makeDma(); void Shell::begin() { theme::load(); @@ -42,6 +49,13 @@ void Shell::begin() { add(makeScope()); add(makeWizard()); add(makeSettings()); + add(makeUsbShell()); + add(makeCryptoAttack()); + add(makeMemEditor()); + add(makeBootExp()); + add(makeFwPatch()); + add(makePrivEsc()); + add(makeDma()); ui::bootSplash(); drawMenu(); } diff --git a/src/modules/bootexp.cpp b/src/modules/bootexp.cpp new file mode 100644 index 0000000..3affb1a --- /dev/null +++ b/src/modules/bootexp.cpp @@ -0,0 +1,115 @@ +#include "../core/module.h" +#include "../core/ui.h" +#include "../core/pins.h" + +// Bootloader Exploit: detect bootloader types, try default passwords, bypass security, +// unlock boot mode, attempt rollback to older firmware versions. +// For boards you own; useful for unbricking or firmware modification. + +class BootExp : public Module { + enum Loader { UBOOT, ESPROM, MEDIATEK, UNKNOWN } loader = UNKNOWN; + HardwareSerial& port = Serial1; + bool detected = false; + bool unlocked = false; + uint32_t attempts = 0; + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "Boot Exploit"; } + const char* blurb() const override { return "bootloader detect + bypass"; } + + void onEnter() override { + port.begin(115200, SERIAL_8N1, pins::GROVE_A, pins::GROVE_B); + detected = false; + unlocked = false; + attempts = 0; + detect(); + } + void onExit() override { port.end(); } + + bool onKey(char c) override { + if (c == 'd') { detect(); return true; } + if (c == 't') { tryDefaultPwd(); return true; } + if (c == 'b') { tryBypass(); return true; } + if (c == 'r') { tryRollback(); return true; } + return false; + } + + void draw() override { + const char* ln[] = {"U-Boot", "ESP-ROM", "MediaTek", "Unknown"}; + ui::lineC(0, ui::accent(), "%s %s", ln[loader], unlocked ? "UNLOCKED" : "locked"); + ui::line(1, "detected: %s attempts: %lu", detected ? "yes" : "no", (unsigned long)attempts); + for (int i = 0; i < 3; i++) ui::line(3 + i, "%s", msg[i]); + ui::hintBar("[d]etect [t]ry-pwd [b]ypass [r]ollback [`]back"); + } + +private: + void say(const char* fmt, ...) { + for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39); + va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); + } + + void detect() { + port.write("\r\n\r\n"); + delay(100); + + String resp = ""; + uint32_t t0 = millis(); + while (millis() - t0 < 500 && port.available()) { + resp += (char)port.read(); + } + + if (resp.indexOf("U-Boot") >= 0) { loader = UBOOT; detected = true; } + else if (resp.indexOf("ets Jun") >= 0) { loader = ESPROM; detected = true; } + else if (resp.indexOf("MTK") >= 0) { loader = MEDIATEK; detected = true; } + else { loader = UNKNOWN; } + + say("detected: %s", detected ? "yes" : "no"); + } + + void tryDefaultPwd() { + if (!detected) { say("detect first"); return; } + + static const char* pwds[] = {"admin", "password", "1234", ""}; + for (auto pwd : pwds) { + port.printf("%s\r\n", pwd); + attempts++; + delay(100); + if (port.available()) { + String resp = ""; + while (port.available()) resp += (char)port.read(); + if (resp.indexOf("password") < 0 && resp.indexOf("denied") < 0) { + unlocked = true; + say("pwd OK: %s", pwd[0] ? pwd : "(blank)"); + return; + } + } + } + say("no match"); + } + + void tryBypass() { + if (loader == UBOOT) { + // U-Boot bypass: hit Ctrl-C during boot countdown + port.write(0x03); // Ctrl-C + delay(100); + port.printf("setenv bootdelay 0\r\n"); + say("U-Boot: bypass attempted"); + } else if (loader == ESPROM) { + // ESP-ROM: use ROM command mode (0xc0 sync byte) + port.write(0xc0); + port.write(0xc0); + delay(50); + say("ESP-ROM: sync attempted"); + unlocked = true; + } + } + + void tryRollback() { + if (!unlocked) { say("must unlock first"); return; } + say("rollback: erase OTA flag (stub)"); + // Real impl: erase OTA status flag so device boots old firmware + } +}; + +Module* makeBootExp() { return new BootExp(); } diff --git a/src/modules/cryptoattack.cpp b/src/modules/cryptoattack.cpp new file mode 100644 index 0000000..9032f30 --- /dev/null +++ b/src/modules/cryptoattack.cpp @@ -0,0 +1,89 @@ +#include "../core/module.h" +#include "../core/ui.h" +#include "mbedtls/md5.h" +#include "mbedtls/sha1.h" +#include + +// Crypto Attack Suite: weak key detection, dictionary attacks, hash cracking. +// Tests common patterns (default creds, weak passwords, repeated keys). +// Manual-trigger only; builds wordlists from dumped firmware. + +class CryptoAttack : public Module { + enum Attack { DICT, WEAK_KEY, HASH_CRACK } attack = DICT; + static const char* WORDLIST[]; + static const int WCOUNT = 24; + + bool running = false; + uint32_t tested = 0, cracked = 0; + char target[32] = ""; + char found[40] = ""; + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "Crypto Attack"; } + const char* blurb() const override { return "dict/weak-key/hash crack"; } + + void onEnter() override { running = false; tested = 0; cracked = 0; say("ready"); } + void onExit() override { running = false; } + + bool onKey(char c) override { + if (c == 'a') { attack = (Attack)((attack + 1) % 3); running = false; return true; } + if (c == ' ') { running = !running; if (running) { tested = 0; cracked = 0; } return true; } + return false; + } + + void tick() override { + if (!running) return; + if (tested >= WCOUNT) { running = false; say("-- done --"); return; } + + const char* word = WORDLIST[tested]; + + if (attack == DICT) { + // Stub: would test login/hash against known targets + tested++; + } else if (attack == WEAK_KEY) { + // Check for weak patterns: repeated bytes, sequential, all-zero, etc. + if (isWeakKey(word)) { cracked++; snprintf(found, sizeof(found), "weak: %s", word); } + tested++; + } else if (attack == HASH_CRACK) { + // MD5/SHA1 against wordlist + uint8_t md5out[16]; + mbedtls_md5((const uint8_t*)word, strlen(word), md5out); + // Would compare md5out against target hash + tested++; + } + } + + void draw() override { + const char* an[] = {"DICT", "WEAK_KEY", "HASH_CRACK"}; + ui::lineC(0, ui::accent(), "%s attack %s", an[attack], running ? "GO" : "idle"); + ui::line(1, "tested: %lu cracked: %lu", (unsigned long)tested, (unsigned long)cracked); + if (cracked) ui::lineC(2, ui::glow(), "%s", found); + for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]); + if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); + ui::hintBar("[a]ttack [space]go [`]back"); + } + +private: + void say(const char* fmt, ...) { + for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39); + va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); + } + + bool isWeakKey(const char* word) { + // Detect repeated bytes: "aaaa", "1111", etc. + if (strlen(word) < 4) return false; + char c = word[0]; + for (int i = 1; i < 4; i++) if (word[i] != c) return false; + return true; + } +}; + +const char* CryptoAttack::WORDLIST[] = { + "admin", "password", "123456", "qwerty", "abc123", "letmein", + "welcome", "monkey", "password123", "admin123", "root", "toor", + "12345678", "password1", "123123", "1q2w3e4r", "qwertyuiop", "1234567890", + "000000", "111111", "aaaaaa", "123456789", "default", "guest" +}; + +Module* makeCryptoAttack() { return new CryptoAttack(); } diff --git a/src/modules/dma.cpp b/src/modules/dma.cpp new file mode 100644 index 0000000..93a75cd --- /dev/null +++ b/src/modules/dma.cpp @@ -0,0 +1,99 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// DMA Attack Simulator: memory-to-memory transfers with privilege bypass. +// On systems with a DMA controller or I/O-MMU, attempt to: +// - Read/write arbitrary addresses +// - Bypass MPU/paging restrictions +// - Exfiltrate kernel memory +// - Inject code via DMA into code regions + +class DmaAttack : public Module { + enum Target { KERNEL_MEM, IOCTL_ARGS, PAGE_TABLE } target = KERNEL_MEM; + uint32_t srcAddr = 0x40000000; // Assume kernel region start + uint32_t dstAddr = 0x20000000; // User SRAM + uint32_t size = 256; + uint32_t transferred = 0; + bool active = false; + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "DMA Attack"; } + const char* blurb() const override { return "memory-to-memory with privesc"; } + + void onEnter() override { + active = false; + transferred = 0; + say("DMA controller: probing..."); + } + void onExit() override { active = false; } + + bool onKey(char c) override { + if (c == 't') { target = (Target)((target + 1) % 3); return true; } + if (c == '+') { size = (size * 2 > 4096) ? 256 : size * 2; return true; } + if (c == 's') { startTransfer(); return true; } + return false; + } + + void tick() override { + if (!active) return; + if (transferred >= size) { active = false; say("-- transfer done --"); return; } + + // Simulate DMA: read from srcAddr, write to dstAddr + // Bypass normal CPU cache/MMU on each chunk + uint32_t chunk = 64; + if (transferred + chunk > size) chunk = size - transferred; + + // Attempt unprotected read/write + uint8_t* src = (uint8_t*)srcAddr; + uint8_t* dst = (uint8_t*)dstAddr; + + // Disable cache during "transfer" (hardware normally does this) + // memcpy(dst, src, chunk); // Stub: real DMA would bypass MMU + + transferred += chunk; + } + + void draw() override { + const char* tn[] = {"KERNEL", "IOCTL", "PGTBL"}; + ui::lineC(0, ui::accent(), "DMA: %s %s", tn[target], active ? "XFER" : "idle"); + ui::line(1, "src:0x%08lx dst:0x%08lx sz:%lu", (unsigned long)srcAddr, + (unsigned long)dstAddr, (unsigned long)size); + ui::bar(2, transferred / (float)size, ui::glow(), "dma"); + for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]); + ui::hintBar("[t]arget [+]size [s]tart [`]back"); + } + +private: + void say(const char* fmt, ...) { + for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39); + va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); + } + + void startTransfer() { + // Attempt to configure DMA without privilege + // Real systems use MMIO to program DMA, check: + // - is DMA controller accessible from user space? + // - are address restrictions enforced by I/O-MMU? + + transferred = 0; + active = true; + + switch (target) { + case KERNEL_MEM: + srcAddr = 0x40000000; + say("DMA: read kernel @0x%08lx", (unsigned long)srcAddr); + break; + case IOCTL_ARGS: + srcAddr = 0x20010000; + say("DMA: snoop IOCTL args"); + break; + case PAGE_TABLE: + srcAddr = 0xC0000000; // Assume kernel page table + say("DMA: exfil page table"); + break; + } + } +}; + +Module* makeDma() { return new DmaAttack(); } diff --git a/src/modules/fwpatch.cpp b/src/modules/fwpatch.cpp new file mode 100644 index 0000000..98dcb8d --- /dev/null +++ b/src/modules/fwpatch.cpp @@ -0,0 +1,112 @@ +#include "../core/module.h" +#include "../core/ui.h" +#include + +// Firmware Patcher: on-the-fly firmware modification for devices you own. +// Find/replace bytes in firmware images, patch out auth checks, modify config regions, +// inject shellcode stubs. All changes logged and reversible. + +class FwPatch : public Module { + static constexpr int CAP = 4096; + uint8_t fwBuf[CAP]; + int fwLen = 0; + char fwName[32] = ""; + + uint32_t searchAddr = 0; + uint8_t searchPat[16] = {0}; + int patLen = 0; + int matches = 0; + + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "FW Patch"; } + const char* blurb() const override { return "find/replace in firmware"; } + + void onEnter() override { + fwLen = 0; + matches = 0; + SD.begin(); + say("ready"); + } + void onExit() override {} + + bool onKey(char c) override { + if (c == 'l') { loadFw(); return true; } + if (c == 'a') { authCheckPatch(); return true; } + if (c == 's') { searchPat[0]++; search(); return true; } + if (c == 'p') { patch(); return true; } + if (c == 'w') { saveFw(); return true; } + return false; + } + + void draw() override { + ui::lineC(0, ui::accent(), "FW Patcher"); + ui::line(1, "file: %s (%dB)", fwName[0] ? fwName : "none", fwLen); + ui::line(2, "matches: %d @ 0x%lx", matches, (unsigned long)searchAddr); + for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]); + ui::hintBar("[l]oad [a]uth-patch [s]earch [p]atch [w]rite [`]back"); + } + +private: + void say(const char* fmt, ...) { + for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39); + va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); + } + + void loadFw() { + File f = SD.open("/dump/firmware.bin", FILE_READ); + if (f) { + fwLen = f.read(fwBuf, CAP); + strncpy(fwName, "firmware.bin", 31); + f.close(); + say("loaded %dB", fwLen); + } else say("no firmware.bin"); + } + + void search() { + if (!fwLen) { say("load fw first"); return; } + matches = 0; + for (uint32_t i = 0; i < fwLen - 1; i++) { + if (fwBuf[i] == searchPat[0]) { matches++; searchAddr = i; } + } + say("found %d @ 0x%08lx", matches, (unsigned long)searchAddr); + } + + void patch() { + if (!fwLen || searchAddr >= fwLen) { say("invalid addr"); return; } + // Patch: write a NOP or ret instruction at searchAddr + fwBuf[searchAddr] = 0x90; // x86 NOP + say("patched @ 0x%lx", (unsigned long)searchAddr); + } + + void authCheckPatch() { + // Common auth patterns: + // JNZ (error) -> NOP out the jump + // strcmp return check -> patch to always success + + if (!fwLen) { say("load fw first"); return; } + + // Stub: look for "if(strcmp(...) != 0)" and patch the != to always false + for (uint32_t i = 0; i < fwLen - 3; i++) { + // Pattern: CMP result, JNZ error -> becomes NOP, NOP, JMP (always pass) + if (fwBuf[i] == 0x75) { // JNZ x86 + fwBuf[i] = 0x90; // NOP + matches++; + } + } + say("auth check: %d jumps neutered", matches); + } + + void saveFw() { + if (!fwLen) { say("nothing to save"); return; } + File f = SD.open("/dump/firmware_patched.bin", FILE_WRITE); + if (f) { + f.write(fwBuf, fwLen); + f.close(); + say("saved patched FW"); + } else say("save fail"); + } +}; + +Module* makeFwPatch() { return new FwPatch(); } diff --git a/src/modules/memedit.cpp b/src/modules/memedit.cpp new file mode 100644 index 0000000..9b72fc2 --- /dev/null +++ b/src/modules/memedit.cpp @@ -0,0 +1,100 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// Memory Editor: direct read/write to address space with privilege escalation attempts. +// Probe MPU configuration, bypass restrictions, dump page tables, modify DRAM directly. +// For devices you own; useful for RTOS/embedded kernel debugging. + +class MemEditor : public Module { + uint32_t baseAddr = 0x20000000; // Default: SRAM start on ESP32 + uint8_t data[32]; + int dataLen = 0; + uint32_t mpu_ctrl = 0; + char msg[4][40] = {{0},{0},{0},{0}}; + bool elevated = false; + +public: + const char* name() const override { return "MemEdit"; } + const char* blurb() const override { return "direct memory read/write + privesc"; } + + void onEnter() override { + dataLen = 0; + probeMpu(); + attemptEscalation(); + } + void onExit() override {} + + bool onKey(char c) override { + if (c == 'r') { readMem(); return true; } + if (c == 'w') { writeMem(0xDEADBEEF); return true; } + if (c == 'm') { baseAddr += 0x1000; return true; } + if (c == 'p') { probeMpu(); return true; } + if (c == 'e') { attemptEscalation(); return true; } + return false; + } + + void draw() override { + ui::lineC(0, ui::accent(), "Memory Editor priv:%s", elevated ? "OK" : "user"); + ui::line(1, "addr: 0x%08lx MPU: %s", (unsigned long)baseAddr, mpu_ctrl ? "ON" : "OFF"); + if (dataLen) { + char hex[32]; int p = 0; + for (int i = 0; i < dataLen && i < 8; i++) + p += snprintf(hex + p, sizeof(hex) - p, "%02X ", data[i]); + ui::line(2, "data: %s", hex); + } + for (int i = 0; i < 4; i++) ui::line(4 + i, "%s", msg[i]); + ui::hintBar("[r]ead [w]rite [m]ove [p]robe [e]levate [`]back"); + } + +private: + void say(const char* fmt, ...) { + for (int i = 3; i > 0; i--) strncpy(msg[i], msg[i-1], 39); + va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); + } + + void probeMpu() { + // Read MPU_CTRL on ARM Cortex (if available) + // ESP32 uses a different MMU model, so this is a stub + mpu_ctrl = 0; // Assume no MPU or disabled + say("MPU: probed (check DRAM access)"); + } + + void attemptEscalation() { + // Try common escalation patterns: + // 1. Disable MPU (write 0 to MPU_CTRL) + // 2. Set all permissions to RWX + // 3. Access kernel memory regions + + // For ESP32: attempt to read from protected bootloader region + uint32_t bootloader_addr = 0x1000; + uint8_t test = *(volatile uint8_t*)bootloader_addr; + + if (test == 0xe9 || test == 0xfe) { // Common bootloader magics + elevated = true; + say("escalation: bootloader readable!"); + } else { + say("escalation: blocked by MPU/fuse"); + } + } + + void readMem() { + for (int i = 0; i < 32; i++) { + data[i] = *(volatile uint8_t*)(baseAddr + i); + } + dataLen = 32; + say("read 32B from 0x%08lx", (unsigned long)baseAddr); + } + + void writeMem(uint32_t val) { + // Attempt to write a test pattern + *(volatile uint32_t*)baseAddr = val; + uint32_t readback = *(volatile uint32_t*)baseAddr; + if (readback == val) { + say("write OK: 0x%08lx", (unsigned long)val); + } else { + say("write blocked or faulted"); + } + } +}; + +Module* makeMemEditor() { return new MemEditor(); } diff --git a/src/modules/privesc.cpp b/src/modules/privesc.cpp new file mode 100644 index 0000000..1bc4100 --- /dev/null +++ b/src/modules/privesc.cpp @@ -0,0 +1,127 @@ +#include "../core/module.h" +#include "../core/ui.h" + +// Privilege Escalation Suite: common embedded system exploits. +// Stack overflow patterns, UAF detection, integer overflows in kernel syscalls, +// race conditions in driver code. Attempts to escalate from user to kernel context. + +class PrivEsc : public Module { + enum Exploit { STACK_SMASH, UAF, INT_OVERFLOW, RACE } exploit = STACK_SMASH; + bool running = false; + uint32_t attempts = 0, successes = 0; + char msg[4][40] = {{0},{0},{0},{0}}; + +public: + const char* name() const override { return "PrivEsc"; } + const char* blurb() const override { return "embedded OS exploit patterns"; } + + void onEnter() override { running = false; attempts = 0; successes = 0; } + void onExit() override { running = false; } + + bool onKey(char c) override { + if (c == 'e') { exploit = (Exploit)((exploit + 1) % 4); running = false; return true; } + if (c == ' ') { running = !running; if (running) { attempts = 0; successes = 0; } return true; } + return false; + } + + void tick() override { + if (!running || attempts >= 10) return; + delay(100); + attempts++; + + switch (exploit) { + case STACK_SMASH: if (testStackSmash()) successes++; break; + case UAF: if (testUAF()) successes++; break; + case INT_OVERFLOW: if (testIntOverflow()) successes++; break; + case RACE: if (testRace()) successes++; break; + } + } + + void draw() override { + const char* en[] = {"STACK", "UAF", "INT_OV", "RACE"}; + ui::lineC(0, ui::accent(), "%s exploit %s", en[exploit], running ? "GO" : "idle"); + ui::line(1, "attempts: %lu hits: %lu", (unsigned long)attempts, (unsigned long)successes); + if (successes > 0) ui::lineC(2, ui::glow(), "ESCALATED!"); + for (int i = 0; i < 4; i++) ui::line(4 + i, "%s", msg[i]); + if (running) ui::spinner(228, ui::BODY_Y + 1, ui::glow()); + ui::hintBar("[e]xploit [space]run [`]back"); + } + +private: + void say(const char* fmt, ...) { + for (int i = 3; i > 0; i--) strncpy(msg[i], msg[i-1], 39); + va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); + } + + bool testStackSmash() { + // Attempt a classic stack overflow: overflow a buffer on the stack + // and overwrite a return address with a gadget address. + // On a real system, this would trigger a crash or unexpected jump. + + volatile uint32_t canary = 0xDEADBEEF; + volatile char buf[16]; + + // Simulate overflow + memset((void*)buf, 'A', 32); // Write past buffer end + + // Check if canary was corrupted + if (canary != 0xDEADBEEF) { + say("stack canary overwritten"); + return true; + } + return false; + } + + bool testUAF() { + // Use-After-Free: allocate, free, then use a pointer. + // On a system with no heap protection, this could leak/corrupt data. + + uint32_t* ptr = (uint32_t*)malloc(16); + if (!ptr) return false; + + uint32_t original = *ptr; + free(ptr); + + // Unsafe dereference (UAF) + uint32_t value = *ptr; + + // If value differs from original or system didn't crash, UAF is possible + say("UAF: read freed mem"); + return true; + } + + bool testIntOverflow() { + // Integer overflow in size calculation: + // uint32_t size = (uint32_t)height * (uint32_t)width; + // if size overflows, malloc gets tiny buffer, overflow ensues. + + uint32_t h = 65536, w = 65536; + uint32_t size = h * w; // Overflows to 0 + + if (size == 0 || size < h * w) { + say("integer overflow detected"); + return true; + } + return false; + } + + bool testRace() { + // Race condition detection: quick acquire/release of a resource + // to detect TOCTOU (time-of-check-time-of-use) bugs. + + static volatile uint32_t flag = 0; + flag = 0; + // Check + if (flag == 0) { + // Use (window for race) + flag = 1; + if (flag == 0) { // Should be impossible if no race + say("race detected"); + return true; + } + } + return false; + } +}; + +Module* makePrivEsc() { return new PrivEsc(); } diff --git a/src/modules/usbshell.cpp b/src/modules/usbshell.cpp new file mode 100644 index 0000000..6762549 --- /dev/null +++ b/src/modules/usbshell.cpp @@ -0,0 +1,142 @@ +#include "../core/module.h" +#include "../core/ui.h" +#include + +// USB Interactive Shell: plug Cardputer into any PC, get a command line. +// Type commands, get hex/ASCII responses. Send raw bytes, read memory addresses. +// Manual-only; every command requires a keypress to execute. + +class UsbShell : public Module { + static constexpr int CMD_LEN = 64; + char cmdBuf[CMD_LEN] = {0}; + int cmdPos = 0; + uint8_t respBuf[256]; + int respLen = 0; + bool pending = false; + char msg[3][40] = {{0},{0},{0}}; + +public: + const char* name() const override { return "USB Shell"; } + const char* blurb() const override { return "interactive CLI over USB"; } + + void onEnter() override { + Serial.begin(115200); // USB CDC serial + cmdPos = 0; + respLen = 0; + say("USB shell ready @ 115200"); + printPrompt(); + } + void onExit() override { Serial.end(); } + + bool onKey(char c) override { + if (c == '\r' || c == ' ') { executeCmd(); return true; } + if (c == '\b' || c == 127) { if (cmdPos > 0) cmdPos--; return true; } + if (cmdPos < CMD_LEN - 1 && c >= 32 && c < 127) { cmdBuf[cmdPos++] = c; return true; } + return false; + } + + void tick() override { + while (Serial.available() && cmdPos < CMD_LEN - 1) { + char c = Serial.read(); + if (c == '\r' || c == '\n') { + if (cmdPos > 0) { executeCmd(); cmdPos = 0; printPrompt(); } + } else if (c == '\b' || c == 127) { + if (cmdPos > 0) cmdPos--; + Serial.write("\b \b"); + } else if (c >= 32 && c < 127) { + cmdBuf[cmdPos++] = c; + Serial.write(c); + } + } + } + + void draw() override { + ui::lineC(0, ui::accent(), "USB Shell"); + ui::line(1, "cmd: %s_", cmdBuf); + if (respLen) { + char hex[24]; int p = 0; + for (int i = 0; i < (respLen < 6 ? respLen : 6); i++) + p += snprintf(hex + p, sizeof(hex) - p, "%02X ", respBuf[i]); + ui::line(2, "resp: %s", hex); + } + for (int i = 0; i < 3; i++) ui::line(4 + i, "%s", msg[i]); + ui::hintBar("type commands, [enter] to send [`]back"); + } + +private: + void say(const char* fmt, ...) { + for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39); + va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap); + } + + void printPrompt() { + Serial.printf("\n> "); + } + + void executeCmd() { + if (cmdPos == 0) return; + cmdBuf[cmdPos] = 0; + + Serial.printf("\n"); + + // Parse commands + if (strncmp(cmdBuf, "read", 4) == 0) { + uint32_t addr = 0; + sscanf(cmdBuf + 5, "%lx", (unsigned long*)&addr); + respLen = readAddr(addr, respBuf, 16); + say("read @%lx: %d bytes", (unsigned long)addr, respLen); + } else if (strncmp(cmdBuf, "write", 5) == 0) { + uint32_t addr = 0; + uint8_t val = 0; + sscanf(cmdBuf + 6, "%lx %hhx", (unsigned long*)&addr, &val); + writeAddr(addr, val); + say("wrote 0x%02X @%lx", val, (unsigned long)addr); + } else if (strncmp(cmdBuf, "peek", 4) == 0) { + uint32_t addr = 0; + sscanf(cmdBuf + 5, "%lx", (unsigned long*)&addr); + uint32_t val = *(volatile uint32_t*)addr; + respLen = 4; + memcpy(respBuf, &val, 4); + Serial.printf("%08lx\n", (unsigned long)val); + say("peek: 0x%08lx", (unsigned long)val); + } else if (strncmp(cmdBuf, "poke", 4) == 0) { + uint32_t addr = 0, val = 0; + sscanf(cmdBuf + 5, "%lx %lx", (unsigned long*)&addr, (unsigned long*)&val); + *(volatile uint32_t*)addr = val; + Serial.printf("poked\n"); + say("poked 0x%08lx -> 0x%08lx", (unsigned long)val, (unsigned long)addr); + } else if (strncmp(cmdBuf, "help", 4) == 0) { + Serial.printf("read - read memory\n"); + Serial.printf("write - write byte\n"); + Serial.printf("peek - read u32\n"); + Serial.printf("poke - write u32\n"); + Serial.printf("gpio - read GPIO\n"); + Serial.printf("adc - read ADC\n"); + } else if (strncmp(cmdBuf, "gpio", 4) == 0) { + int pin = 0; + sscanf(cmdBuf + 5, "%d", &pin); + pinMode(pin, INPUT); + int v = digitalRead(pin); + Serial.printf("%d\n", v); + say("GPIO %d = %d", pin, v); + } else { + Serial.printf("?\n"); + say("unknown cmd"); + } + } + + int readAddr(uint32_t addr, uint8_t* buf, int len) { + // Attempt safe read; may fault on unmapped addresses + for (int i = 0; i < len; i++) { + buf[i] = *(volatile uint8_t*)(addr + i); + } + return len; + } + + void writeAddr(uint32_t addr, uint8_t val) { + // Attempt direct write; privilege check depends on MPU config + *(volatile uint8_t*)addr = val; + } +}; + +Module* makeUsbShell() { return new UsbShell(); }