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