Securing Linux Utilities Against Supply Chain Exploits

It broke. Well, technically it didn’t break. It did exactly what it was told to do, which is usually the problem.

Last Tuesday, I spent nine hours dissecting a malicious payload that slipped into our staging environment through a compromised dependency. I won’t name the specific package—mostly because the maintainers have already pulled it and rotated their keys—but the mechanism it used to establish a reverse shell was infuriatingly simple. It didn’t drop a compiled binary. It didn’t need a zero-day exploit. It just used the standard Linux tools sitting right there on our servers.

This is the reality of modern supply chain attacks. Threat actors don’t want the hassle of cross-compiling malware for different architectures anymore. They just piggyback on your application’s permissions and live off the land.

The Execution Probe Game

When the malicious script booted up, it didn’t immediately try to blast out data to its C2 server. First, it checked what it was allowed to do. Our servers run PHP 8.3.4, and like any paranoid sysadmin, I had a decent list of blocked functions in our php.ini.

The malware didn’t care. It just ran a silent probe, iterating through every possible way to interact with the underlying Linux shell until it found an opening.

Linux terminal screen - npm - ng new didn't work, showing blank terminal screen - Stack ...
Linux terminal screen – npm – ng new didn’t work, showing blank terminal screen – Stack …
$methods = ['popen', 'proc_open', 'exec', 'shell_exec', 'system', 'passthru'];
$shell_method = null;

foreach ($methods as $method) {
    if (function_exists($method) && is_callable($method)) {
        $shell_method = $method;
        break;
    }
}

// If it found one, it immediately tested it with a basic Linux utility
if ($shell_method) {
    // Proceed to abuse curl, wget, or bash
}

I had blocked system and exec. I forgot to block proc_open. That one oversight was all it took. Once the script had shell access, it started calling basic utilities to map out the system. It ran whoami, checked /etc/os-release, and set up a persistent heartbeat using ping.

Why Stripping Binaries Isn’t Enough

Well, that’s not entirely accurate. My first instinct was completely wrong. I figured I’d just remove the networking utilities from our production containers. If the attacker can’t use curl or wget, they can’t download their secondary payloads, right?

I spent an afternoon rebuilding our Dockerfiles, stripping out everything but the bare minimum. I tested it on my M3 Mac running

Can Not Find Kubeconfig File