ETERNALBLUE, DOUBLEPULSAR, Heartbleed…. Many of us have heard of these terms and possibly of their association with malware. However, the ecosystem and jargon can be confusing. How is ETERNALBLUE really related to WannaCry and Petya? What is the difference between an exploit and a malware to begin with? As a Malware Researcher, I’ve done deep studying of the Windows operating system internals, user-mode APIs, kernel APIs, and kernel internals as well as how malware uses sneaky techniques in order to exploit and trick both users and programs. In this article, we’re going to take a high-level overview of how malware is constructed using exploits. The reason for this is that during my research, despite there being a zillion different ways to actually exploit software for malicious reasons, understanding the core concepts is most important because the details often change while the fundamentals rarely do. Understanding these fundamentals can be useful not just for other malware researchers, but even for the power user, sysadmin, or the curious in helping to avoid and catch malware tricks.
What is a program
In order to understand how exploits work, it’s important to understand exactly what a program (or “application”) is to begin with. Every bit of information on a computer system, which is stored either “in memory” (ie RAM, ROM) or “on disk” (hard drive, solid state drive, USB storage, SD card, etc) is simply a series of numbers that we call “bytes.” Each byte is made up of 8 bits. The bit is the smallest unit that we use. For example, here is one byte of data: 1001 0101. A program is just a series of bytes which are interpreted as instructions by the processor, which executes the instructions one-by-one (per core; so it can actually execute more than one set at once but each actual core is executing one at a time). These instruction sets are released by the manufacturer of the processor such as Intel or AMD and they conform to a standard called an “architecture” such as the x86, x64, or ARM architectures. The processor does work in units called “clock cycles.” It takes some number of cycles to perform each operation that a computer performs, such as addition, subtraction, multiplication, or division. For example, on some processors, division between two numbers may take 17 cycles to perform. A 2.6 Ghz processor can perform 2.6 billion cycles per second. Programs also contain what’s called “data sections” and, just like the instructions, these are also bytes. The difference is that instead of being interpreted as instructions, the processor will interpret the data bytes as information upon which it acts. For example, say the processor knows that the code 5 means “add the following two numbers together” and then we give the processor this instruction:
5 75 25
The processor reads this as “add 75 to 25” or 75+25 and it comes up with the number 100 as the answer. In this example, the instruction was 5 and the data was the number 75 and the number 25.
So in order for a program to execute, its instruction bytes must be loaded into memory and then the processor’s instruction pointer (IP) must be placed at the beginning of said instructions. Operating systems employ what is called “threading” in order to keep track of instruction execution throughout various programs. You are probably aware of the fact that you can be running 10-20 or more apps at the same time on the computer and each app is doing something in the background “at the same time.” What’s actually going on is the operating system is giving each application some specific amount of time on the processor cores and then switching it out and replacing it with another application. The method in which this madness is tracked is called threads and processes. If you press Control+Alt+Delete on your keyboard, you can view a list of processes. If you click the “Performance” tab, you can even see the total number of processes and threads which are running. “Running” here means “loaded into virtual memory and executing at least one thread.” Simply put, each “thread” is a separate list of instructions with its own processor state. For example, ransomware may have a couple threads which are encrypting a victim’s files in the background quietly while a third thread is displaying the ransom note. Thus, it’s possible and very common for a given process to have multiple threads, and an application to even have multiple processes, such as Google Chrome or Mozilla Firefox. This is useful because if a computer user minimizes an application and the application doesn’t need to do anything at all, it can be removed from the processor (and possibly even RAM), and other programs which need to use the processor can do so but as far as the user think’s the program “is still open.”
The operating system is managing all of this and while yes, it is very complicated, the overall idea is simple: The OS is prioritizing the processor time and memory so that it operates in a seamless manner and slows/crashes as little as possible for a nice user experience.
Now, let’s switch gears and go into attack mode.
Understanding the concepts above opens up a vast world of thought about how malware works. After all, malware stands for malicious software. As mentioned above, software is simply a list of instructions for the processor to follow along with data loaded into memory for those instructions to interact with. So let’s put on our malware author hats and look at problems from the perspective of an attacker.
As a malware author I…
1. Need to create malicious instructions
In order to control the computer and get it to do bad things, a malware author needs to first craft some malicious instructions. These instructions can come in the form of regular program code or so-called “shellcode” which is code created outside of a program and then injected into an otherwise legitimate program in order to get it to do bad things. But what does “injected into a program” really translate to? It means that the instructions must be placed in the memory area allotted to the program by the operating system.
2. Need to load these instructions into memory in preparation for the CPU to execute them
The instructions must be in memory in order for a thread’s instruction pointer to be moved to the beginning of them. This means that the instructions must, at some point before execution, be loaded into memory from a website, drive, other program, other area in memory, etc…
3. Need to get the CPU to execute the malicious instructions
As introduced above, in order for this to occur, a thread’s instruction pointer must be moved to a memory location at the very start of the list of malicious instructions. The thread will then sequentially execute the malicious instructions.
4. Need to be as sneaky as possible about it, avoiding both user detection and software detection (antivirus/antimalware solutions)
Antivirus/antimalware as well as malware researchers and analysts can actually read the instructions just like the CPU can. They are able to interpret them and find common malicious behaviors this way. As malware authors, we want to avoid being detected. There are several ways to do this:
A) Encrypt or obfuscate (hide) the instructions – If the instructions require a secret code in order to decrypt or deobfuscate, then no anti-malware software or malware analyst will be able to understand or detect the malicious instructions because they will appear to be gibberish. This prevents them from being detected. For example, WannaCry placed instructions into an encrypted zip file and attached them to the main executable. This meant that the encrypted instructions could sneak by many antivirus solutions on both the end-user computer (endpoint) as well as the network.
B) Place the bad instructions in a remote, trusted location instead of inside of the malware file. This means that the malware file will be scanned and no malicious behavior will be detected because it doesn’t actually exist in the file; instead, the malicious behavior exists elsewhere, such as on a server, the Windows Registry, a database, or in a benign file, and the malware, instead of executing internal malicious instructions, reads external instructions after it is already launched and then executes them. A good example of this is “fileless malware” which for example, can store instructions in the Windows Registry and then copy them out and execute them. Essentially, all this is needed in order to fulfill this step is a safe place to store instructions.
C) Derive or generate instructions after the program is already running and considered to be safe so that they are not detected before the program is running. Simply put, if I create a program which writes another program which does bad things, it may not be as detectable by normal antivirus solutions as if it were originally written to do bad things.
D) The sky is the limit
Using Good for Bad
It’s worth exploring the topic of using good for bad a bit more. Technically, anything bad that malware can do has to actually use a legitimate computer function. For example, the Windows encryption functions are used every day by millions of computers to protect user data. However, these same functions can be used by malware to encrypt a victim’s files and throw away the key. Also, the operating system function which deletes a file is obviously a legitimate function which is used to delete files on a daily basis by those who want to free up space and organized their data. However, this same function can be used to wipe a victim’s computer of files that they care about. What this means is that, as malware authors, we want to try and find out functions which are part of legitimate programs that we can use for malicious purposes. This also means that as operating system developers and security engineers, we need to know all of the functions which could be potentially used for bad and try and protect them from malicious usage while still allowing them to be used for legitimate purposes.
Another example of using good for bad, which we’ve previously covered is Powershell scripting attacks. Powershell is a legitimate and very useful program used by millions of consumers and businesses worldwide. It is also a very powerful program (no pun intended) which has the ability to encrypt files, delete files, and do almost anything on the computer. Powershell is made by Microsoft and comes with every copy of Windows and thus, it is a “trusted” program and is not blocked by any antivirus software. So as a malware author, if we can provide instructions to Powershell to do our bidding, we can again compromise a system because Powershell is trusted. Trusted programs have higher authority over the control of the computer than untrusted and/or blocked programs. For this reason, it is in attacker’s best interest to mess with trusted/legitimate programs in order to either take control of them or use them as vehicle to take control of the computer or other programs. Using legitimate programs for bad or for ways in which they were not intended is called exploiting them, and code which does this is exploit code. The act of actively looking for vulnerabilities or security holes in programs is called vulnerability research and people who write exploit code are called exploit developers. Many people do this for legitimate reasons: for example, a software company may hire such professionals to scan their own software for vulnerabilities in order to patch them before malicious actors can exploit them. However, malware authors and exploit developers can also work together to create malware by incorporating code into the main malware program (or some other resource which it reads from) which exploits vulnerabilities in some other program. WannaCry and Petya are both examples of malware which included exploit code called “ETERNALBLUE” in them which made up just one component (albeit a very critical one) of the malware, enabling it to spread like a worm across networks.
How OS developers try to mitigate these attacks
None of these ideas are new. However, every single piece of malware or exploit uses these very concepts to perform malicious acts even to this day. Due to this, as one would imagine, there is plenty of active development by both operating system developers as well as antivirus developers on new ways to prevent malware from doing its deeds. There are many such ways, but here are a couple foundational ones which are implemented into the operating system and sometimes even the hardware:
As we just discussed, one absolute requirement for malware to execute is the presence of its instructions in a place such that a processor can execute them. Generally, this means the presence of its instructions “in memory,” loaded into RAM. One way to prevent malware is to prevent code from even being able to be injected into a legitimate program in memory. By using memory protections, an operating system can segregate or section off memory on a per-program or per-process basis and then assign privilege levels on memory which lock other programs out of being able to write to or modify the memory of another program. This in turn prevents malware from injecting malicious instructions into a legitimate program because it doesn’t have write privileges to write to the victim program’s memory. The subject gets much more complicated than this and there are a vast array of other considerations, but the general idea of memory protections is the prevention of unauthorized modification of bytes in certain sections of memory so as to mitigate the injection of malicious code or to maintain each processes’ integrity in memory so that it can run without being stepped on by other processes. The idea is that even if a thread could be controlled, without malicious code for it to execute, that wouldn’t be nearly as useful to malware authors.
Code execution protections
There are memory protections which specifically address code execution. Meaning, the OS can tell a program “Sure, you can write all the instructions you want to this particular area in memory, however, I won’t allow you to ever move a thread’s instruction pointer there.” So effectively, this also prevents malware from executing, even if it is able to write malicious code in that area of memory because it prevents execution of the code. Remember that in order for malware to work, it needs to be present in memory AND be executed?
Malware can’t easily inject code into a program if it cannot find it in memory. Using this concept, there is a protection called Address Space Layout Randomization which changes the position of a program each time it is loaded in memory so as to capitalize on this fact. However, if a malware author can find and break into a process and locate its location, then they are able to defeat this protection and inject malicious code anyway.
There are also positional protections at a lower level than ASLR and the operating system itself, such as stack canaries, which protect a program from writing bytes outside the bounds of areas where they should be written, such as writing a different memory location for the instruction pointer to visit in order to execute malicious code.
Conclusion and Next Posts
So, with these types of protections, why is there still malware? Well, in reality, these protections can still be bypassed by exploiting software. For example, if an attacker is able to find a bug in a program which allows him/her to gain access to the executable code section of the program’s memory, then he/she can still execute malicious code even though the system has code execution protection in place. This is where antivirus solutions like PC Matic come into play in order to add an extra layer of protection where the operating system and other user applications fall short and leave holes open.
In future posts, we will be going over the actual details of many of these attacks; describing how they occur and which operating system functions allow them, as well as ways to prevent or detect them. The attacks involve quite a few steps each and can be somewhat complicated, but with the high-level knowledge of information from this post, it becomes easy to understand the why and how of the details and even think of new ways to both create and prevent malware. This is critical for malware research because if we can think ahead of attackers, we can patch security holes before too much damage is done. One example of this, is thinking of ways in which malware can store malicious instructions. We’ve seen this occur in the Windows registry, script files, text files, web servers/web pages, encrypted as an attached resource to an executable file (WannaCry/Petya), placed in executable memory of another program, placed in non-executable memory of another program but moved to executable memory later, as well as inside of internal operating system data structures such as atom tables and the “extra window memory” of a Windows GUI Window Process. In fact, hiding malicious code is the same sort of cat-and-mouse game that is played between law enforcement and criminals who hide contraband in various panels of vehicles. The only difference is, malware analysts don’t have malware-sniffing dogs.