Bali, Indonesia
Insights

Checking /etc/ld.so.preload to Confirm Hidden Malware Processes

October 19, 2026
0:00
You receive an abuse notice reporting high outbound traffic, or your server's load average sits suspiciously high while CPU-intensive tasks crawl. You open a shell, run top or ps aux, and see nothing unusual—just normal background services idling. When system monitoring alerts disagree with standard process inspection utilities, the reporting tools themselves may be lying because an attacker hooked the underlying system calls. On a compromised Linux server, this concealment often starts with a file named /etc/ld.so.preload. Understanding how this mechanism works and how to look past it is essential when investigating stealthy intrusions.
Most common administrative utilities on Linux—including ps, top, lsof, and netstat—are dynamically linked. When you execute them, the operating system uses the dynamic linker (typically ld-linux.so on systems running GNU C Library, or glibc) to load shared libraries like libc.so before the program runs. These standard libraries provide functions that read directory contents, list processes in /proc, and handle network sockets. Linux provides an intentional feature through /etc/ld.so.preload that allows administrators to specify shared libraries that must be loaded before any other library for every dynamically linked executable. While designed for system-wide debugging, performance tracing, or custom memory allocators, malicious actors exploit this exact mechanism to install user-space rootkits. Once an attacker gains root privileges, often after automated SSH credential stuffing, they place a shared object path into this file. When ps or top runs, it loads the attacker's custom shared object first. The malicious library hooks common library functions, such as readdir or fopen. Whenever the process monitoring utility asks for a list of directory entries inside /proc, the hooked function intercepts the result. If a process ID or executable name matches the attacker's malicious binary (such as a cryptocurrency miner or botnet client), the shared object strips that entry from the returned data. The administrative tool never receives the record, so it displays a completely clean process list.
The dynamic linker inspects two primary preloading mechanisms: the LD_PRELOAD environment variable and the /etc/ld.so.preload configuration file. They achieve a similar result by inserting a shared object ahead of standard dependencies, but their scope and persistence are fundamentally different. The LD_PRELOAD variable is scoped to a specific execution environment. It affects only the process running with that environment variable set, along with any child processes spawned from that session. If a user logs out or runs a service under a sanitized systemd unit, the variable disappears. Additionally, the dynamic linker automatically ignores LD_PRELOAD when executing setuid or setgid programs to prevent unprivileged users from hijacking elevated processes. In contrast, /etc/ld.so.preload applies globally across the entire operating system. Any dynamically linked program executed by any user, system service, cron job, or daemon will load the libraries listed in this file. Because it resides in /etc, modifying it requires root privileges. Once written, it establishes persistent interception that survives reboots and impacts every user-space program that relies on dynamic linking. For attackers deploying stealthy mining tools or background backdoors, this file guarantees that process hiding occurs automatically system-wide.
A common point of confusion during an incident is running cat /etc/ld.so.preload and receiving an error stating that the file does not exist, or seeing an empty response even when hidden activity is suspected. The reason is circular: cat, ls, and standard editors are also dynamically linked programs. If the attacker's preloaded library is programmed to hide the presence of the rootkit, it will intercept file-access functions like open, open64, stat, and access. Whenever a dynamically linked binary attempts to read or inspect /etc/ld.so.preload, the malicious hook catches the file path argument. The hook returns an error code—frequently ENOENT (No such file or directory)—before the kernel is ever queried. As a result, using standard system binaries to inspect the file creates a situation where you are asking a compromised interpreter whether it is compromised. To see what is genuinely happening, you must bypass dynamic linking entirely or inspect the system through interfaces that do not load the hooked shared object.
To bypass user-space hooking, you must use statically linked binaries. A statically linked program contains all of its required code within the executable file itself. It does not invoke ld-linux.so, does not read /etc/ld.so.preload, and calls the Linux kernel directly via system calls (syscalls). Because the attacker's library is never loaded into the process's memory space, the hooks cannot intercept anything. Many rescue toolkits, emergency distributions, and packages like busybox-static provide statically compiled tools. If you have access to a static build of busybox or a static shell, you can directly inspect the filesystem without loading dynamic libraries. You can also leverage internal shell built-ins in certain environments, though static binaries remain the most reliable method.
BASH
# Verify whether a tool is dynamically linked or statically linked
ldd /bin/cat
# Output showing dynamic dependencies confirms it can be hooked:
# libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6

# Inspect /etc/ld.so.preload using a statically compiled binary
busybox cat /etc/ld.so.preload

# Read directory entries directly without dynamic hooks
busybox ls -la /etc/ld.so.preload
If the file exists and contains entries, busybox will output the absolute path to the malicious shared object (for example, /lib/x86_64-linux-gnu/libprocess_hide.so or /usr/local/lib/libaudit.so). Once you have this path, you have identified the shared library executing the hook. If you do not have static tools installed on the system, do not attempt to compile or install untrusted dynamic packages directly on the compromised host, as package managers like apt or yum are themselves dynamically linked and subject to interception.
Confirming an unauthorized entry in /etc/ld.so.preload answers how processes are being hidden, but it is rarely the full extent of the intrusion. In hosting environments, user-space rootkits are typically deployed as intermediate steps after an initial breach, such as an automated SSH dictionary attack against root or an exploit against a web application. If you have not reviewed your SSH boundary defenses, our guide on Hardening Linux Web Servers Against SSH Brute Force and Zero-Day Exploits covers how automated bots systematically probe these access points. First, do not simply delete the referenced .so file without clearing or removing /etc/ld.so.preload. If you delete the shared object while /etc/ld.so.preload still points to it, every dynamic binary on the system may fail to execute with an error stating that the preloaded library cannot be opened. This can render the server completely unresponsive, locking you out of SSH sessions and administrative commands entirely. The safe sequence is to truncate or remove /etc/ld.so.preload first, verify that standard commands execute cleanly, and then isolate the malicious library for forensic analysis. Second, check where the shared library originated and what processes were launched alongside it. Once /etc/ld.so.preload is disabled, running ps aux will suddenly expose processes that were previously concealed—such as coin miners running under disguised names or outbound network scanners. Inspect cron directories (/etc/cron*, /var/spool/cron/crontabs), systemd timer definitions, and SSH authorized keys files for persistence mechanisms left behind by the attacker. When dealing with persistent user-space concealment, assume the operating system's integrity is compromised. Neutralizing /etc/ld.so.preload restores visibility so you can identify the outbound connections and payload mechanisms, but restoring the system from a verified clean backup or rebuilding the server remains the safest path to full recovery.