 This is Patrick Wardle. We have, I've had tiny nine problems, but little snitch ain't one. And I'll let him just take his talk away. There you go, 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 SINAC. So SINAC does crowdsourced 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 SINAC.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 expoltrate data or talk to a 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. Basically, its goal is to alert the user 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 expoltrate data. It has various components. There is a kernel driver or a kernel extension that runs in ring zero. And we're going to 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 anytime the firewall core detects unauthorized traffic. So it's going to pop up and tell the user, hey, process X is trying to connect to IP address Y and then the user can confirm it tonight. All right, so little snitch is a firewall. So how can we bypass it? That is to say, how can we expoltrate data without being detected or connect to a command and control server without generating any pop-ups 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 undelatable 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. Then we can write our custom code that's trying to expoltrate 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 expoltrate 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 dilip 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 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 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. All right, so let's talk about how to reverse engineered little snitch. Specifically, it's kernel extension with the goal of finding an exploitable kernel vulnerability. Bypassing a firewall, bypassing any security product is 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 wanna find because that's a lot bigger of a problem. So a little snitch kernel extension lives in library slash extensions. It's signed and it started automatically every time the system starts. We look at its info.plist file which has characteristics about it. We can see it's an I-O-Kit driver. So what is I-O-Kit? I-O-Kit 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 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 IO kit provides to pass in user mode data that's processed by an IO kit 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 kinda 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 wanna 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 superclass. The superclass performs some basic 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 superclass then will directly invoke the dispatch method. So we'll 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 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 smethods that ideppro has flagged. So 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's 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 seven, I found an interesting bug. So method seven 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 seven 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 these bytes and then the user mode address of where to copy from. Now if you look at the pseudo code, 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 awhile 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 allocation function they use which is OS malloc 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 gonna 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 gonna truncate that when it goes to allocate that. So in this case it's only gonna 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 two to the 31 or four 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 eight is the code that sets the flag. Basically what method eight 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 four which passes us back some 16 bytes of random data. We then hash that with MD five and a hard coated salt. The hard coated salt is embedded in the user mode components of the little snitch firewall. And then we invoke method eight. Again method eight 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. Okay 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 pass that to an allocation routine that truncated that down to 32 bits. So for example would only allocate a buffer of two bytes or three bytes. But then when I stepped over the copy routine it actually only also copied three or four bytes. So you know 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 B copy. If you look at the assembly for underscore B copy it's a handwritten assembly routine. You can see that although it's 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 gonna 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 gonna 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 B copy 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 B copy 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 gonna try to copy some massive amount of bytes into a small allocated buffer which is gonna 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. We need a tactical solution here. So how can we tame this while kernel copy? Well it turns out that B copy is actually fault tolerant which is a good thing. So B copy again is copying data from user mode into the kernel mode. So what happens is if it hits an un-matt 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 un-matt page. So we can map two pages in user mode, un-matt the second page and then pass in a pointer that say like five bytes before that un-matt 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 un-matt 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 v-table which is a pointer to all its function pointers. So we can corrupt that or we can control that v-table. And once you control a v-table of an object you control you can invoke methods on that. It will use that corrupted v-table which basically gives you RIP. So here's a screenshot of the kernel broken on instruction. It's a call instruction that 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 zero 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, at least let your users know that they should update. So 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 gonna 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 wanna reverse engineer. The AD guys don't always like to share. So I try to share. And also I blog about this stuff. So feel free to check it out if not, 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 wanna 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 email anytime. I love talking about all this stuff. And thank you again, I really appreciate you attending my talk.