2022 Jailbreak Security Summit: Patrick Wardle: Making oRAT, Go.
oRAT is a new piece of macOS malware, written in Go, belonging to a recently uncovered APT group, "Earth Berberoka". After first addressing challenges of reversing Go-based malware, we will provide the first comprehensive analysis of this intriguing threat …but not in the traditional way.
Rather we’ll highlight the creation of a custom command & control server that allowed us to uncover the malware’s full functionality, simply by asking the right questions!
Patrick Wardle is the creator of the non-profit Objective-See Foundation, author of the The Art of Mac Malware book series, and founder of the Objective by the Sea macOS Security conference. Having worked at NASA and the NSA, as well as presenting at countless security conferences, he is intimately familiar with aliens, spies, and talking nerdy. Patrick is passionate about all things related to macOS security and thus spends his days finding 0days, analysing macOS malware, and writing free open-source security tools to protect Mac users.
Patrick Wardle: So for the last talk today, we're going to have Patrick Wardle talk on customization of analyzing a customized C2, making oRAT, Go.
Patrick Wardle: All right. Welcome. Aloha. Today I'm stoked to be - as Tom mentioned - talking about making oRAT, Go. So my name is Patrick Wardle. I am the founder of the Objective-See Foundation. We basically do three things. We create free, open-source security tools for macOS. We work on "The Art of Mac Malware" analysis book series and also organize the Objective by the Sea Mac Security Conference. So today we're going to be performing a comprehensive analysis of a rather intriguing Mac malware specimen known as oRAT. So we're going to start with some background information. We're then going to talk about how to reconfigure the malware and how to understand its protocol. And then the core of the talk will be focusing on creating a custom command-and-control server, which will then allow us to task the malware to uncover or reveal its capabilities without us having to do extensive reverse engineering of the sample. It's also worth pointing out - and I mentioned this on the slide - that the approach we're talking about today is actually applicable to the analysis of essentially any malware sample. So in some senses, you can think of oRAT as simply a case study for this analysis approach. And we'll also see - and I'll briefly touch on this at the end - you know, the focus of today's conference is misinformation and disinformation. And we'll see once we've created a custom command-and-control server that allows us to task the malware, well, this essentially gives us a malware sample and a custom command-and-control server that we can utilize in offensive cyber-espionage operations that give us a very high level of plausible deniability.
Patrick Wardle: OK, so let's dive in first with some background information. So oRAT was originally discovered by Trend Micro. They reported on it and attributed it to a new APT group, noting that this (inaudible) created or had malware samples that would target Windows, Linux and Mac users. The main sample was an interesting specimen called oRAT. And that's the malware sample we are going to be analyzing today. Now, their report was - as we'll see on the next slide - was more of an overview. So, yes, they talked about the infection vector of the malware. They talked about its configuration properties and its likely capabilities. They really didn't perform an in-depth, comprehensive analysis of the sample. And more importantly, their analysis was largely based on static analysis. And those of you who do malware analysis probably know that it's really actually important to do dynamic analysis because, you know, static analysis can give you a good idea of what a malicious sample may likely do, but you always want to confirm your assumptions with dynamic analysis. So I reached out to one of the researchers from Trend Micro and said, you know, hey, how did your analysis go? And they said, oh, we kind of just did a brief triage - again, this was all really the company was looking for.
Patrick Wardle: But also, when I tried to do dynamic analysis, I ran into some showstoppers and really couldn't get the malware to perform like I wanted to. So what we're going to do in this talk is build upon that existing analysis but approach the analysis kind of from a different approach. Now, if you're familiar with Mac malware analysis or any malware analysis, normally, what you do is you run the sample in a debugger, open it in a disassembler and, you know, maybe sniff some network traffic. We're going to kind of perform the analysis from the other side - from the side of a command-and-control server. So the idea is pretty simple. And that is that we are going to perform some basic triage of the malware so that we can understand how to reconfigure it and understand its protocol just enough so that we can task it and then create a command-and-control server and task the malware so that then we can coerce or essentially trick it into revealing its capabilities for us. So we'll do this by running various analysis tools on an infected system where the malware is, then send tasking from the command-and-control server and watch on the infected system what the malware does when we task it.
Patrick Wardle: Of course, since we also have a custom command-and-control when the malware responds to us, we can see what's in its response - again, with the goal of trying to figure out comprehensively the capabilities of the malware without having to spend long hours completely reverse engineering the sample. Now, obviously, there's a few prerequisites for performing this type of analysis. And there's three that we'll go over here shortly. The first is, obviously, we need to figure out how to reconfigure the malicious sample so it talks to our command-and-control server, not the attacker's, right? Secondly, we also have to understand the protocol - the language of the malware - so that once it connects to our command-and-control server, we can send it the correct tasking that it'll understand so that it reveals its capabilities to us. And thirdly, I mentioned we're going to need monitoring tools on an infected system so that when we task the malware, we can observe what it does. This is already solved. I've written a lot of malware analysis tools, so we don't really have to worry about reimplementing those. Now, before we get into that, it's always good to do some initial triage of any malicious sample. And what I do when I get a malicious sample is I perform some very basic steps. I generate a hash of the sample. I use the file command to figure out what type of binary or file it is. This is important because most static and dynamic analysis tools are file type specific. So if this malware were, say, a Python script, it wouldn't make sense to load it in a disassembler. But if it's a compiled binary, yeah, we're going to probably use a disassembler and a debugger. So always good to identify the file type. I also then use the strings utility to look for any embedded strings. This can often tell you a lot of the likely capabilities of a malicious sample. In this case, we can see that the strings reveal that it's packed with UPX. And we'll talk more about that on the next slide. And then finally, I always look at the code signing information. In this case, the malware is actually unsigned. So we know that oRAT is a unsigned, packed, 64-bit Mach-O binary. Now, I mentioned it's packed. We saw that by simply observing the fact that there are embedded UPX strings in the binary.
Patrick Wardle: UPX is a very common packer that a lot of malicious samples use. And if you're familiar with packers, you know, they basically take an executable and compress and obfuscate them. So obviously, if we want to analyze the sample, our first step, therefore, is to unpack the sample so that our triage or continuing analysis can commence. Luckily, binaries that are packed with UPX are very easy to unpack. You just run UPX again with the -d flag, passing the path of the packed binary. And UPX will actually unpack the sample for you. So trivial to unpack. One interesting thing is when we unpack oRAT, we can see it's almost 10 megs. That's huge for a malicious sample. And we'll explain why in a second. So now we've unpacked the sample, we can dig into it a little more, perform some continued triaging. And one of the things I always do is look at the dependencies that a program or malicious sample contains. Now, dependencies are third-party or system libraries that the malware is going to leverage. And it's good to enumerate these because they can tell you a lot about what the malware is doing. For example, if it links against the Disk Arbitration framework, it's likely interested in external drives - perhaps monitoring USB drives. If it links against the AVFoundation framework - this is the Apple framework used to talk to the mic and the webcam - so it's possible or likely then that the malicious sample in that case might be trying to spy on the user via the webcam.
Patrick Wardle: Interestingly, if we look at the dependencies for oRAT, we can see that it only has three. And they're just pretty standard ones. And that's interesting because most malware has a rather extensive list of dependencies, especially if it's fully featured and functionally complete. So I went back and reran the strings command because I thought, OK, maybe there were some static dependencies. And this turned out to be the case. And so from the strings command, we can see various static dependencies. These are libraries that have been directly compiled into the oRAT binary versus dynamically loaded at runtime. And based on their names, we can see they're related to networking, daemonization and cryptography. So this gives us some sense about what the malware might do. Interestingly enough, based on the paths of these static dependencies, we can see that - A, they are likely open source and B, perhaps written in Go. So now we have to go on a brief tangent and talk about reverse engineering binaries that are written in Go, because - as we'll see - there's several unique challenges that you're going to face when reverse engineering a Go binary. Good news is there's various resources that other people have written on analyzing binaries that are written in Go. So I wanted to put these in the presentation.
Patrick Wardle: So if you are interested in diving more into this topic or if you come across malicious samples written in Go that you have to analyze, check out these resources. Now the first question is how do you determine - how do you ascertain if a binary that you're looking at has been compiled from Go source code? Turns out it's trivial, super straightforward. And there's a few ways you can do it. First, if you look at the embedded strings, there's always going to be a Go buildid. This is actually added by the Go compiler. So any binary that has been compiled from Go source code is going to have that string. Also, if you load the compiled binary into a disassembler, very quickly at the entry point of the binary, you're going to see it invoking various Go runtime bootstrap functions. So now that we know that oRAT is compiled in Go, I want to mention some of the challenges you're going to face reversing or analyzing oRAT or any binary that has been compiled from Go source code. So the first thing is these binaries are massive. For example, if we write a simple, hello, world - in Go - you can see that on the slide - and compile that, that turns out to be a binary that's over one megabyte in size, which is huge for a program that literally just prints out, you know, hello, Jailbreak. And the reason for this is that the Go compiler statically compiles a large number of Go libraries into the binary. And this is why the binary is so big.
Patrick Wardle: So the challenge then becomes how do we identify these embedded static libraries and essentially ignore them instead finding the malicious code and focusing on reverse engineering that, right? You don't want to spend your day reverse engineering an open source library, you want to focus on the malicious code and how that's being used. Secondly, a lot of reversing tools are confused or rather tripped up by Go strings. And this is because strings in Go are not null terminated, right? So other programming language - C, C++, Swift, Objective-C - those strings are going to be null terminated. And reverse engineering tools like disassemblers can handle those. So again, if we return to this Hello World Go binary, what I've done is I've compiled it and then opened it in a disassembler. We can see that, sure, the disassembler has located the call to the print function, as expected. But if we look back in the disassembly, it has not been able to determine that the string we are printed is Hello Jailbreak. Now, if we manually follow the cross-reference, indeed, we can find that string embedded in the binary, so that way we can figure out it's being printed. But it's way easier when the disassembler is smart enough to include that directly in-line in the disassembly because a lot of times embedded strings can tell you what the binary is doing. So it's just good to be aware of these shortcomings when you're analyzing a binary that's written in Go.
Patrick Wardle: We're going to jump to one other tangent before we get back to talking about the malware, and that is virtualizing macOS on Apple Silicon. Now, we need to talk about this because what we want to do is we want to run the malicious binary oRAT in a virtual machine and have it connect out to our command-and-control server and then task it. Obviously, we don't want to run the malware on our, you know, base developer system. Previously, on Intel-based systems, I use something like VMware - really great, support it, works, no problems. Unfortunately, VMware does not work on recent versions of Mac, specifically ARM versions of Mac, in the sense that it can't yet virtualize Mac itself. Luckily, Parallels, which is another common virtualization product, can. And this took me a while to figure out, but when I came to Parallels, I was like, ah, breath of fresh air because once you install Parallels, they basically have a prebuilt Mac VM that will work on ARM-based systems, M1 and M2, that they actually just download, and then it's ready to go. So it's like a click of a button, and then you have a virtualized instance of macOS, even on M1 or M2 systems, that you can perform your dynamic malware analysis. So kudos to Parallels.
Patrick Wardle: But I was wondering because when I installed Parallels, I first noticed that the installer asked for my credentials. This is totally unsurprising. Most installers require this information. But I said, you know, installers often get things wrong. A few years ago, I gave a talk at DEF CON showing that a lot of companies' installers are insecure. So I decided to take a quick peek at the installer for Parallels because this was software I was running on my system. And any time I install software on my system, I want to know, am I increasing my attack surface? Or are there trivially exploitable vulnerabilities in this product? So I said, let me take a quick look. So what I did was I ran a process monitor, and I basically just wanted to observe the actions the privileged installer would take after the user provided their credentials to allow the installer to perform the install. And so we can see that, indeed, the processes that are run are running with uid 0, so they're running as root, unsurprising. The user has authenticated it and given the installer these permissions. But if we look at the permissions for the files that they are executing, the binaries backing the processes, we can see they're owned by user.
Patrick Wardle: Now, this is normally a bad thing. If something is running with root privileges but has its file permissions set to users, in theory, a unprivileged local attacker or piece of malware could, again, in theory, surreptitiously modify those and then gain root privileges when that item is run. Now, Parallels did seem to be OK because these binaries were being executed off a disk image, which, by default, is mounted with read-only privileges. So it's not writable. So if we try to modify this user-owned binary that's being executed with root privileges, that would fail. Well, of course, there's a trivial way to get around this, which is just remount the disk image with rewrite. So this is a zero-day vulnerability that affects the Parallels' virtualization product, specifically its installer. And this is how it's trivial to exploit in five steps. So first, what an attacker, an unprivileged attacker or local malware, could do is simply detect the mount of the installer's disk image when Parallels' installer is executed. Step two, then, it can send a SIGSTOP message to the installer that says, hey, pause. Hold it right there. And then in step three, unmount the disk image and remount it with read-write permissions. It could then tell the installer to continue and, once the user has authenticated the installer, modify the files on the disk image, which now are naively executed by the privilege installer as root, giving the malware root access.
Patrick Wardle: So we're going to try to do a demo. It's kind of a race condition, so we'll see how this goes. OK. So step one, the exploit is going to wait for the installer to be run. We're then going to run the Parallels installer. We're going to make sure not to participate in customer experience because I don't want this being sent to Parallels. The installer is going to continue. And if we look at the exploit code - let me make this a little bigger - we're going to see that, eventually - if the Wi-Fi is working - that it's going to detect the disk image. It's going to send a SIGSTOP message to the installer application, remount the disk image as rewritable and then tell the installer, OK, continue. The installer at this point is none the wiser. So what I'm going to do is I'm going to now complete the exploit. I've gone onto the disk image, which now is writable, and surreptitiously modified the files that the installer is about to run. So now when the user authenticates the installer, which, you know, they have to to install, we get a root shell. Actually, we get many root shells. Whee. And I'll prove to you this is a root shell. Come on. And the installer, you know, keeps running, right? We don't want to break anything. So we do like a, who am I? We can see I'm root. So if you're using Parallels, I guess the tl;dr is, like, be careful because their installer is, well, full of bugs.
Patrick Wardle: So let's get back to talking about the malware because that's what this talk is about. But I just wanted to, like, throw in a zero-day to make this talk a little more exciting, right? Tom was like, Patrick, you got to do a great talk. I was like, OK, no pressure - zero-days in the software I'm using. All right, so back to oRAT. So we mentioned the goal is to analyze this malware comprehensively by creating a custom command-and-control server. We mentioned that this required several prerequisites that include, first and foremost, getting the malware to talk to our custom command-and-control server so that, ultimately, we can task it. So we need to figure out how to reconfigure the malware so it talks to our server. So if we return back to the TrendMicro report, we can see that they mentioned that their analysis uncovered the fact that the malware contains its configuration information as an encrypted blob at the end of the malicious binary. So if we open the malicious binary in a hex editor, we can see that, yes, indeed, there is the encrypted blog - blob, and following that is the AES decryption key and the size.
Patrick Wardle: Now, one point to - one thing to note - and this tripped me up for a while. When you unpack the sample, even if you tell UPX to respect this additional overlay data at the end of the file, it doesn't. It'll strip it out. So yes, you'll get an unpacked binary, but the encrypted configuration file component will have been removed. So what you actually have to do is manually add it or decrypt it from a packed sample - again, just something to be aware of.
Patrick Wardle: OK. So we have this encrypted blob. We have the AES decryption key and the size. So let's write a decryptor. Now, don't judge this code too much. This is the first program I've written in Go, but it works, so good enough. So what we can see it's doing is simply opening the binary, and then it uses the AES decryption in GCM mode - that's the mode that the malware uses - to decrypt the config. And on the bottom of the slide, we can see that, yes, indeed, we now have the decrypted configuration information from the malware. So let's take a closer look at this. It starts with some local listener information. This is only relevant when the gateway flag at the bottom is set to true. We're not going to really dive into that. But that basically allows the malware to run as - in gateway mode. In the middle of the configuration information, though, we can see there is a C2 dictionary that contains two members, a network key value pair and an address key value pair. The network key value pair specifies the protocol that the malware should speak, and it can take one of three values that are either TCP, encrypted TCP or encrypted UDP. The address key value pair, unsurprisingly, contains the address of the command-and-control server, specifically the address, IP address, URL and a port. So what we can do to reconfigure the malware is simply modify these values - right? - put in the address of our custom command-and-control server and then reencrypt the config data and jam it at the end of the file so that when the malware runs, it will find that new configuration information.
Patrick Wardle: So my second Go program was and encryptor for this config, and it's basically just the opposite of the decryptor because AES is symmetrical. So as we can see on the slide, we basically take this new configuration information. We encrypt it again using AES in GCM mode. We then add the now-encrypted configuration information at the end, followed by the key and the size because this is the format the malware expects it to be in. And now if we dump the malware, top right, we can see, yes, indeed, there is our new config. Of course, it's encrypted. Most importantly, though, when we set a Netcat listener on the IP address we specified, our command-and-control server, and execute the malware in Parallels, we get a callback. So hooray. This is progress. We now have the ability to reconfigure the malware so it talks to our command-and-control server, which ultimately, we will use to task the malware to reveal its capabilities.
Patrick Wardle: Now, I was doing a bit more triage of the binary, the malware, and I noticed that it seemed to check for various environment variables that started with RK_, and it turned out it was looking for three specific environment variables that could actually allow you to reconfigure the malware for testing purposes. Now, these environment variables are not persistent, right? So if you want to, like, redeploy this malware for your own offensive operations, you're going to have to use the encryptor - decryptor and encryptor method. But for testing, for example, just to reveal the capabilities of the malware, using the environment keys are kind of nice. So there's, as I mentioned, three environment variables. The first one is RK_NET, and if this has been set, the malware will use that to determine what protocol it should use. Similarly, if the RK_ADDR environment variable is set, the malware will use that value as the address of the command-and-control server that it wants to connect out to. And then finally, the RK_DEBUG environment variable, if set to one or anything not zero, this will tell the malware to run in debug mode, which is great because we get various debug strings. And also, this prevents the malware from turning itself into a daemon, which makes it easier to debug.
Patrick Wardle: So here's an example of that. We can see, in a debugger, I've set these environment variables. Specifically, I've set RK_NET to TCP. This says please use TCP in unencrypted. This just makes it easier to sniff and analyze the traffic. And then also, I have then specified the same address of my custom command-and-control server via the RK_ADDR environment variable. Once these variables have been set, I execute the malware in a debugger, and we can see indeed that it goes and reads those values, and it's going to use those to connect out to the command-and-control server. So again, it's just nice to have an alternate method to reconfigure the malware that's a little more simpler, perhaps. OK.
Patrick Wardle: So to reiterate, we now have the ability to coerce the malware to talk to our server, which was prerequisite number one. Prerequisite number two is we need to figure out or understand the language that the malware speaks so when it connects to our command-and-control server, basically saying, task me, we know how to task it correctly. So let's talk now about the protocol that oRAT uses so that we can reimplement that and task the malware to reveal its capabilities. So I did some continuing analysis because I noticed that - and you might have, too - when it connected out to our custom command-and-control server, which previously was just a Netcat listener. Yes, we got a nice POST request, but there was this question mark at the beginning of the traffic. And the question mark is Netcat basically saying, I've got some binary data. I don't know how to print this out. I'm just going to print a question mark. So it's kind of like, OK, something strange is going on, right? There's some other protocol header information. Who knows? So I dug into the malware a little more, and I saw it had a dependency on a library called S-box. S-box, it turns out, is an open-source library. It's an open-source Go library available on GitHub. And if we look at its README file, it describes exactly what it is, and that's on the slide. Basically, it's a simple multiplexing library written for Go.
Patrick Wardle: So what is multiplexing? Well, multiplexing is the idea that you can take multiple networking streams and multiplex them into a single session - one connection. And then on the server-side, the server will demultiplex these streams to then handle each network request. And this is exactly what the malware is doing by means of this S/MUX third-party library. So it's going to do things like generate a network heartbeat request, handle command-and-control server tasking. We'll also see it has the ability to do some tunneling. These, though, are all multiplexed over a single network connection over a single session. And this is beneficial from the attackers or malware's point of view because this is very efficient and also stealthy. Instead of there being three, four, five, eight connections from the malware to its command-and-control server, there's only one. So this is great. And we can see that if we run a network monitor like Netiquette, which is a simple utility I wrote, we can see that, yes, indeed, when the malware connects to the command-and-control server, even though it's sending multiple streams of data, these are all multiplexed over one single connection. Now, this is relevant because this means if we're creating a custom command-and-control server to talk to the malware, we also have to be multiplex aware because, kind of as the diagram shows, we're going to get a single session, but we're going to have to demultiplex that out to handle all the networking streams.
Patrick Wardle: So now let's talk about how we can create such a custom command-and-control server that first and foremost can understand this multiplex logic. So first, I had to figure out how to use this S/MUX package, this library, right? The malware's using it client-side. Obviously, we can use it server-side without having to, you know, rewrite all this multiplex and demultiplex logic ourself. I didn't even know how to, like, use Go packages, so I do what most programmers do and Google. And it turns out you, like, type Go install, and that installs the package. So very straightforward. And then, if you want to use it in your Go code, you just specify the dependency. So, all right, we've figured out how to use the S/MUX package in our own code. So now what we have to do is we have to open a listener - a listening socket and then accept the connection when the malware connects. This isn't any S/MUX multiplex logic yet. This is just listening and accepting a connection in Go.
Patrick Wardle: So you can see code to listen. And then once a connection comes in, we accept that, and then we invoke a method called handle connection to, well, handle the connection. So when we compile this and run this and then execute the malware in the virtualized environment, we can see that it successfully connects to our command-and-control server. So, you know, we're making progress. Now, though, we need to handle the multiplex logic so that we can actually start talking to the malware. So this is where we start using the APIs of that S/MUX library. Specifically, we initialize a multiplex server. We then accept the multiplex stream, which is the initial request that comes in from the malware. And then we handle that stream. Once we compile this code and rerun this, when the malware reconnects, we can see that it happily connects, and we print out all the information. Note now that binary data that Netcat couldn't print out has been handled successfully. This is basically S/MUX's specific protocol data that the APIs now process. So again, making good progress here.
Patrick Wardle: Now, before we go on, we really need to talk briefly about how we can actually task the malware. And this was mentioned in the Trend Micro report, and they basically said that what the malware does is start up a local server client-side to accept requests from the server. And the way that does this is by registering routes. And in the context of this, this is basically parlance for the idea of registering, essentially pattern matching for URL requests that then mat to handler codes. So as we can see on the slide, in theory, the way the oRAT malware expects to be tasked is actually to get a network URL request from the command-and-control server. This will then be processed locally by the malware's server. And then because it has registered these routes, it will look up the request and map that to some code and then execute it. So it's just kind of an efficient way to handle requests from a server to perform specific actions. And we'll look into this a little more.
Patrick Wardle: If we reverse engineer the malware specifically looking at its register router's function, we can see it does this in four steps. So first, it specifies a route handler. This is a chunk of Go code, a method, a function, that will be invoked for each specific request. Step 2 - it then specifies the route request verb. This is either going to be get or post. Step 3 - it specifies the route request. This is the API endpoint, the URL string that it's going to look for to map to the request and then, ultimately, registers or installs that route. We can see this in action. If we run this in a debugger and make a request to the malware, we can see it doing two things. It has a method called Find. This takes the URL request that comes from the command-and-control server, parses it and looks for the appropriate route to handle that. And then it's going to invoke that method and execute that.
Patrick Wardle: So what we can do is we can peruse or triage that register route function in the malware's disassembly and essentially extract the strings or the routes that mapped to the requests that we can task. So I've comprehensively done that. And in the table, we can see they're fairly self-describing. So, you know, we have a ping request, perhaps screenshot, proxy, et cetera, et cetera. So again, the idea is we want to be able to task the malware to recover its capabilities. Well, because the route names are so descriptive, it's likely that we already know what the malware does. But as we'll see, we don't actually have to go in and now reverse engineer all these methods to find out what they exactly do or how they're implemented. Instead, we can just make the request from the command-and-control server. You can imagine, though, a more comprehensive, more complex, more sophisticated malware would likely obfuscate these, right? I see a lot of malware where the tasking logic is looking for a command one, two, five or some obfuscated strings. And in that case, you know, a tasking capability would be even more powerful. Still, though, we don't know what all of these do. For example, agent/net, like, probably does something network-related, but we don't know. So what we're going to do is we're going to task the malware, give it this request and observe what it does, essentially have the malware show us what this capability is versus spending all our time reverse engineering each and all of these logic handlers.
Patrick Wardle: Now, obviously, some of these are going to take parameters. So the next question is how do we figure out what parameters these take? We could maybe task and see if the malware tells us, or we can take a quick peek at the route logic, and very quickly, we can see the strings for the parameter. So for example, if we look at the port scan route, we can see that, unsurprisingly, expect a string host and another string for port. So now we know we can probably perform a port scan. We want to confirm this with our custom and command control server by specifying a host and a range of ports. OK, so now we have the ability for the malware to connect to our command-and-control server. We understand the protocol, the tasking logic. So now let's go ahead and task it to, A, confirm our findings so far and, B, to figure out exactly what it's doing.
Patrick Wardle: Now, the question, though, becomes how? And this took me a while to figure out, but it turns out it's pretty easy, and we'll walk through it. So the malware, like, connected to my command-and-control server. And I said, OK, it's expecting these various requests, get requests, post requests. And when I tried to send it to the malware, it, like, just didn't do anything. And so I was a little perplexed. And remember, I mentioned that the malware starts a local server to handle the requests from our command-and-control server, that then it maps to the tasking logic. And this is where the other researchers got tripped up as well. They said, OK, we can see it call, for example, the start server method, but then if we do, like, a process listing or a socket listing, like, there's no listening socket. So like, how do we test that? And we can confirm that. Again, if we run Netiquette, the network monitor, and look for listening sockets, even though the malware has called a method called start server, well, there's nothing listening for tasking. So that was kind of perplexing.
Patrick Wardle: The answer, of course, is that the server is listening for data that comes across the multiplex session, so the listening is done internal to the process, not actually on an external socket, which in retrospect makes a lot of sense. So what we can do to task the malware to give it these requests for it to perform these various tasking routes is three simple steps. And I say three simple steps. This took me, like, a week, embarrassingly, to figure out. But once it's all working - trivial. So what we have to do is when the malware connects to us, we actually open a new stream on the existing multiplex session. And we can do this using the S/MUX APIs. And that's the library that the malware uses that we also use in our custom command-and-control server. Then once we've opened this stream, we can write the URL request to the malware. This gets multiplexed over to the malware, demultiplexed and then delivered by the malware internally to that local server it set up to handle the requests. And then any responses from the tasking will get written back to that same stream. So again, client - sorry, server side - we can simply read off that stream to get any responses.
Patrick Wardle: So let's see this in action. So here we have our custom command-and-control server. The malware has connected out to us. And once it connects in with this initial join request, as I mentioned on the previous slide, we open a new stream, and then we can now task the malware. So we know that one of the tasking parameters, one of the routes, is /agent/info. And based on info, it's not too much of a far jump to say this is probably a survey. But I wanted to confirm that and also see what - the survey information the malware was collecting. Now, traditionally, if we weren't using a custom command-and-control server, we would have to go in and reverse engineer all the logic that does this. But since we have a command-and-control server that we can task the malware, we can simply say, you know, do your survey and send me the results, which then allow us to figure out exactly what it's doing. And we can see that then in the bottom of the slide that the malware returns to us its survey data. And we can see it contains the version of the operating system, the architecture, the hostname, version and JoinTime. So hooray. We now know exactly how the malware surveys an infected system.
Patrick Wardle: Next up, we can also download files using the download task. And this takes a single parameter, which is the name of the file to download. Now, since the malware has created this local server, the concept of downloading is, you know, uploading essentially. So the download command will download a file from the infected system to your server - exfiltration, right? So if we execute this command when the malware comes in, we can see that, in the response, the malware has sent us the contents of the file. We can also confirm this by running a file monitor on the infected system. And when we task the malware via the download request, we can see that it opens the specified files, reads the content, and sends that back to us. So again, this is an example of running a passive analysis tool on an infected system that reveals exactly what the malware is doing passively.
Patrick Wardle: I was also very interested in the screenshot capability of the malware because there's multiple ways to perform screenshots on macOS. I wanted to see how the malware was doing it. Turns out, though, when I task the malware, the malware just came back and said, hey, sorry I haven't implemented this on macOS. So a little disappointed. But again, I was able to figure out this data not by reverse engineering but simply asking it to perform a screenshot. So again, we're at the point where the malware is just telling us all its capabilities, which is a very efficient approach to comprehensively analyzing it. Next up, I wanted to try the net request because this was an example of one of the tasking strings, which I had no idea what it was. And it turns out it's an instruction to the malware to set up a tunnel. So when you execute the net request, if you specify a host, a port, a network protocol and a timeout, what it'll do is it'll take that host and port information and attempt to connect to that. And then any traffic that's sent from that host or from the command-and-control server will be tunneled between the two. So in this case, the malware essentially become a proxy server. So we can see that on the slide, I have created a Netcat listener on port 1234. I've executed the agent/net command specifying that same host and port. And when the malware then connects out to that, if I write knock, knock on the command-and-control server that is then proxied or tunneled to the host I specified. And then anything I type on the host is proxied back or tunneled back to the command-and-control server. So it's a kind of nice feature. We don't see a lot of malware with tunneling capabilities.
Patrick Wardle: The malware also has the ability to perform port scans, which again is a neat feature to have in malware. It shows it's used for likely reconnaissance or perhaps spreading throughout networks once it's gained initial traction. Similarly to the net command, you specify a host and, in this case, a range of ports. And I was wondering, you know, how does the port scan utility work? How is this task implemented? Well, if we run a network monitor - something like Wireshark - turns out it's implemented very simply. The malware simply tries to connect sequentially to every port in the range you specify. So here we can see that what I did was I opened a host and opened port 1234, just ran a Netcat listener. And the malware went through and tried all the ports. And when it got to that port, it was able to successfully connect, which then means that port is open, which it then reported back to me in the command-and-control server saying, OK, I performed a port scan, and this is the port that I found was open.
Patrick Wardle: Finally, it supports the ability to self-delete. It's kind of cool, like, Q&D, clean off, unbox. And that's done via the kill-self command. So if we execute this from a command and - from our custom command-and-control server, we can see it performs two actions. So if we run a process monitor on the infected host, we can see that it's going to terminate the process. But before it does that, in the file monitor, we see it invokes the unlink command to remove itself. So it deletes itself and then exits. And then in our command-and-control server, unsurprisingly, we get a client disconnected method because the malware has terminated, and so the socket connection is closed.
Patrick Wardle: So I'm going to try a little demo that basically shows our connecting to my command-and-control server and some tasking. This is going to take a second because I'm using parallels. And I'm going to have to restart it because I just infected - or exploited. So this is the Parallels VM. What I'm going to do is I'm just going to run the malware in this - once it logs in. OK, don't need to install Parallels. I'm also going to come over here and close my lovely Rootshell. Goodbye, Rootshell. And I'm going to start my custom command-and-control server that's going to be listening on port 1337. So I'm going to click allow. So this is now listening. I'm now going to come to the VM. And before I run the malware, I'm going to use the environment variables I talked about earlier to configure it and tell it how to connect to my command-and-control server. So let me copy and paste those because I'm a very slow typer. And I hope this works. I was, like, writing this, like, five minutes before the presentation. Tom's like, Patrick, are you ready? I'm like, hang on, got to compile the command-and-control server. So we'll see.
Patrick Wardle: So we've set three - I know this is really small. I don't know how to make this bigger. But so we set the RK Net environment variable. And we set it to a value of TCP. This tells the malware to use TCP unencrypted. We then set the RK_ADDR environment variable to the address of our command-and-control server on port 1337. And then we set RK debug to one. This is just - makes some debug statements appear. So now, in theory, if I run the malware, which is called darwinx64, we should get a command - we should get a connection. So first we can see that - and I can make this one bigger so we can see that. Is that readable? Kind of? Cool. And again, custom command-and-control server, we get a connection. When the malware connects, it always sends a post command saying /join, hey, I'm here. And some other basic information, type zero - I don't know what that means. It's just a check-in. So at this point now, we can open a new stream to task the malware. Again, the value or data we write to the stream is going to be multiplexed over to the malware. The malware is going to demultiplex that, pass it off to its local server. It's going to look up the route for the request we handle and then execute the handler logic for that specified tasking.
Patrick Wardle: So we send these survey requests - which if you remember is /agent/info. We get back a 200 OK. This is the malware saying, cool, yeah, I got that. Everything was OK. And if we read the contents then from that stream, this is the response from that tasking. We saw this on the slides, but this is the survey data from the malware. We then send a second request. We want to exfiltrate for the file .txt. And so again, we write it to that same stream. This is via the /agent/download command. Specify the path of the file we want to exfiltrate. The malware will read that and then send back the results. And just to prove that I'm not making this all up, if we go to the VM and we look at the exfil.txt, we could see it's the file containing that logic. And if we had been running a file monitor on the virtual machine when we made that request to exfiltrate the file, the file monitor would pick up the fact that the malware, as expected, opened that file and read its contents and sent it out to us.
Patrick Wardle: OK, so some conclusions and takeaways. Three main takeaways. First, it's always good to analyze or study the (inaudible) of APT groups because this will give you insight into their sophistication and their abilities. This is especially true in the macOS space. We don't see a lot of macOS APT groups. I mean, we all know they're there. I think the main reason is that the detection software isn't quite as mature as Windows counterparts. This is improving, especially if you're running, like, Jamf Protect or something. But it's still good to study that because this can give you insight into your adversary's skill or lack thereof. Now, the main takeaway is hopefully that analyzing software can be done - analyzing malware can be done in multiple ways. And if you take the approach we talked about today, which is creating a custom command-and-control server, to me, this is both more interesting but also more efficient, right? Once you have the ability to reconfigure or coerce the malware to talk to your server and once you understand the protocol and the tasking mechanisms, you can simply task the malware and watch what it does, and it will reveal all its capabilities to you. So this is far more powerful than static analysis, and I would argue more powerful than simple dynamic analysis - at least far more efficient.
Patrick Wardle: And then third, I mentioned this kind of at the beginning of the talk - and this is probably especially applicable to this audience - we now have a piece of malware that we fully understand, a piece of malware that's fairly feature complete, has some neat things like tunneling, proxy scan, et cetera, et cetera. More importantly, though, we have a command-and-control server that can control this malware. So, you know, go play, right? We could, in theory - hypothetically speaking - use this malware and our custom command-and-control server to target victims, let's say, of interest to us with a very high level of plausible deniability. If those infected systems were ever uncovered, they would - the analyst in that case would likely attribute the infection back to the original malware authors, not us using our own custom command-and-control server. So again, feeding into that disinformation idea.
Patrick Wardle: Now, if you're interested in the tools, techniques and approaches I talked about today, I've actually written a whole book on analyzing Mac malware. Now, this is not a sales pitch because the book is actually 100% free online. Although, if you do want a hard copy, No Starch Press has printed it. So you can pick it up in bookstores, Amazon, et cetera. And all royalties from the book actually go back to the Objective-See Foundation - so also good cause. And speaking of the Objective-See Foundation, I briefly want to thank the companies and products that support it because this is what allows the tools we create to be free and open source, the books to be free online and the conference we organize to be free as well. So again, thanks to these companies and products.
Patrick Wardle: So that is a wrap. Thank you so much for letting me ramble about zero-days and tasking malware and all the things. I believe we have a few minutes for any questions. So the floor is yours. Don't be shy. Any questions? OK, that means you understood it. Amazing. Thank you again for attending. This wraps up the day. I'm going to hand this back to Tom. Also, before that, just thanks to Tom, Kasey, the rest of the Jailbreak crew for putting on this awesome conference, letting me speak. So let's give them all a round of applause.
Tom Mcguire: Thank you, Patrick.