Device forensics on a deception network is, or should be, a bit of a challenge. It is a challenge because the deception net is, or should be, configured to prevent access to the outside—including the internet, as well as local networks—by any code running inside the device. In simple terms, that means that if I put an agent on a decoy and then try to extract data using the agent, all of my data will go to the sinkhole. As I will discuss later when I introduce Infocyte, an excellent network forensic tool, it is almost worthless on the deception portion of the enterprise because its results go to the sinkhole. While that is as it should be, it does not simplify my forensics tasks. We need a better solution for this problem and, fortunately, we have one: device memory forensics is built into the deception net.
In my last post, I pointed out that our target, when we looked at a high-level analysis, seemed to have been the victim of a code injection attack and the code appeared to be assembler. I speculated that it was a buffer overflow attempt—unsuccessful, it appeared—and I speculated that because the top-level analysis suggested http, it might be an attack on the web services. The memory forensics dump expands significantly on that but does support the hypothesis that our attacker was attempting a buffer overflow.
So, getting to the details, we have a lot of ground to cover this time. I'll begin with a little background to set the stage, and then I'll go into the memory dump. Let's start with the buffer overflow and the http that we saw last time. The purpose of a buffer overflow can be, among other things, a denial of service—and it might be an unintentional denial of service if the attacker makes errors—or it might be a way of appending a command or command string to the buffer overflow code.
The actual attack in this case consists of setting the stage by locating a point in memory where the code can be lodged and feeding a large number of characters until the buffer overflows. The idea is to give the attacker access to a shell from which commands can be injected. As you will see, our attacker has tried to compromise several common Windows functions. Our decoy is a Windows server 2008 with nothing particularly special about it beyond the fact that it is configured as a decoy. That means that some common Windows services are present, and it is those our attacker wants to compromise.
The other thing that we need to know about the decoy is its architecture within the deception network. Recall that our BOTsink platform has a number of virtual machines, each one with several interfaces. The interfaces become the decoys. There is more detail on this in an earlier blog post, Part 4: Introducing Forensics. This architectural issue is an important one because the various decoys share memory on the VM that houses them. That means that when I look at the memory dump, I actually am seeing the VM and I need to pick out activity directed at the decoy. That's easier than it sounds.
We already know from the high-level analysis what we are looking for—a buffer overflow attempt—and we know that in our case we have only two decoys that I have set up manually. At the time of the attack, I had not set the BOTsink to create additional decoys on its own. We'll get into that in a future posting, but for now, if it is one of the two decoys I created, it's in the VM's memory.
When I tell the BOTsink that I want a memory dump, I get a couple of things (three, actually, but at the moment I'm only interested in two): a nicely formatted report and a text file with the entire dump. The nice report says everything is okay—I would expect that since the attempt was not successful—and the text file gives me a line by line detail of the VM's memory and what was going on in it. Typically, when we do memory forensics, we only get what is going on at the moment we take a snapshot of the memory. However, in many cases, there are artifacts of prior activities that have yet to be flushed. That is the case here. Our attacker's activity was fresh enough that nothing has yet replaced it in the memory locations in which I am interested.
Before we go any further, let's look at what the attacker was attempting. He went after both internet-facing addresses, and his attacks were different in each case. In one case, he attempted to download and execute code from an external site using an SSH password guessing attack, and in the other, he injected assembly code. In the top-level analysis (session activity) for the SSH attack, we get the following:
Figure 1 - SSH Attack Against One of My Decoys
In this attack, our adversary has successfully logged in with the admin account and attempted to download a file called bot.sh from 126.96.36.199. I dug around a bit and found the code on GitHub (https://github.com/CryptoLions/EOS-Claim-Bot/blob/master/bot.sh if you're inclined to analyze it). This appears to be a crypto-mining bot. Looking at the activity in Figure 1, we can see that the attacker raised the rights to RWX at superuser level and then executed the bot's command. Presumably, it is happily mining away and reporting its results. It's not, of course, since the BOTsink has passed all of its results to the sinkhole. Now, let's move on to the code injection attack which is a lot more interesting.
By examining the BOTsink's event report, we see exactly how the attack progressed. First, our attacker pinged the address, connected to the web server, and ran a port scan. He used a half-open scan to evade detection. He creates an inbound WMI connection and begins to attempt lateral movement. He moves to the system32 directory, still connecting through the vulnerable web services. At this point, he makes an http connection request to \inetpub\logs\LogFiles\w3svc1\u_ex200818.log. He's ready to attempt the code injection part of his attack. Looking at the memory dump, we see that the BOTsink has identified the injected code. We also see where the attacker is attempting to access memory and inject the code.
First, he attempts compromising taskeng.exe. This is a Windows service that tracks pre-programmed tasks and runs them when the operator dictates that they should be run. If our adversary is successful, he can program a task to run when he wants performing whatever function he wishes. He sets up the attack in memory and runs the overflow attempt. I'm not going into the analysis of the setup code, but it is shown in Figure 2 if you want to take the time to reverse it.
Figure 2 - Code Injection Setup
He then begins the buffer overflow attempt in Figure 3 (truncated here for space).
Figure 3 - Buffer Overflow Attempt
The rest of the attack is similar; however, he attempts compromising several other services. The first, after taskeng.exe is svchost.exe. Service host is a standard Windows service and, if you look at running processes, you'll see many instances. That is legitimate and it makes it hard to find a compromised service host file.
The next one is msdtc.exe, which is the Microsoft Distributed Transaction Coordinator and is a legitimate Microsoft service. It allows distribution of transactions across multiple servers such as personal web server or MSSQL server.
After taking another swing at service host, he attempts ossec-agent.exe. This is an agent for host-based intrusion detection, and it is likely that this is an obfuscation attempt. Perhaps he thinks that the buffer overflow will kill the agent, effectively blinding the HIDS.
This is an interesting analysis from several perspectives. First, it is fairly simplistic. It is clear that the attacker knows MS Server 2008, but he is not very careful. There is no effort to clean up after the attack, and it is not clear what his end game is unless he simply wants backdoor access to the server for some reason.
Second, it is equally clear that the method of ingress is via web services. These should be patched. This example shows how easy it is for an attacker to take advantage of a weakness in the http, especially given that it is legitimately accessible via the internet. Giving credit where it is due, though, our attacker is fairly conversant with assembly language. A knowledge of assembler is important for memory forensics.
Finally, the deception network gives us all we need for a detailed analysis of the attack, tells us that it happened, and protects us from it. That intelligence lets us block the offending IP address (although, if the attacker is competent or state-sponsored, it is likely that he will return from a different IP).
Next time, we will introduce a next generation forensics tool that we can use to perform similar analyses on actual devices rather than our decoys. We cannot use this tool on the decoys because—as I have pointed out earlier—the deception network will sinkhole our results. After that, I'll dive into lures and some of the more exotic features of deception networks.