This is Patrick Wardle. We have 99 problems, but Little Snitch ain't one. And I will let him just take his talk away. Awesome. Thank you. Aloha. So let's talk about owning Little Snitch. So as you mentioned, my name is Patrick Wardle. I've worked at a bunch of acronym places. Currently the director of R&D at Synac. So Synac does crowd source vulnerability discovery with vetted security researchers. So if you're interested in getting paid to find bugs in our customers' web apps, mobile apps, IoT devices, and network endpoints, check out Synac.com. All right. So we only have 20 minutes, so we're going to jam through a good amount of stuff. We're going to start by briefly talking about what Little Snitch is. We're then going to talk about how to bypass it, so how to exfiltrate data or talk to accounts. So let's get started. So the first thing we're going to talk about is the command and control server without being detected by the firewall. Then we're going to talk about reverse engineering the kernel component, looking for security vulnerability. And then talk about how to exploit a bug that I found. Now, before attacking any technology, it's good to have a basic understanding. So let's briefly talk about what Little Snitch is. So what is Little Snitch? Well, Little Snitch is basically a firewall. Oh, yeah. Basically, its goal is to alert the user to a bug that's there. And this is anytime it sees any unauthorized traffic. So this could be a piece of malware connecting to a command and control server or an attacker trying to exfiltrate data. It has various components. There is a kernel driver or a kernel extension that runs in ring 0. And we're gonna be focusing mostly on this because this is where the security vulnerability I found lies. There's also some pieces that run in user mode. So there is a daemon that runs in the root session that does some rules management. And then there's some interactive components that run in the user session. Most notably, there's a launch agent that is responsible for displaying the alert any time the firewall core detects unauthorized traffic. So it's gonna pop up and tell the user, hey, process X is trying to connect to IP address Y, and then the user can confirm or deny it. All right, so little snitches of firewall. So how can we bypass it? That is to say, how can we exfiltrate data without being detected or connect to a command and control server without generating any popups which would alert the user what we're doing? So the first thing is, let's look at little snitches, firewall rules. And what is this? There is a default, undeletable system rule that says anyone can talk to iCloud. So what we can do is we can reverse engineer the iCloud protocol, and it's pretty basic. It's JSON based. And once we understand the protocol, what we can do is we can set up a command and control server on iCloud. So we can write our custom code that's trying to exfiltrate data or write some malware that connects to a command and control server that is then on iCloud. Now little snitch will see this traffic, but since it conforms to that rule, it won't generate an alert. So basically now we can exfiltrate data, talk to a command and control server without alerting the user at all. Another way to bypass little snitch is by abusing its process level trust. So little snitch, in terms of granularity, assigns trust at the process level This means if a process is allowed to talk to the internet, any code or threads or dynamic libraries within that process can talk to the internet as well. So this means if we can find any way to inject malicious code into any of the processes that little snitch trusts or allows to talk to the internet, we can connect out without the user being alerted. So for example, on my box, GPG keychain is allowed to talk to the internet. Which makes sense, it does key management, checks for updates, stuff like that. Fortunately, GPG keychain is vulnerable to a Dylib hijack attack. This means we can plant a malicious dynamic library on the file system and then every time this application is started, either by the user or programmatically by some malware in the background, the dynamic library will be loaded automatically by the OS loader into the context, into the process context of this trusted application. At that point we can then connect out to the internet. Again, little snitch will see this connection, but since it conforms to a rule, it will allow it without alerting the user. Finally, another way to bypass little snitch is to simply turn it off. So I reverse engineered what happens when the user clicks on stop network filter. And basically what happens is the user mode component of the firewall connects and authenticates to the kernel component and we'll talk about how to do that in a minute. But once it's connected and authenticated, it simply invokes method B. Method B takes a single parameter, a zero to turn off the firewall, or a one to turn it on, so we can write our own code to do this ourselves. Now the best part about this bypass is it's invisible to the UI. So if malware invokes method B with a zero to turn off the firewall to exfiltrate data and then or connect to a command and control server, if the user looks at the status of the firewall, it'll still show that it's on. Alright, so let's talk about how to reverse engineer little snitch. Specifically, it's kernel extension, with the goal of finding an exploitable kernel vulnerability. Bypassing a firewall, bypassing any security product is, you know, pretty easy. You target a certain antivirus product, you target a certain firewall, you're gonna be able to get around it. I mean, little snitch makes it really easy, but still, they should not have exploitable security bugs, right? These are security tools. So, in my opinion, that's kind of what we want to find, because that's a lot bigger of a problem. So the little snitch kernel extension lives in slash library slash extensions. It's signed and it's started automatically every time the system starts. If we look at its info.plist file, which has characteristics about it, we can see it's an Iokit driver. So what is Iokit? Iokit is basically Apple's device driver environment. So it's an object-oriented programming model that's implemented in a subset of C++. And there's a lot of good resources on it, so I'm not gonna spend a lot of time talking about the details, but on the slide we can see this is a skeleton Hello World driver. Basically, you implement a bunch of C++ methods, you compile this, load it into the kernel, and then the kernel proper will invoke these methods. So we can see, for example, it invokes, you know, init, probe, start, and obviously you could put code in these methods to do whatever you want your driver to do. Now, in terms of reversing, specifically looking for exploitable kernel vulnerabilities, I always like to see how and where user mode data is processed. The idea here is if we can pass in some user mode code to, or data to the kernel mode driver, and it processes it in a vulnerable way, we might be able to find a security vulnerability. So it's important to understand what mechanisms Iokit provides to pass in user mode data that's processed by an Iokit driver. So as the slide shows, there's a variety of mechanisms. We're only gonna focus on sending control requests, because this is what little snitch does, and this is also the mechanism where you pass larger structures that might have pointers, sizes, interesting things that the kernel driver might not validate or use correctly. So first, let's kind of talk about a conceptual overview of how a user can invoke a method in the kernel driver. So on the slide we see at the bottom, there's a user or some user mode code, and say it wants to invoke a method, for example, method one. How does it do this? Well, it makes a request to the kernel with a selector. A selector is simply an integer, and as we'll see, it's an index. So this request gets routed into the kernel, and then the kernel proper will forward it to the correct IO kit driver. Specifically, it'll call that IO kit driver's external method function. What the external method function does is use that selector, that integer, as an index into an array of function pointers. These are the methods that the driver exports or exposes to user mode. So if we want to invoke method one, we pass in a one. So once the external method has extracted that function pointer, calls it the dispatch method, it invokes its super class. The super class performs some basic validating validation. And for example, if method one takes a structure of size x and makes sure that the user also passed in a structure, and that the structure they passed in is of size x. Now it doesn't validate what's in that structure, and we'll see in a minute that's kind of a problem. Now once that parameter validation is successful, the super class then will directly invoke the dispatch method. So it will then actually invoke method one. So here's the example, some user mode code of how to actually do this. So there's basically three steps. Step one is you find the driver you want to connect to, and you do this by the driver's name. You then connect to it to create this connection object. And then finally you invoke the method. And there's a bunch of APIs how you invoke the kernel mode method. In this example, we're passing in a structure, so we call the IO connect call structure method. This again gets routed into the kernel. Kernel will invoke the external method of the driver. That will validate the parameters and then call the function that the selector indicated. Okay. So let's get back to little snitch and talk about how to connect to its IO kit driver and then how to enumerate the methods and then audit them. So if we reverse engineer the user mode component, specifically the user mode daemon of little snitch, we can see it connecting to the little snitch driver via the string at underscore obdev underscore LSNKE. So what we can do is we can write our own custom code that tries to connect to that kernel extension as well. And when we compile and run that, lo and behold, we are allowed to connect to that kernel extension and then reconnect to the kernel extension. So what dispatch methods can we call? That is to say, what methods does the little snitch kernel driver export or expose that we can invoke from user mode? So if we reverse engineer the external method of the little snitch IO kit driver, we can see where it uses that selector. And in the disassembly, you can see there is an array of function pointers called S methods that IDAPRO has flagged. So if we double click on that and follow the cross reference, we can see there are all the methods that we can invoke from user mode. So there are 17 of them or so. So I started auditing these methods, because, again, these are the methods we can reach from user mode. And when I got to method 7, I found an interesting bug. So method 7 calls a bunch of helper functions, and one of these helper functions processes the data that gets passed in from user mode. So what method 7 is trying to do is simply copy some bytes from user mode into kernel mode. So it takes a structure that has a size of the kernel, and a size of the bytes, and then the user mode address of where to copy from. Now if you look at the pseudocode, it's probably the easiest to see, unless you prefer to read assembly. But you can see it extracts the size out of the user mode structure, allocates a buffer, and then if that allocation is successful, it copies the data of that same size into the kernel. So you might look at this, and it took me a while, and I didn't really see that there was a problem. This looked like normal valid code. Well, the problem is size matters. Why? Well, the output allocation function they use, which is OSMalloc, takes a 32-bit integer. Well, the copy function, which is copy in, takes a 64-bit integer. So obviously if you pass in a 64-bit size, which is what little snitch extracts from that structure, it's going to truncate that when it allocates it. So for example, if we pass in one with a bunch of zeros and a two, basically a 64-bit value, it's actually going to truncate that when it goes to allocate that. So in this case, it's only going to allocate a buffer of two bytes. Then when it goes to copy, copy in uses the entire 64-bit value. There's no truncation that occurs. So obviously we get a massive heap overflow because it tries to copy some 2 to the 31 or 4 billion bytes into that. All right. So can we exploit this bug? Well, it turns out first before the vulnerable code, there's actually a check in the little snitch driver. And what the check does is it checks some value, which turns out to be an authentication flag. And if that is not set to one, it bails. It doesn't even invoke the buggy code. So we have to figure out how to set this flag so we can reach the buggy code. So I reverse engineered the remaining piece or methods in the little snitch kernel driver, and I found out that method 8 is the code that sets this flag. Basically what method 8 does is it expects a hash from user mode, then it computes a secondary hash itself and compares those hashes. Those hashes match, it sets that flag to one. So this is exactly how we can pass in the correct hash so those both match so we can set this authentication flag. So we connect to the little snitch driver. We invoke method 4, which passes us back some 16 bytes of random data. We then hash that with MD5 and a hard-coded salt. The hard-coded salt is embedded in the user mode components of the little snitch firewall. And then we invoke method 8. Again, method 8 is going to recompute or compute the secondary hash, and since we know how to generate that hash, it will now match and authenticate. So it's basically kind of like security through obscurity for authentication purposes. So we can now authenticate. But can we trigger this bug? So I found this bug in 2013, and when I was stepping through the code in a kernel debugger, I saw, yes, they extracted a 64-bit value, passed that to an allocation routine that truncated that down to 32 bits. So for example, it would only allocate a buffer of 2 bytes or 3 bytes. But then when I stepped over the copy routine, it actually only also copied 3 or 4 bytes. So that was sad, right? It didn't actually seem to trigger the bug. So I looked into the copy in routine to figure out what it was doing. Copy in is a function written by Apple, and under the hood it calls underscore bcopy. If you look at the assembly for underscore bcopy, it's a handwritten assembly routine, you can see that although its function definition says, hey, I take a VM size T, which is a 64-bit value on 64-bit systems, and even the comment says I'm going to use RDX, which is again a 64-bit register, if you look at the assembly code, they actually only use the ECX register. So this means that that 64 value that gets passed in, that size, is also going to get truncated. So unfortunately this at the time wasn't really a bug. Well I did what any normal person did, and I filed a bug report with Apple. I basically said, hey guys, your bcopy routine is buggy. And we all know how Apple is. They take their time. So I had to wait two and a half years for them to fix this. That's only why I'm talking about it now. So they fixed it, which is good. So if you look at bcopy now and look at the assembly, you can see that they correctly use RDX, or the 64-bit registers, as the function definition says it should. So, awesome. We can authenticate and we can trigger the bug. But it's still going to try to copy some massive amount of bytes into a small allocated buffer, which is going to trash the kernel and cause a kernel panic. So basically we need to figure out a way how to exactly control the number of bytes, so we can maybe overflow it by six or seven bytes. You know, we need a tactical solution here. So how can we tame this wild kernel copy? Well, it turns out that bcopy is actually fault-tolerant, which is a good thing. So bcopy, again, is copying data from user mode into the kernel mode. So what happens is if it hits an unmapped page, it handles this gracefully and stops copying. So we can exploit this fact by passing in an address that's close to a page boundary of an unmapped page. So we can map two pages in user mode, unmap the second page, and then pass in a pointer that's, say, like five bytes before that unmapped page. And what's gonna happen is that copy routine is gonna try to copy four billion bytes in, but as soon as it hits that unmapped page, it's gonna stop. So that's perfect, because now we control the exact number of bytes that are copied. So now we have all the components needed for an exploitable heap overflow. We control the size of an allocation buffer in the kernel. We control the values of the bytes copied. There's no constraints. We can put in zeros, nulls, whatever we want. And most importantly, we can copy the number of bytes that get copied into this buffer. So what we can do to exploit this is we can perform a heap spray, some heap feng shui, and basically get a C++ object that we own to be immediately adjacent to this little snitch buffer. We can then overflow the little snitch buffer into that C++ object. And if you know how a C++ object is laid out in memory, it has a vtable, which is a pointer to all its function pointers. So we can corrupt that, or we can control that vtable. And once you control a vtable of an object you control, you can invoke methods on that. It will use the corrupted vtable, which basically gives you RIP. So here's a screenshot of the kernel broken on instruction. It's a call instruction. It uses RAX. I've blown it up a little bigger so you can see the values. But if we look at what RAX is, it's 41, 41, 41, 41. So basically, we control the instruction pointer in kernel mode. Now, unfortunately, we don't have time to talk about how to weaponize this exploit, but there's been a great number of really awesome talks articulating exactly how to do this if you have such a heap overflow. So they talk about how to groom the heap, how to get these C++ objects where you need to be, how to bypass KSLR, SMAP, SMAP, that kind of stuff, and some payloads. Now, one interesting weaponization technique that you can maybe use with this is that even if the bug is patched, this is still a valuable bug. So on modern versions of OS 10, even if you have root access, you can't bypass system integrity protection, and you also can't load unsigned code into the kernel. However, this is a signed driver. So as long as we have a buggy version of this driver, we can bring this to a target, load the driver, and then exploit the vulnerability. Once we exploited it, we have arbitrary code execution in the context of ring 0 in the kernel. So now we can bypass system integrity protection or even run unsigned code in the kernel. All right, so let's wrap this up. So what did the vendor do? Well, the good news is they fixed the bug pretty quickly. I said, hey, guys, you should probably just pull out a 32-bit value and pass that to both the allocation and the copy function. Then you don't really have to care what it's doing under the hood. So that's exactly how they patched it. Fortunately, then, they really downplayed the bug. So the exact quote was, they fixed a rare issue that could cause a kernel panic. This is bullshit. It's not a rare issue. This was in all versions of Little Snitch. It's also not a kernel panic. It's exploitable security vulnerability. So I was a little irked because I was like, come on, guys, you're a security company. You're providing paid security tools. Like, if someone reports to you a security bug, like, let your users know that they should update. So, you know, that was a little bit of a bummer. But I think they've gotten better. All right. I'm assuming you guys are interested in Mac stuff, which is why you're here. So I'm just briefly going to mention my personal Mac security website. I apologize for the shameless plug. But everything is free. A lot of open source Mac security tools. There's a bunch of modern Mac malware samples if you want to reverse engineer. The AD guys don't always like to share, so I try to share. No worries. All right. So we have 54 seconds. So there's time for one or two questions. I'll hang around afterwards if any of you want to chat. So are there any questions about little snitch kernel exploitation? Anything else? Back one slide. Yes. Awesome. Well, thanks again. Feel free to shoot me an e-mail anytime. All the stuff. And thank you again. I really appreciate you attending my talk.