take a jailbreak -stunning guards for ios jailbreak- by kaoru otsuka

70
Take a Jailbreak - Stunning Guards for iOS Jailbreak - Kaoru Otsuka

Upload: code-blue

Post on 22-Jan-2018

237 views

Category:

Devices & Hardware


0 download

TRANSCRIPT

Take a Jailbreak - Stunning Guards for iOS Jailbreak -

Kaoru Otsuka

Who am I

• An iOS hacking enthusiast

• A first-grade high school student at Waseda University High School

Summary

• Escalation to root

• Escalation to kernel

• Disabling mitigations for post exploitations

This talk explains the following 3 Methods

The bugs• Those bugs are found by Ian beer who is at

googleprojectzero

• CVE-2016-7637 - Broken kernel mach port name uref handling on iOS/MacOS can lead to privileged port name replacement in other processes

• CVE-2016-7644 - XNU kernel UaF due to lack of locking in set_dp_control_port

• CVE-2016-7661 - MacOS/iOS arbitrary port replacement in powerd

Attack vector

• CVE-2016-7637 and CVE-2016-7661 to privilege escalation to root

• CVE-2016-7644 to gain the kernel task port

• Applying patches to disable a bunch of mitigations

CVE-2016-7637

This vulnerability can be applied to MITM attack and leads us to gain a root task port.

CVE-2016-7637

• The bug is basically a mistake of an buffer overflow checking (but not buffer overflow bug in ipc_right_copyout)

CVE-2016-7637

• What’s the meaning of “pegging”?

• Suppose it is to prevent a sort of buffer overflow and wrap around to 0

• But the concept of “pegging” is hardly used in xnu

• Can we exploit it?

Mach IPC system

• The ports targeting on this exploit are related to ipc_entry

Source: “Through the mach portal”, ianbeer https://bugs.chromium.org/p/project-zero/issues/attachment?aid=280146

CVE-2016-7637• The point is that

UREFS count is exceeded at 0xFFFE and send “overflow” message to the target port

• The next UREFS count being supposed to be 0xFFFF will result in still retaining the UREFS count being 0xFFFE

• So it’s promoted to an out-of-sync vulnerability

• Let’s take a look at the inside of this exploit in the next slide

CVE-2016-7637• The applied technique is sending 0x10000 messages (of

the same send right) to the target port

• The messages are made to be freed in the process of mach_msg_server (sending invalid messages)

• Spraying those malicious messages to target port’s UREFS and they will be freed after they are counted to UREFS

• This cause the target port being freed!

• Let’s reallocate there and take control of the target port

CVE-2016-7637• There’s a strategy to mitigate for the reallocation of a port

and using it (like Use-After-Free)

• “ipc_entry” has an entry of generation number (in “ie_bits”)

• Generation number entry consists of 6bits bit field.

• Generation number will be checked on the userspace/kernelspace boundary

• Incrementing generation number program is below

CVE-2016-7637

• Generation number is up to 64 (not overlapping)

• So we need a primitive that allows us to loop generation number around to match the generation number at 64th reallocating

• Exploiting reliably, the target port needs to locate at the approximately middle of the freelist

ipc_entry freelist

• It’s a simple LIFO list.

• Though the value indicating the next node isn’t an address but an index of “is_table”

• Unlinking the entry from freelist, old head becomes our next node

CVE-2016-7637

• This topic is to enhance the reliability for this exploit

• Sending N messages (reallocating and freed soon) for the sake of target port to be down the freelist

• After that, sending 62 loops of 2N messages to increment target port’s generation number

CVE-2016-7637

• Review this exploit

• Carry out the UREFS bug

• Sending N messages

• Sending 62 loop of 2N messages

• Target port’s generation number will have been matched

What’s suitable for the target port?

• We need a send right for that port

• The kernel ports can’t be consumed since kernel-owned ports are looked up each time (e.g. bad setting for generation number)

• launchd is a great service

launchd

• com.apple.iohideventsystem can be accessed inside the sandbox and approved to have send right

• Thanks to insecurities of Apple, com.apple.iohideventsystem receives the task port from client of it

• Man-In-The-Middling the target port to capture the task port gives us root task port

Task port

• Task port is assigned per task

• Task port can be obtained by task_for_pid though this API is so restricted

• If we have the task port, we can do anything on its process.

CVE-2016-7661

• Powerd is a client of com.apple.iohideventsystem

• Powerd runs as root

• Crashing powerd process (CVE-2016-7661) brings the target port to receive powerd’s task port

This vulnerability leads us to gain kernel task port

CVE-2016-7644

ipc_port• ipc_entry has a reference to ipc_port (ie_object)

• ipc_entry has only reference for ipc_port object

• It’s maintained in zones which allocated by zone allocator

CVE-2016-7644

• This vulnerability is a sort of Use-After-Free

• set_dp_control_port is only called from root task port so we can’t use this bug without previous exploiting

CVE-2016-7644

• Threads can race to see the same value for dynamic_pager_control_port and release it

• ipc_port_release_send decrements the reference count io_references

How we know the reference count

io_references hits 0?

CVE-2016-7644

• MIG has the feature named no-more-senders notification to notify us if there’s no ip_srights by ipc_port_release_send

• The total count of senders ip_srights is decremented as well as the reference count

• So we’ll use the advantage to notice when it reaches to 0

no-more-senders• ipc_port_release_send drops one reference and one

send right for that port

• ipc_notify_no_senders sends port->nsrequest port when port becomes no-more-senders

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

ie_object

portA

1 reference

0 send right

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

portB

Making a reference for that port

2 references

1 send right

portB has send right to portA though doesn’t receive from portA

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

portB3 references

2 send right

portC

PortC will be the receiver of no-more-senders notification

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

portB3 references

2 send right

portC

Trigger that bug!

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

portB2 references

1 send right

portC

Trigger that bug!

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

portB1 references

0 send right

portC

Trigger that bug!

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

portB1 references

0 send right

portC

send no-more-

We could get the notification so we won

the race!

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

portB1 references

0 send right

portC

Then, what will happen if the portB will be

destroyed?

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

portA

ie_object

0 references

0 send right

portC

Then, what will happen if the portB will be

destroyed?

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entry

ipc_entries

ie_object

0 references

0 send right

What’s ie_object pointing to?

The ipc_entry->ie_object becomes a dangling pointer!

zalloc

• zalloc is a system call that assigns zones corresponding to the size

• zalloc have a local freelist per zone

• zones are freed by memory pressures or mach_force_zone_gc

CVE-2016-7644

• There are zones for ipc_port as I said before

• We allocate a ram_mb*20 number of ports (early ports)

• And we alllocate 20 of ports (middle ports)

• Finally, we allocate 5000 of ports (late ports)

• Not forget to prepare stashed port (corresponding to portB) for all of ports we allocated

CVE-2016-7644

• Causes the bug for each middle ports

• Destroy the stashed ports

• Eventually we’ve made ipc_entries point into dangling port pointers

CVE-2016-7644

• Make the page be able to reallocate other kind of zones(currently it’s for ipc_ports) to capture the kernel port

• We here use another technique to gain kernel task port

Fake ipc_port• Use dangling ipc_ports to retrieve or write ip_context

• Overlapping the zone we targeted on using ool_ports

• ool_ports will misunderstand ipc_ports members

Source: “Through the mach portal”, ianbeer https://bugs.chromium.org/p/project-zero/issues/attachment?aid=280146

CVE-2016-7644

• We are aiming to get the kernel task port

• The kernel task port are supposed to be allocated at bootstrapping kernel which is probably at the first page of ipc_ports

• And we can get/set the ip_context of ipc_ports (with mach_port_{set|get}_context)

CVE-2016-7644• Rewrite the every ip_context of dangling ipc_ports to the addresses

which are around the middle of first ipc_ports’ page (This is CORE technique of our Jailbreak)

• There is a kernelspace to userspace conversion function which converts “port address” to “port object”

• Using this conversion, once our dangling ipc_ports’ ipc_context will be rewritten to the address of kernel task ports, the overlapped ool_ports (whose host_port) becomes kernel_task port with some probability!

• As a result, the ool_ports can be used for receiving kernel_task_port from userspace tasks.

This enables our userspace task to manipulate kernel memory in any way!

Process handling in xnu

• Process handling implementation in xnu is similar to one in BSD

• There is a great deal of benefits to compromise a couple of values in the exact structure

Privilege Escalation

• Most exploits in various platforms likely use this sort of technique

• “proc” structure is provided by per process

• “allproc” variable holds a single linked list for every process’s “proc”

• Rewriting flags and credentials inside “proc” structure!

“proc” structurestruct proc { LIST_ENTRY(proc) p_list; /* List of all processes. */

pid_t p_pid; /* Process identifier. (static)*/ void * task; /* corresponding task (static)*/ struct proc * p_pptr; /* Pointer to parent process.(LL) */ pid_t p_ppid; /* process's parent pid number */ pid_t p_pgrpid; /* process group id of the process (LL)*/ uid_t p_uid; gid_t p_gid; uid_t p_ruid;

..... kauth_cred_t p_ucred; /* Process owner's identity. (PUCL) */ ..... uint32_t p_csflags; /* flags for codesign (PL) */ ..... • At first glance, we just rewrite the values “p_uid” and “p_gid”

• But these fields are’t used inside the kernel process maintaining system

• The real one is inside the “p_ucred” structure!

“kauth_cred_t”

• “typedef struct ucred *kauth_cred_t;”

• “ucred” structure is the original credential maintainer

• So we should do is to copy a highly privileged credential to our process

p_csflags

• Flags for codesign

• Just a 32bit bitfield

• Attributes are defined in bsd/sys/codesign.h

• Editing several values to allow/deny options

p_csflags

• flag |= CS_PLATFORM_BINARY|CS_INSTALLER|CS_GET_TASK_ALLOW

• This allows us to obtain a task from another process

• flag &= ~(CS_RESTRICT|CS_KILL|CS_HARD);

• Omitting complicated options

• Until 2 months ago, it was called KPP but now it is called KTRR or AMCC

• The most annoying mitigation for jailbreakers

• Based on arm’s TrustZone technology

• As of xnu-4570.1.46, it became partially open-sourced (there seems no sync_handler implementation)

AMCC

AMCC

• There are 4 privilege level being established in Trustzone

• EL0 - User-space programs are running here

• EL1- Kernel and iBoot(Bootloader)

• EL2 - Unused in iOS

• EL3 - AMCC/KTRR

AMCC

• AMCC’s strategy is the kernel regions with read-only or read|exec-only permissions to be guaranteed unmodified

• If these regions are determined as an invalid region, AMCC causes kernel panic

• Bypass the regions checking loop so that giving us to write anywhere

AMCC

• In theory, it is inevitable

• Though in practice, it is not inevitable

• Let’s get started into arm abyss!

AMCC

• AMCC needs to retrieve several system registers to know the kernel space states or user space states

• We can set these registers if we have kernel execution

System Registers

• TTBR1_EL1The TTBR1_EL1 controls the base address of translation table 1

• CPACR_EL1The CPACR_EL1 controls access to floating-point, and Advanced SIMD functionality from EL0, EL1, and EL3. The flag register “CPACR_EL1.FPEN” called “NEON” determines if it traps

The loopEL0

FPU Execution

EL3 Executes

sync_handler

EL0 or EL1 IRQ Execution

EL3 Executes

Watchtower

sync_handler

• It’s the core of AMCC

• Supposed it checks KTRR regions integrity and registers integrity

• If there’s invalid pages it will trigger kernel panic

• It sets CPACR_EL1 to not to trap FPU instruction

Watchtower

• It will be called by IRQ instruction

• Its source is located at osfmk/arm64/locore.s

• Restoring a bunch of register and CPACR_EL1 to trap any instruction in EL0 and EL1

The solution• Referred to @qwertyoruiopz Yalu102 jailbreak

• 1st, load dummy Translation Table Base address to TTBR1_EL1

• 2nd, hit CPACR_EL1 not to be trapped by EL3(but it triggers check on EL3 now)

• 3rd, load fake Translation Table Base address to TTBR1_EL1

• 4th, executes “tlbi vmalle1” to invalidate all stage 1 translations

• And patching instruction touching cpacr to nop

The solution

• The code is below

Shadowmapping technique

• gVirtbase - virtual address of translation table entries

• gPhybase - physical address of translation table entries

• Both of them is stored in pmap structure

• The technique is replacing original Translation Table Entry to our fake Translation Table Entry so that giving us write permission to executable pages

Problem• TTBR_EL1 will be reset to 0 on sleeping

• There are 2 kinds of sleeping

• Idle sleep sleeping when cpu is idle

• Deep sleepsleeping when the screen has been black out for more than 30 seconds

• The state of them is stored in “struct cpu_data>interrupt_handler”

AMFI

• Apple Mobile File Integrity

• Exist as a daemon and a kext

• The reason why it is targeted by attacker is that its kext audits the binary’s entitlements, code signing and MAC(Mandatory Access Control) policy

PE_i_can_has_debugger

• Patching a1 for the function to always return true

• If the function always returns true, many of the checks in the kernel will turn off

Disabling sandbox

• MAC policies are stored at “mac_policy_conf->mac_ops”

• These policies needs to be disable for jailbreaking

• Just rewrite those pointers to be null

LwVM

• Light-weight Volume Manager

• LwVM wraps GPT

• _mapForIO traps the root partition writing

• Partition object which mark it lock by a flag is held on heap

• Removing this flag to write anywhere in the root partition

Remount rootfs

• mac_mount prevents mounting root partition as r/w

• Bypassing this protection is as the same doing as we did in LwVM

• vnode(checked every mounting on) have a flag

• Remove that flag and remounting root partition

References

• Through the mach portal (by ian beer)

• yalu102 (by Luca Todesco)

• iOS 10 Kernel Heap Revisited (by Stefan Esser)

• Mac OS X and iOS Internals (by Jonathan Levin)

Demo

Tested on iOS10.1.1 iPhone5s