RC RANDOM CHAOS

dd writes raw sectors below the filesystem

Why the Unix dd command is a real security primitive: raw block writes below the filesystem, wiper TTPs, exfiltration, and the telemetry gap defenders miss.

· 7 min read
dd writes raw sectors below the filesystem

dd does not ask. No confirmation prompt. No dry-run by default. No undo. It reads bytes from one file descriptor and writes them to another, and it treats a 2TB block device the same way it treats a 40-byte text file. Direction is set by two arguments. if= names the input. of= names the output. Swap them once and the copy runs backwards. The disk meant to be read becomes the disk that gets overwritten. That is the entire failure mode. The tool is working exactly as designed.

The nickname “disk destroyer” frames this as a hand-slip at the keyboard. It is not. The keyboard is the least of it. The real exposure is dd embedded in shell scripts, provisioning pipelines, and imaging automation, where the arguments are not typed by a human but assembled from variables, and where nobody reviews the resolved command line before it runs as root.

The mechanism sits below the filesystem. When dd opens /dev/sda for output, it writes raw sectors. It bypasses the VFS layer, the inode permissions on individual files, and every ACL the filesystem would otherwise enforce on a path. The kernel validates one thing - write access to the block device node itself. Root has it. A container started with --privileged or holding CAP_SYS_ADMIN with the host device mapped in has it. A CI runner flashing bare metal has it. Once the descriptor is open, dd streams whatever if= produces straight onto the device. The partition table, the LUKS header, the superblock, the journal - all of it is just byte offset zero and up. There is no structural distinction between destroying a filesystem and writing a file. Same syscall path. Different target.

Two distinct security primitives fall out of this. One is destruction. One is disclosure. Both are underappreciated because the utility is treated as benign.

Destruction is the obvious one. dd if=/dev/zero of=/dev/sda or if=/dev/urandom writes across the block device, clobbering the partition structure and filesystem metadata in the first megabytes and then everything after. conv=noerror,sync tells it to continue past read errors and pad short blocks, so the write does not halt when it hits a bad sector. It maps cleanly to MITRE T1561.001, Disk Content Wipe, and T1561.002, Disk Structure Wipe. The parent class is T1485, Data Destruction. On an SSD, recovery is worse than on spinning media. TRIM and the flash translation layer mean the original blocks may already be gone before any carving tool is attached. There is no dd equivalent of a recycle bin. The write is the event and the event is terminal.

Disclosure is the primitive people forget. dd if=/dev/sda of=/tmp/out.img reads the raw device below the filesystem. That read returns deleted-but-not-overwritten files, slack space, unallocated blocks, swap contents, and LUKS headers - data the filesystem permission model would never expose through a normal open() on a path. A process that can read the block device can pull credentials out of freed pages and key material out of swap without touching a single file the ACLs protect. This is T1005, Data from Local System, executed through the block layer instead of the file layer. EDR products tuned to watch file access patterns frequently miss it, because there is no sensitive file being opened. There is a block device being read sequentially, which looks identical to a legitimate backup.

The scripting surface is where this stops being a niche concern. Consider the standard shape of a provisioning or imaging script. A device path is computed, an image is selected, and dd writes the image to the device. dd if="$IMAGE" of="/dev/$DISK". Now walk the failure conditions. $DISK unset under an unquoted expansion resolves the output to /dev/, and depending on the surrounding logic and the value substituted elsewhere, the target drifts. A $DISK that was meant to be sdb but inherited sda from a prior loop iteration writes to the wrong disk with no error and no prompt. A pipeline that runs the imaging step against whatever block device enumerated first will happily overwrite the boot volume if enumeration order changed between hardware revisions. None of these are dd bugs. They are the absence of validation around a utility that performs no validation of its own. set -u is not set. The device path is not confirmed present, not confirmed unmounted, not confirmed to be the intended target by serial or by-id. The script trusts a variable, and dd trusts the script.

This is the same trust-boundary collapse seen across CI/CD compromise. The job holds more capability than its function requires. An imaging step needs write access to exactly one device. It is instead handed root in a context that can reach every block device attached. The delta between what the job needs and what the job is granted is the blast radius. When the input to dd is attacker-influenced - a poisoned image URL, a manipulated device variable injected through an environment override, a build parameter that flows unsanitised into of= - the utility becomes the weapon and the pipeline pulls the trigger with full privilege. The classic wiper campaigns operationalised exactly this primitive. HermeticWiper and CaddyWiper against Ukrainian infrastructure, Shamoon against Saudi Aramco, all wrote raw structures to disk to render systems unbootable. HermeticWiper reached the block layer through a signed EaseUS partition-management driver rather than dd, but the objective was identical - direct writes beneath the filesystem to destroy structure that the OS depends on to boot. On Linux, dd is the built-in, already-trusted, already-present version of that capability. Nothing to drop. Nothing to sign. Living off the land, MITRE T1105 not even required.

What defenders see depends entirely on instrumentation they usually have not deployed. dd is an ordinary process execution. On a host running auditd with an execve rule, the SYSCALL and EXECVE records capture the full argv, so the resolved if= and of= values are recoverable after the fact. Sysmon for Linux emits Event ID 1, process creation, with the command line, giving the same visibility if the deployment exists. That is the good case. The command line is the whole story, because the argument values are the intent.

The bad case is everything below the exec. There is no native alert for a raw write to a block device. The kernel does not raise a security event when a process opens /dev/sda with write intent and begins streaming zeros. auditd can watch the node with a path rule on /dev/sda for write, but that rule is not present in a default policy, and the device set has to be enumerated in advance. EDR agents on Linux commonly instrument process creation and file operations against paths under the filesystem, and are blind to sequential block-device I/O. So the destruction and the disclosure both happen inside a visibility gap. The exec fires. The write does not. By the time the process exits, the partition table is gone or the raw image is staged for exfiltration, and the only artifact is a process-creation event that looked like routine imaging. If argv was assembled from variables and never logged pre-execution by the script itself, even the intent is reconstructed only from whatever process-creation telemetry happened to be running.

The correct read on dd is that it is not a vulnerability and there is no CVE to assign. There is no bug to patch. It is a fundamental utility whose behaviour is precise, documented, and unforgiving, exposed at a privilege level where every operation is final. The risk is not the binary. The risk is the automation wrapped around it that treats device paths as trusted input, runs as root without least-privilege scoping, and carries no validation of target, mount state, or intent before the write begins.

What survives every patch cycle, because there is nothing to patch, is the operational reality. A block device write bypasses the filesystem and everything defending it. dd will read below the ACLs and write below the ACLs on request, with no distinction between backup and destruction, and no confirmation either way. The exposure lives in scripts that build of= from a variable and hope the variable is right. Auditd execve rules and Sysmon for Linux Event ID 1 recover the argv after the fact. Nothing recovers the disk. The mitigation is scoping and validation upstream of the command - least-capability execution context, explicit device targeting by stable identifier, and pre-execution logging of the resolved command line - because the command itself will never refuse. It was never built to.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.