[Translated by {Iikka}{Yli-Kuivila} (ITKST56 course assignment at JYU.FI)] 33C3 preroll music Herald: The Max is a security researcher at Lookout has been doing this about 10 years, he spent a lot of time in obfuscation, exploit development, security research, previous Black Hat speaker, currently focused on mobile security research and working on his PhD. He'll be telling you about some of the internals of Pegasus malware today. With that, I will turn over to Max to take it away. Max: Thank you. applause M: Hi, everyone, say my name is Max Bazaliy, and today we'll talk about the Pegasus Internals. I'm from Kiev, Ukraine, currently work as a security researcher at Lookout and last few years focused on a jailbreak techniques. So that's why I co-founded the Fried Apple team and we're working on a various iOS jailbreak, including eight and nine. So Pegasus. Pegasus is a high quality espionage software that can be used for complete surveillance of a device. Just everything from stealing your personal data up to remotely activating a microphone or camera on a device without any indication it's really happening. So in order Pegasus to work, it need to jailbreak a device first because the iOS sandbox prevents application from spying on each other. So that's why Pegasus rely on a trident exploit chain to completely own a device and install persistence that can be used on each device. Here's a really terrifying list of targeted apps, including even the known as most secure ones, like Telegram, WhatsApp, Viber. And I'm pretty sure you can find your favorite messenger in this list. Before going into a deep technical analysis of the vulnerabilities used, I want to tell a story how we get it - a Pegasus sample. So police met Ahmed Mansoor, who's mostly known for his job as a human right defender. He's even a recipient of Martin Ennals award, sometimes called the Nobel Prize for Human Rights. So I understand this year Ahmed received a message with a text that someone in a state prison got - someone is imprisoned in a state prison. So and he received another text with a similar thing the next day. But previously he was targeted by hacking team in 2012 and got FinFisher in 2011. So now, instead of clicking on the link, he contacted the Citizen Lab because he was working with those guys before. So he sent a link for Citizen Lab to analysis and we are in Lookout research team. We get initial sample and a link from a Citizen Lab. So in this story, I mostly will focusing about technical, part. So in order to work - Pegasus rely on the Trident exploit chain and it uses three stages. So on the first stage, it uses a memory corruption to achieve a remote code execution in a Safari context. After that, it jumps - after this on a device - it jumps to a second stage and uses two vulnerabilities to exploit the kernel. One is used for by - by the Kernel Address Space layout randomisation and another to achieve kernel - a remote - a kernel code execution kernel level code execution. And finally, on the third stage it installs espionage software and uses a special trick to achieve on device persistence. So I will focus on each stage more detailed. The first stage, as they say, is a single use spear-phish url that will be invalidated after the first click. It contains obfuscated JavaScript that first thing it do it checking for a device type: Is it iPhone, is it iPad, is it 32 or 64 bit. And based on information about device processor type the different versions of shellcode will be downloaded. Which is in stage two. And finally exploits remote code execution vulnerability in a webkit in order to execute the shellcode. So what vulnerability will it use it? CVE 4657 remote code execution in WebKit. Basically the vulnerability is use after free that achieved by using two bugs and in a sample that we got it's not stable because it relies on WebKit garbage collector. The problem itself lives in a - MarkedArgumentBuffer that can be exploited by usage of the defined properties. So defined properties is a method that defines new or modified properties directly on object. It takes a few arguments and the object itself and the properties objects which can have descriptors that constitute the property to be defined or modified. It have a pretty simple algorithm, it contain few loops on the very first iteration of each property descriptor checking for a formatting and after that get appended to a descriptor's vector and to make sure that the reference to property descriptors do not become stale, they need to be protected from being garbage collected. For this purpose MarkedArgumentBuffer is used. We see it at the very very end, MarkedArgumentBuffer append. So MarkedArgumentBuffer prevents objects from being deallocated. And after each property -get has been validated and it's okay, the defineOwnProperty associate each user supplied property with a target object. And here is a problem, because it's possi- ble when the defineProperty will be called it's possible to call any user defined JavaScript methods. If in these JavaScript methods garbage collection can be triggered, it will deallocate any unmarked heap-backed object. I will go a little bit deeply in the details. First of all, a few words about MarkedArgumentBuffer and JavaScript garbage collector. So JavaScript garbage collector is respon- sible for deallocating an object from a memory when they are no longer referenced. It runs at a - random intervals and based on the current memory pressure, the current device types and so on. And when garbage collector checking if objects should be deallocated, it walks through the stack and check for reference to an object. A reference to an object also may exist in the application heap, but in this case an alternative is used called the slowAppend. So MarkedArgumentBuffer is initial inline stack contains eight values. Well, that's mean when the ninth value will be added to the MarkedArgumentBuffer the capacity will be expanded. It will be moved from a stack memory to a heap memory. This is what the slowAppend is doing. SlowAppend move stack from a - move buffer from a stack to a heap, and now object not automatically protected from a garbage collection. And to make sure they were not deallocated, they need to be added to heap's markListSet. This is what we see here. So slowAppend trying to acquire heap context and it can be acquired adding an object like, marking an object into markListSet. And here is a problem, because when the heap context can be acquired, it can be acquired for a complex object, only for a complex object. So this mean for primitive types like integer, booleans and so on, they're not heap backed object and they'll be not marked as a markListSet. And there is a bug in the slowAppend. We should be able to call it just once. So this mean when the buffer will be moved from a stack memory to a heap memory and one of the properties will be a simple type, like an integer, so they're not automatically protected by garbage collection and all the next corresponding values will be not protected as well because of the bug in the slowAppend. Here is a picture that's illustrating it and in reality the reference to JavaScript objects still exist. But if, uh, in a in a call to definedOwnProperty method, any of the user supplied methods will be called, they can remove this reference and object will be deallocated. So to summarize, all the information here is how it can be exploited. So we specify an props object which contain 12 descriptors and first nine of them values are simple typed, like zeroes, eight. Which mean that p8, which is the ninth value bit will be added to markListSet. It will trigger the slowAppend and the buffer will be moved from a stack to a heap. And the next corresponding value is just - length and which not_number and array will be not marked by markListSet and not automatically protected by garbage collection. What happened next, when different properties will be called on the length property and you'll try to convert not_number to a number which for that user's defined it toString method will be called. The toString method remove the last reference for an array and forces the garbage collection cycle by allocating a large amount of memory. Which leads that object will be deallocated by garbage collector. The very next thing it is doing is reallocate the new object over a stale one. So this is how specially crafted use after free was used in Safari to achieve remote code execution and to execute a shellcode. The shellcode exist in a second stage, which is a payload which contained the shellcode and compressed data. The most interesting for us is the shellcode because it's used for a kernel exploitation in Safari context and the compressed data basically is a loader that downloads and decrypts the next stage. One of the vulnerabilities used is a CVE 4655, which is a info leak that's used to bypass a kernel address layout randomization. It exploits the information that constructor and OSUnseralizeBinary method they miss bound checking. So that mean that attacker can create OSNumber object with a really high number of bits and call it within the application sandbox where io_registry_entry_get_property_bytes. Here's how it looked like. So OSUnseralizeBinary method to handle binary serializations in a kernel. It converts a binary format to basic kernel data object. It supports different container types, sets, dictionaries, array, object types, strings, numbers and the point of interest in this is the OSNumber. So as we see here, it passed two arguments: value and length and there is no real check that for - for length property. So this mean we can control the length that is passed to an object. And why it is a problem, because here is a constructor for OSNumber:init and as we see the length property passed here is newnNumberOfBits and it overrides the size variable. And the problem that size is used in other methods, in a case that OSNumber numberOfBytes, which leads it return value of numberOfBytes is now fully controlled by attacker. Which is real bad because it's used next in io_registry_entry_get_property_bytes which handle OSNumbers and its use numberOfBytes to calculate the object's length, the OSNumber length. But unfortunately it use a stack based buffer to parse and save OSNumber value. And what happened next, it is copying memory from kernel stack to heap used the attacker controlled length. Which mean we can specify how many bytes will be copied from a kernel stack and returned to user land. This is what happens: the first thing we are doing, we are create a properties array that have a dictionary which have a OSNumber with a high length in our case, is 256. Next, we need to spawn the user client by calling IOService open extended, which will deserialize this OSNumber object and crea- te this object in the - in the kernel. And now we need to read it by calling the IORegistryEntryGetProperty, which leads it - we copied the 256 bytes of the kernel stack memory and the kernel stack memory will contain kernel pointers. And from a kernel pointer, we can determine the kernel base. So now we get a kernel base and we can jump to the next vulnerability, which is CVE 4656 it is use-after-free to achieve kernel level code execution. It exploits information because the setAtIndex macro does not really retain an object and we can trigger it within the application sandbox from OSUnserializeBinary. Again the OSUnserializeBinary it's a function that parse and deserialize objects in the kernel - it support different data types - different container types. And the interesting thing it supports kOSSerialize object. It means that we can create a reference to another object. Really useful in the future, because in a way of deserializing and parsing objects OSUnserializeBinary saved object pointer to a special objects array. And using setAtIndex for it. And as we see setAtIndex just save object pointer to array with some index not retaining it. It's bad, because the next code, which casting OSString to a OSSymbol it is releasing the object pointer. What does it mean? We still have an array that holds all the object pointers, which is objects array, and we just released one of the objects but still hold the pointer. If we can create a reference to an object we can exploit use-after-free. This is what happens because kOSSerializeObject allows us to create a reference and we will just call retain on already deserialized, already deallocated object. This is how exploit look like. So first of all, we create OSdictionary and it will contain a string that due the bug will be deallocated. So now we need to reallocate it with our controlled object to fit in the same memory spot. It's OSString in our case, OSString class in a memory will be 32 bytes. We need to allocate the same size. For this purpose OSData is a perfect candidate because we can control OSData buffer, buffer size and buffer content. So what we can do, we can create a fake OSString with a fake vtable and this fake vtable will point to some digits in the kernel. The very last thing we need to do is trigger a user-after-free by adding a kOSSerializeObject. So once again, OSString got deserialized, deallocated, reallocated to a new object, which is OSData buffer, which will point to the same memory spot: we've got a use-after- free. So after getting use-after-free, Pegasus uses some kernel patches to disable security checks like setuid to easily escalate the privileges, bypass amfi checks by patching out amfi_get_out_of_my_way, disable code signment enforcement by patching cs_enforcement_disable variable and finally, a remount the system partition to be readable, & writable so it can execute a loader for the next stage to download and decrypt the next stage. The next stage contain the real espionage stuff that will be used to sniff all the like SMS, all the calls, all the like personal data. It have a three groups. One is a process group which have a main process: sniffing services model that use a SIP protocol to communicate with command control like a process manager and so on. The next interesting thing is a group of the dylibs, because Pegasus rely on the Cydia substrate - the jailbreak framework called - they renamed it as libdata. It uses Cydia substrate to inject dynamic libraries into application process. So in our case, we have a dynamic libraries for Viber, for Whatsapp which can be injected into the application space to install application hooks. And the last thing is com.apple.itunesstored file. Which is a JavaScript that contain code and the shellcode that will execute - that can execute unsigned code. I will focus on it next. So the bug exists in a JSC binary. JSC binary is like a helper for JavaScript core, JavaScript and in Apple. And it can lead to unsigned code execution. In combination with rtbyddyd trick it can be used to completely gain persistence on the device. It exploits that it is a bad cast in setEarlyValue method and fortunately it can be triggered only from Jesty application context. So what is a problem? It exploits the problem in JavaScript binding SetImpureGetterDelegate which have in C++ for function SetImpureGetterDelegate. This function takes a few arguments - one is the impureGetter and the second one is a generic isObject that will be set as this impureGetter delegate. The problem will be next slide - so we just parse two arguments and call a setDelegate. The setDelegate called set which finally called setEarlyValue. Here is a problem, because there is no real check that the object type passed to setImpureGetterDelegate is really ImpureGetter. So this means that if any other object type will be passed, it will be improperly downcasted as ImpureGetter pointer. That's what happened here. So it's a bad cast that have no real check for object type and which lead that we can overwrite on those object fields. Here are the same function, but now decompiled in IDA Pro. So in our case ImpureGetter is a base variable here and the delegate is this generic JS object. We see that the pointer, which is base plus 16, can be overridden with a pointer to a delegate. Which lead - you see on the right JSArrayBufferView class - if I pass JSArrayBufferView class as a first argument, the m_vector field will be overwritten with a pointer to a delegate. Which is really bad, because it can lead to readable, writeable primitives. To exploit that Pegasus uses two dataviews. I will call them dataview one and dataview two. And to call and setImpureGetterDelegate on both, which leads it m_vector field in the first dataview will be overwritten with the pointer for the second dataview. And now by setting and reading the values on the first dataview we can override object fields in the second. While we need it, we can map the second dataview as entire process memory by overwriting the second dataview arraybuffer offset to be zero by overwriting the second dataview length to be four gigabytes in a case of 32 bit process and set type as fast array type. So basically the second dataview now is mapped to the entire process space and we can getint, setint to get arbitrary read and write anywhere in the process memory. The same thing can be used even to get execution primitive. But in this case, we can call setImpureGetterDelegate twice and instead of exposing the entire process memory, we can leak just an object address. If you can leak an object address, we can produce a function that have like hundreds of try - empty try- catch constructions and force JIT to compile it. And in a - in this process, a special, readable, writeable, executable memory segment will be allocated. We can leak address of this JIT segment, overwrite it with a shellcode and execute. So this is how the bad cast can be used to like re-exploit even a kernel on each boot. It is used with a persistence mechanism which is rtbuddyd. So the problem is that System spawning rtbuddyd service with a special early-boot argument. This mean if you take any other binary signed by Apple and name it as rbuddyd, it will be spawned on a boot. That is what the Pegasus is doing. So they take JSC binary which is signed by Apple, name it as rtbuddyd, then take a JavaScript that contain exploit. Make it a sym link, call it early-boot which leads to when the rtbuddyd will be spawned it with early-boot it will call JSC with the js-exploit instead. So with this trick and the bad cast it re-exploits kernel on each device boot. There is some tricks the Pegasus use to make it harder to reverse engineer, like use one time links. So after you click on any of the link they'll be invalidated and now redirect to Google or other sites. It re-encrypts all the payloads each time they are downloaded just on the fly. And of course, they are trying to hide itself to make it look like a system component. Um, of course, it blocks iOS system updates to make sure you can't - you cannot batch your device just on the fly, to clear all the evidence: clear Safari history and caches and we found a self-destruct mechanism that can be triggered remotely or on a time out. So in addition to this terrifying list of supported applications, it records any microphone usage, any camera usage, GPS, location, keychain passwords, even including the Wi-Fi and the router one. Why they need router - I don't know. Application hooking. So how how it operates. As I mentioned earlier, it use Cydia substrate and with the help of Cydia substrate it preloads dynamic libraries into application process and intercept some critical functions. It uses Cynject to run into already - into already running processes. So this is like a high level picture of how it looks like. So all the application level critical functions and the framework level critical functions are intercepted by Pegasus. So now Pegasus can control them, can collect them, can back them, can send Command & Control and so on. To summarize, Pegasus is a remote jailbreak spotted in the wild. It's pretty scary because it doesn't require any user interaction. And the last similar thing was like five years ago when the comex released his jailbreakme 3. This year Luca Todesco used one of the Trident vulnerabilities for his jailbreaking. I want to say a special thanks to Citizen Lab for helping out with achieving a Pegasus sample. All the Lookout research & response team, the Divergent security guys and all the individual researchers who was involved in the research. So last some useful links which contain a 44 page PDF report with a really, really deep details on the vulnerabilities that is used even with the difference between 32 and 64 bit ones. So if you're interested in. Please take a look. I think this is it do you guys have any questions? applause M: Mm hmm. H: OK, please keep it brief. We only have some minutes left for the questions, and if there are any questions, please go to the microphones in the hall. And we start with the Signal Angel from the Internet. SA: Thank you. Is there any way to build your app, protect it from this exploit? M: Yes, it is, because the Pegasus use some of the known jailbreak techniques it is possible to detect for example that system partition is remounted as readable & writable. It could be one of the indicators that some generic jailbreak is running on a device. Or like check for especially file that Pegasus use but like better check it in general for jailbreak pages, the kernel pages. audience shuffling noise H: Please try to stay a bit quiet. We are still in the middle of the Q & A. If you don't have to leave now, please stay seated until afterwards and if you have to leave now, please do not talk. Microphone three, please. M3: What's the user experience during this? M: User experience, I mean - you mean - you mean when you get a device infected by Pegasus? Well, there is it's scary there is no real indicators on a device that you get something. That you click on the link, the mobile web browser opens and just closes and crashes. There is no new applications spotted on your on visible applications and so on. But in a real it's running like three new system services, but they are not visible to a user. H: Thank you. And please, another question from the Internet. SA: Thank you. Have you any idea how active this exploit is in the world? M: Say it again please? SA: Have you any idea how active this exploit is in the wild? M: I'm sure it was a very targeted attack because, uh, these exploits are pretty expensive. For example, uh, Zerodium now pays 1,5 million for remote jailbreaks like these so I don't think that actors of this like spyware, want to like - want to deal malware accessible for everyone. So I think it's a very very targeted attacks. It is hard to predict how many devices was infected by Pegasus. Now we know about the Mansoor one. So, again, I think it's very, very targeted thing because it's very expensive. H: Thank you for this answer. Microphone number five, please. M5: Do you have any more information on the NSO or the group that's behind it? Are they using any other software? And how spread is this in the wild again? M: Yeah. So in this case, we focused mostly on the technicalities of the Pegasus itself, but Citizen Lab made their investigation on NSO and the like the NSO is like cyber arms dealer. So please take a look about in a Citizen Lab report on that. So they have much more information. H: Do we have a question from the Internet? Am I overlooking anyone? No, then this is it, thank you for your talk. applause postroll music Subtitles created by c3subtitles.de in the year 2022. Join, and help us! [Translated by {Iikka}{Yli-Kuivila} (ITKST56 course assignment at JYU.FI)]