How an Arbitrary File Write Becomes Code Execution Through ld.so.preload
Writing a file is not running one, yet attackers bridge the two every day. How the dynamic linker, web roots and scheduler files turn a file-write primitive into a persistent implant.
An arbitrary file writeArbitrary File Write🛡️A vulnerability class in which an attacker controls both the contents and the destination path of a file written by a target system. On a general-purpose OS it is usually equivalent to code execution, because files written to certain locations are loaded or run automatically. sounds less alarming than remote code execution. You cannot run the file you just dropped; you can only place bytes at a path. Yet in the FortiMail zero-dayZero-Day🛡️A security vulnerability that is exploited or publicly disclosed before the software vendor can release a patch, giving developers 'zero days' to fix it. CVE-2026-104286, attackers turned exactly that primitive into a persistent implant on the appliance. The bridge between "write a file" and "run my code" is worth understanding, because it recurs on nearly every Linux-based system you operate, and because knowing the bridges tells you which files to watch.
The gap between writing and running
A file write primitive gives an attacker control over the contents of some path on disk. That is not the same as execution. The operating system does not run a file because it appeared; something has to load it, interpret it, or execute it. The attacker's job, once they can write, is to find a path the system itself will act on. Every such path is a bridge from passive bytes to active code.
The reason this matters defensively is that the set of bridges is small and well known. You do not have to imagine every file an attacker might drop. You have to know the handful of locations where a written file gets executed by something already running, and make sure a write to those locations is either impossible or immediately visible.
ld.so.preload: the bridge used against FortiMail
The cleanest bridge on Linux is the dynamic linker. When any dynamically linked program starts, the loader reads a file at `/etc/ld.so.preloadld.so.preload🛡️A Linux file read by the dynamic linker that lists shared libraries to load into every dynamically linked process before any other library. Designed for legitimate interposition, it is a favored persistence mechanism: writing a path into it forces attacker code to load into processes system-wide, including root-owned ones.` (on FortiMail, `/data/etc/ld.so.preload`) and loads every shared library listed there into the new process before anything else. It was designed for legitimate interposition, such as debugging or injecting an allocator. For an attacker it is close to ideal: write a malicious shared object anywhere readable, add its path to `ld.so.preload`, and your code now loads into essentially every process that starts, including the ones running as root.
That is precisely what the FortiMail IoCs show. The intruders dropped a shared object at `/data/lib/liblog.so` and registered it through `/data/etc/ld.so.preload`. From that point their code ran inside the appliance's own processes, which is how a mail gateway ended up with an added archive account quietly shipping messages to an external host. Two files written, and the box belongs to someone else.
The other common bridges
The dynamic linker is one route; the pattern generalizes. When you reason about an arbitrary file write, walk the list of things that execute files on their own:
- **Web roots and script directories.** Write a `.php`, `.jsp`, or `.aspx` file under a path the web server will serve, and the next request executes it. This is the classic webshell, and it is why the null byte in CVE-2026-104286 mattered: defeating an extension check lets the attacker land an executable file type where a static one was expected.
- **Startup and scheduler files.** A crontab under `/etc/cron.d`, a systemd unit, or a shell profile runs at a predictable time or event. Slower than preloading, but it survives a reboot.
- **Configuration files for a privileged service.** You may not write code at all. Overwriting `/data/etc/httpd.conf`, which also appears in the FortiMail IoCs, can change how a root-owned daemon behaves, point it at attacker content, or relax a control.
- **Libraries loaded by a specific program.** If a daemon loads a plugin or library from a writable directory, replacing that file executes code the next time the daemon starts or reloads.
Each of these is a case where the system does the running for the attacker. The write is the whole exploitExploit🛡️Code or technique that takes advantage of a vulnerability to cause unintended behavior, such as gaining unauthorized access.; execution is a side effect of normal operation.
Why extension and path checks are not enough
Developers often guard a file write with a check: the name must end in an allowed extension, or the path must stay inside an upload directory. Both checks are routinely defeated, which is why the FortiMail flaw pairs a path traversalPath Traversal🛡️A web vulnerability (CWE-22) where user-supplied input in a file path escapes the directory the application intended to serve from, typically via parent-directory references, letting an attacker read or write files elsewhere on the server. with a NULL byte problem. Traversal sequences break out of the intended directory. A NULL byte or similar terminator confuses the layer that validates the name versus the layer that finally opens it, so a path that passes the check is not the path that gets written. This is the same class of parser disagreement that produces authentication bypasses, where one component and another disagree about what a request says. The defensive takeaway is that input-based allowlisting on a file write is brittle. The durable control is to make the dangerous destinations unwritable and to watch them if they ever change.
What this means for defense
If you run appliances or servers, treat the bridges as your monitoring targets rather than trying to anticipate every possible dropped file. The preload file, the web root, the scheduler directories, and the configuration of privileged daemons are a short, stable list. A change to any of them, outside a known deployment, is a high-signal event. That is the logic behind file integrity monitoring, and the practical steps for standing it up on a locked-down box are covered in How to Detect Unauthorized File Changes on a Hardened Appliance.
Understanding the write-to-execute bridge also reframes how you read an advisory. When a vendor describes "arbitrary file write by an unauthenticated attacker," do not mentally downgrade it because no code execution is claimed. On a general-purpose operating system, the bridges are always present. That is why CVE-2026-104286 rates a CVSS 9.8 and why its mitigation could not wait for a patch, a tradeoff we examine in Why Disabling a Feature to Dodge a Zero-Day Is a Business Decision. A file write against a network-facing management interface should be read as code execution unless you can prove the bridges are all closed.