Cybersecurity In-Depth: Feature articles on security strategy, latest trends, and people to know.

Why do fileless attacks persist? Let's break down the strengths and weaknesses of the existing mitigations.

Rui Maximo, Cybersec Entrepreneur

February 9, 2021

5 Min Read

Even though the term "fileless malware" was only coined recently, fileless attacks have been around for more than three decades. You may have heard of the more well-known names, like buffer-overflow, stack-overflow and heap-overflow attacks. The first known exploit of this category was the Morris worm, developed by Robert Morris in 1988 and created to prove the risks of exploiting buffer overflows.

As attacks evolve in sophistication, countermeasures struggle to keep up — the security industry has failed to eliminate a single attack category. Last week in part 1 of this series, we examined what fileless attacks are. Here in part 2, to understand why these threats haven't been mitigated, let's break down the historical countermeasures put in place to eliminate them.

Safer C/C++ Libraries

Applications developed using the standard C and C++ libraries are vulnerable to buffer-overflow attacks because they don't perform boundary checks on the input-string arrays to standard functions. To mitigate this issue, safer versions of these libraries were created to replace the originals.

While this is a good strategy for reducing potential buffer-overflow vulnerabilities, most legacy applications don’t use the safer libraries. 

Static Analysis

Static analysis tools scan source code in search of potential vulnerabilities. Integrating scanners into the continuous integration and delivery process is a good way to "sanitize" source code and achieve secure coding practices.

These static analysis tools are not foolproof solutions, however, because not all developers run them, and they often need to be updated when new security vulnerabilities are discovered. This means that they cannot catch attacks they have never seen before, which is the very definition of zero-day exploits.

Stack Canary

The stack canary, introduced in 1997, is a guard inserted by the compiler between the return address and the input parameters to a function. The compiler adds a random value before the return address and saves a copy of this random value (or canary) in a safe place off the stack.

Before the function returns, the canary is checked against the saved value. If the return address is modified via a buffer overflow, then this guard would be trampled. The assumption is that, for an overflow to occur, the canary must also be overwritten.

This countermeasure can be circumvented by guessing the value of the canary by brute-forcing it one byte at a time. Once the canary is determined, the stack can be overflowed and the return address modified without detection — and the value of the canary rewritten with a reverse-engineered value.

W^X Stack

Another approach to preventing stack-overflow attacks is W^X stack, which was introduced in 2003 by Intel and AMD at the chip level, and is leveraged by several operating systems, including Linux.

Marking the stack as either writable and non-executable or non-writable and executable prevents malicious code on the stack from running. Attempting to inject executable code onto a stack where executable code is blocked would trigger the W^X stack countermeasure.

An attacker can leverage existing assembly instructions in the application itself to subvert the control flow of the application. The first approach to circumventing a W^X stack is the return-into-libc attack by security expert Solar Designer. By exploiting a buffer-overflow vulnerability to introduce new frames onto the stack that execute existing assembly instructions to call libc functions, an attacker can launch shellcode.

Since applications call system resources, the attacker can assume that the libc library is likely to be loaded by the target application and can then leverage the functions in the libc library to launch a shell terminal. Once the shell terminal is launched, the attacker has control of the target system.

Subsequent evolutions of this type of attack leveraged the procedure link table (PLT) to launch shellcode, known as a return-to-PLT attack. The latest evolution of this type of attack is return-oriented programming (ROP). Here the attacker examines the assembly code of the target application for instructions of interest with a return code, and pieces together these assembly instructions, called gadgets, to launch a shell terminal. ROP attacks have further evolved in sophistication and more general applicability into Just In Time ROP and Blind ROP attacks, which I invite the reader to research on their own.

Address Space Layout Randomization

Address space layout randomization (ASLR), introduced in Linux in 2000, randomizes the memory space at which shared libraries, such as libc, are loaded in memory. Instead of applications having their own individual copies of the same library, these libraries are shared across multiple applications as read-only copies, and the address at which these shared libraries are loaded in memory is tracked in the PLT.

ASLR makes it difficult for attackers to guess the address where these shared libraries are loaded. However, only the base address is randomized. The offset of a particular function from its base address remains constant. Once the base address of a shared library or the address of a function with a shared library is discovered, an attacker can locate the address of all the functions within that library.

When compiled, every Linux application includes constructor and destructor calls before main() is called and after main() is called. The compiler automatically adds the link loader library assembly code into the compiled application as well. The link loader library, ld.so, is responsible for loading libc and other dynamic libraries. The return-to-csu attack leverages these known additional assembly instructions added to every application to circumvent ASLR.

This attack is a two-stage process:

  1. Construct a micro ROP attack to discover the address of the function write() in the libc library loaded by the link loader in the PLT.

  2. With this known address of the write() function, stage another ROP attack to open a remote shell.

By exploring the different countermeasures the software industry has devised to combat fileless malware, and seeing how easily they are circumvented, it is clear that hackers are honing their craft at an alarming rate.

In part 3 of this series next week, we'll discuss new methods that can more effectively mitigate fileless attacks — and finally eliminate the entire threat category.

About the Author(s)

Rui Maximo

Cybersec Entrepreneur

Rui Maximo developed a strong interest in security during his master's program in Mathematics. After completing his thesis in cryptography, he was recruited into security roles throughout his 25 years career in the software industry, and held a variety of roles from software engineer, program manager, manager, author, principal engineer, instructor to software architect and consultant. When he's not honing his skill sets, you'll likely find him reading, studying the market, writing an article, snowboarding or traveling (at least until covid hit). He loves to spend time with his kids. Rui grew up all over the world, but calls Seattle home.

Keep up with the latest cybersecurity threats, newly discovered vulnerabilities, data breach information, and emerging trends. Delivered daily or weekly right to your email inbox.

You May Also Like


More Insights