Tuesday, February 20, 2018

Creating Your Own iOS 11.1.2 Jailbreak With The QiLin Toolkit

Introduction
Jonathan Levin recently released the QiLin, a toolkit, whose aim is to "alleviate the
jailbreak enthusiast or security researcher from the nooks and crannies of post-exploitation, and
to standardize jailbreaking in a way which will be as forward compatible as possible." The toolkit is used in his LiberiOS and LiberTv jailbreak - the former of which is based on Ian Beer's aysnc_wake.

At the time of writing this post, I couldn't find a step-by-step tutorial on how to get started with QiLin. So I decided to quickly add some notes on doing just that.

Environment Setup
Here are some steps to get started:
  1. Download/open Xcode
  2. Download Ian Beer's async_wake project(see previous link) 
  3. Download the QiLin object files(see previous link). You need qilin.o as well as sha1.o/sha256.o
  4. Save the .h file from the QiLin wiki to a file.(eg QiLin.h)
  5. Open the async_wake project downloaded in step 2 and add the QiLin object files downloaded in step 3 to the project.  
    Adding QiLin object files
  6. Drag and drop the QiLin.h file saved in step 4 to the project. Now is also a good time to add the iOSBinpack and tar utilities. Note the tar utility is apart of the binpack. At this point, your project should look like the following: 
QiLin header and utilities
Fixing Errors
Trying to build the project at this point will fail. Let's fix that. Before proceeding, ensure you are signed into Xcode and then update the Build Identifier by navigating to General -> Identity -> Build Identifier.

You most likely will end up with an error indicating that sha256.o does not contain bitcode. Navigate to Build Settings and toggle off Enable Bitcode.
Disable bitcode
One other likely error is that of duplicate symbols. Looking at QiLin's exported functions, reveals that one of the methods is called _go.
Duplicate methods
However, the async_wake project also has a go method(see didFinishLaunchingWithOptions). To fix this, highlight the method(Xcode) then right click and select Refactor -> Rename. I updated mine to jbme(). After that the project should build without errors.

LiberiOS
With the environment up and running, we can now delve into the toolkit. First off, the writeup(linked earlier) does provide an idea of which methods to call, but let's have a look at LiberiOS to see how it uses the toolkit. We will then use that as a guide for our purposes. 

LiberiOS first calls doIt() which initializes the toolkit with a call to initQiLin(). The method takes the both the kernel task port and kernel base as arguments. 
init QiLin
Once initialized, the _go() method is called and this is where the magic happens:

 jtool -d _go LiberiOS | grep BL  
 Disassembling from file offset 0xe0e4, Address 0x10000e0e4 to next function, mmapped 0x120f46000  
   10000e0f4     BL   _rootifyMe     ; 0x10000d544  
   10000e118     BL   libSystem.B.dylib::_printf     ; 0x100013aec  
   10000e120     BL   libSystem.B.dylib::_getpid     ; 0x1000138b8  
   10000e124     BL   _getProcStructForPid     ; 0x10000b234  
   10000e128     BL   _ShaiHulud2ProcessAtAddr     ; 0x10000b854  
   10000e14c     BL   libSystem.B.dylib::_fopen     ; 0x100013870  
   10000e170     BL   libSystem.B.dylib::_fprintf     ; 0x10001387c  
   10000e184     BL   Foundation::_NSLog     ; 0x100013738  
   10000e188     BL   _platformizeMe     ; 0x10000bfb0  
   10000e1b4     BL   _borrowEntitlementsFromDonor     ; 0x10000de0c  
   10000e1c4     BL   libSystem.B.dylib::_sleep     ; 0x100013b70  
   10000e1d8     BL   _entitleMe     ; 0x10000d28c  
   10000e1e0     BL   libSystem.B.dylib::_getpid     ; 0x1000138b8  
   10000e1f4     BL   _entitlePidWithKernelEnts     ; 0x10000c904  
   10000e200     BL   libSystem.B.dylib::_sleep     ; 0x100013b70  
   10000e208     BL   _remountRootFS     ; 0x100009c64  
   10000e210     BL   _castrateAmfid     ; 0x10000ac24  
   10000e234     BLR   X8                      ; 0x24107ff7e64028f0  
   10000e25c     BL   _unpackBinariesToPath     ; 0x10000da98  
   10000e264     BL   _disableAutoUpdates     ; 0x100009f10  
   10000e288     BL   libSystem.B.dylib::_copyfile     ; 0x100013840  
   10000e2a4     BL   libSystem.B.dylib::_mkdir     ; 0x100013a2c  
   10000e2e4     BL   _execCommand     ; 0x10000d7cc  
   10000e310     BL   _spawnAndPlatformize     ; 0x10000dfa8  
   10000e344     BL   _spawnAndPlatformize     ; 0x10000dfa8  
   10000e370     BLR   X8                      ; 0x24107ff7e6402910  
   10000e374     BL   libSystem.B.dylib::_getpid     ; 0x1000138b8  
   10000e378     BL   _getProcStructForPid     ; 0x10000b234  
   10000e380     BL   _ShaiHuludProcessAtAddr     ; 0x10000b99c  

The QiLin writeup already has an explanation for what most of the methods do. But even so, just looking at the above list and the IDA disassembly, it's pretty straightforward what is going on. Keep in mind, you might not be interested in including all of the advertised functionality e.g. disableAutoUpdates()/sendStatToJ() in your jailbreak.

Ok, so all we need to do is call those methods - from the toolkit - in our project and we are done. Post exploitation has never been easier!

QiLin Toolkit
As mentioned earlier, the first step in using the toolkit is calling the initQiLin (mach_port_t TFP0, uint64_t KernelBase) method. The method takes tfp0(task_for_pid0) i.e. SEND right to the kernel task as well as the kernel base. async_wake already provides tfp0 via the get_kernel_memory_rw() method. So all we need to determine is the kernel base. Determining the kernel base involves searching kernel memory for the magic bytes 0xfeedfacf.
Determining kernel base
One frequently asked question is how do I determine the kernel base. I used the following code - taken from the Coalfire-Research Labs project:

 // Code lifted from https://github.com/Coalfire-Research/iOS-11.1.2-15B202-Jailbreak  
 extern uint64_t find_port_via_kmem_read(mach_port_name_t port);  
 uint64_t dump_kernel(mach_port_t tfp0)  
 {  
   // ok, where the f*ck is the kernel  
   // uint64_t kernel_base = 0xfffffff00760a0a0; //15B202 on iPhone 6s  
   mach_port_t self = mach_host_self();  
   uint64_t port_addr = find_port_via_kmem_read(self);  
   uint64_t search_addr = rk64(port_addr + 0x68); //KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT  
   search_addr &= 0xFFFFFFFFFFFFF000;  
   printf("[+]\tGoing backwards until magic seen....\n");  
   while (1)  
   {  
     if (rk32(search_addr) == 0xfeedfacf)  
     {  
       printf("[+]\tOk, looks like we've found the beginning of the kernel!\n");  
       printf("[+]\tKERNEL IS AT 0x%llx\n", search_addr);  
       return search_addr;  
     } else {  
       search_addr-=0x1000;  
     }  
   }  
 }  
 // End lift  

iOS kernelcache
As an aside, you can obtain the kernel by navigating to ipsw and selecting your device. Once downloaded, change the extension to .zip and unzip it. The kernelcache will be labeled kernelcache.release.iphone<x>. This file has been compressed and must therefore be decompressed before any analysis can occur.  You can use lzssdec for this, but the easier option is using joker.
Decompressing kernelcache
Show Me Code
Before we actually write any code, let's look at the overall flow:
Jailbreak flow
And with that, the actual code(jbme() snippet) then looks like:
 mach_port_t tfp0 = get_kernel_memory_rw();  
  printf("tfp0: %x\n", tfp0);  
  /*- Determine kernel_base -*/  
  uint64_t kernel_base = 0;  
  kernel_base = dump_kernel(tfp0);  
  /*- Initialize QiLin toolkit -*/  
  qInit = initQiLin(tfp0, kernel_base);  
  /*- Get root privs -*/  
  printf("[+]\tCalling rootifyMe.....!\n");  
  rootMe = rootifyMe();  
  /*- Unsandbox -*/  
  printf("[+]\tUnsandboxing.....!\n");  
  uint64_t origCreds = ShaiHuludMe(0);  
  printf("[+]\tPlatformizing.....!\n");  
  /*- PlatformizeMe -*/  
  platformizeMe();  
  /*- Get Entitlements -*/  
  printf("[+]\tBorrowing Creds.....!\n");  
  uint64_t origEnt = borrowEntitlementsFromDonor("/usr/bin/sysdiagnose","-u");  
  sleep(3);  
  sleep(1);  
  /*- Remount / -rw -*/  
  printf("[+]\tRemount Root file system.....!\n");  
  remountRootFS();  
  /*- Kill amfid-*/  
  printf("[+]\tCastrate AMFID.....!\n");  
  amfid = castrateAmfid();  
   if(amfid){  
     printf("[+]\tFAILED to Castrate AMFID.....!\n");  
   }else{  
     /*- Unpack tools -*/  
     unpackBinariesToPath("binpack64-256.tar", "/jb", "tar");  
     /*- Get your shell -*/  
     copyfile("/jailbreak/etc/motd", "/etc/motd", 0, 0xA);  
     mkdir("/etc/dropbear", 0755);  
     execCommand("/jb/usr/local/bin/dropbear", "-R", "--shell", "/jb/bin/bash", 0, 0, 0);  
   /*- Go back into the sandbox-*/  
     pid_t myPid = getpid();  
     uint64_t myProcStruct = getProcStructForPid(myPid);  
     ShaiHuludProcessAtAddr(myProcStruct,origEnt);  
   }  

Once executed you should end up with your shell :
shell access
The good thing about the toolkit, is that you can view what is happening during each method call - which is very helpful to say the least.

QiLin debug info
QiLin Notes
While using the toolkit, you may notice that not all exported functions are included in the .h file. To fix that, simply determine the arguments of the function you are interested in - using your favorite disassembler - and then update the .h file. For example, to include the unpackBinariesToPath method, determine the arguments:

unpackBinariesToPath function arguments
And then update the QiLin.h file:
Update QiLin header file
The astute reader would have also noticed that _getProcStructForPid and _ShaiHulud2ProcessAtAddr were replaced with a call to ShaiHuludMe(0), additionally recent versions include an unShaiHuludMe method. However, the latter has not yet been included in the latest qilin.o object file.

Conclusion
With the advent of the QiLin toolkit, post exploitation has never been easier. Thanks to Jonathan Levin for this awesome kit. This should be enough to get you up and running. 

Have fun!!!!

UPDATE: Just thought I would add that the following calls can be replaced by unShaiHuludMe (as soon as the object file is updated)

 /*- Go back into the sandbox-*/  
     pid_t myPid = getpid();  
     uint64_t myProcStruct = getProcStructForPid(myPid);  
     ShaiHuludProcessAtAddr(myProcStruct,origEnt);  

The method has already been added to the header file:
unShaiHuludMe in current QiLin.h
Additionally, prior to calling the above block that puts you back in the sandbox, you should check the return value from the call to borrowEntitlementsFromDonor.

Oh and its NOT async_wait it's async_wake. Don't ask how I missed that :) Thanks for pointing that. out.

Sunday, January 28, 2018

A Look Into Why macOS High Sierra's App Store Preferences Can Be Unlocked With Wrong Credentials

Introduction
macOS High Sierra, AAPL's latest OS offering, seems to be "High" on something ever since it's release. Earlier in November of last year, High Sierra users were greeted with the news that anyone could login as root after clicking on the login button several times. Following on the heels of that announcement, a couple weeks ago, users were again alerted to another issue, that saw a user with administrative privileges being able to unlock the App Store menu in System preferences with any password. This latter issue piqued my interest, and I decided to take a look to see if I could understand the underlying flaw. This blog post chronicles that journey.

App Store Preferences
Authentication
In order to understand the bug, it is important that we first examine how the OS handles authentication. For this, we first turn our attention to the Pluggable Authentication Module(PAM). This is a standard UN*X library which aims to abstract and modularize the UN*X authentication API's.

The main PAM module used by macOS is /usr/lib/pam_opendirectory.so.2. The library links with the OpenDirectory.framework, which interfaces with the /usr/libexec/opendirectoryd daemon. The daemon serves as a focal point for all directory requests in the system. The configuration for this daemon is maintained in the /System/Library/OpenDirectory/. This directory, contains a Modules sub-directory which further contains plugins for various types of directory implementations.

Of importance to us is the /System/Library/OpenDirectory/Modules/PlistFile.bundle, which enables access to directory data stored in AAPL's format of property lists(.plist). The corresponding binary is found in /System/Library/OpenDirectory/Modules/PlistFile.bundle/Contents/MacOS and is aptly named PlistFile.

Updating App Store Preferences
With the groundwork out of the way, let's look at a normal authentication flow. The bug was not present on High Sierra 10.3, and so I started my investigations there.

 macho-reverser:opend macho-reverser$ jtool -l opendirectoryd_10_3  
 LC 09: LC_VERSION_MIN_MACOSX    Minimum OS X version:  10.13.0  
 LC 10: LC_SOURCE_VERSION        Source Version:     483.1.4.0.0  

Unlocking App Store preferences and setting a break point on Password resulted in the following hit:

Password Breakpoint hit
Here, opendirectoryd called the  odm_RecordVerifyPassword method from the PlistFile. This is in keeping with what we discussed earlier, given we are authenticating against a local directory(plist format). A quick look at the function, and we see it calls two other methods - sub_18F1 and sub_826B:
Helper Methods

sub_18F1
Based on the above, the method takes three parameters. Note, on x86_x64 architectures, the first six parameters are passed in registers RDI, RSI, RDX, RCX, R8 and R9 respectively. If there are more than six, the program's stack is used for any additional ones. Setting a breakpoint at the function's entry point and examining the parameters reveals the following:

sub_18F1 arg 3
Ok, so this seems to be a dictionary containing data about the user that is trying to authenticate. Keep in mind, that the attributes shown in the above image, can also be read using the dscl tool.

dscl queries

sub_826B
Moving on, this method, seems to be where the action is, and we again start the analysis by examining its arguments. To set the breakpoint, we determine the ASLR slide as follows:

ASLR Slide
And then add that value to the function offset in IDA:

sub_826B offset
Another way of determining the breakpoint address is to rebase the segment in IDA. Using the ASLR slide determined earlier, from IDA, navigate to Edit -> Segments > Rebase -> Image base and enter the value. With the breakpoint set, the function arguments are:

Function parameters
Tracing through the method, we see a call to function sub_591C


sub_591C function call
And printing the arguments:
sub_591C paramter
The method then calls the CCKeyDerivationPBKDF method to derive a key from the password, and returns the resulting key in parameter v29 :

Generate key from CCKeyDerivationPBKDF
 This value is then compared against the stored value with a call to bcmp :

Comparing user supplied value with stored credentials

That is basically the flow for unlocking the App Store Preferences Pane. Also, to kick start this entire process, it seems that opendirectoryd registers a number of handlers for various requests:
API handlers
Of interest to us would be the odm_handle_api_authentication handler, as this handles calls to ODRecordVerifyPassword.

The Bug on High Sierra(10.3.2)
With all that said, on High Sierra(10.3.2) none of this happens. In other words, setting a breakpoint on odm_handle_api_authentication or even VerifyPassword produces not a single hit. Simply put, no validation takes place. It's almost as if the call was removed altogether. However, on High Sierra(10.3.3) - where the bug has been fixed - things go back to normal and once again the odm_RecordVerifyPassword method is called. Go figure!

Conclusion
In the end it seems somewhere along the line there was a breakdown.This resulted in the password verification code block not being called at all. I am not sure of where the exact call into opendirectoryd occurs and that may be worth a further look.....

Hope you found the post interesting. As always, comments/feedback welcome.