[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)]