Sunday, October 29, 2017

"BLIND" Reversing - A Look At The Blind iOS App


Intro
"Blind is an anonymous community app for the workplace". In other words, if you as an employee have ever wanted to "speak freely" aka bash your employer/coworkers anonymously - because let's face it that's really what it comes down to - then BLIND is maybe the app you are looking for. This looked like a rather interesting app to "poke" at as I wanted to know just exactly what was happening under the hood.

Scope and Environment
My focus was on the app itself. And I registered with linkedin as opposed to my work email. This had some limitations chief among which was restricted access to the members area. I also did not look at all the functionality. Instead I chose to look at what I considered to be the core components. And even then I did not cover everything.

My environment was as follows:
- Jailbroken iPhone 5S running iOS 9.3.3
- jtool
- IDA Pro
- Hopper
- BurpSuite Pro
- Frida

Jailbreak Detection
The first thing I noticed is that the app did not have any jailbreak detection routine. Now before you go crazy and be like "well that's not a security issue.." I tend to agree with you. I don't think lack of these type checks is a security issue per se. I think more in terms of it being a part of a broader defense in depth approach.

Certificate Pinning
The second observation is that the app did not check the authenticity of the remote endpoint by verifying its SSL certificate(Certificate Pinning); therefore its feasible to perform Man-in-the-Middle (MiTM) attacks to eavesdrop on and tamper with data. Let me hasten to say however that this is not as bad as it sounds because first the attacker would have to trick the user into installing a malicious certificate on the device and second - as you will see later on - the app encrypts the data it sends to the back end.

On a side note the argument that the attacker needs to first trick the user into installing a rogue CA is a common one. And yet while I agree there, there are tools that aim to simplify the process. This blog post from Sensepost is one such example.

One final thing to note here regarding Certificate Pinning, is that the app provides two options for logging in: work email and LinkedIn Verification(see below). As I mentioned before I opted for the latter. A side effect of this choice was that because there was no pinning an attacker could capture the linkedin credentials. Provided of course you installed the malicious cert.

Login options
Obtaining The Binary
Ok so with those "issues" out of the way let's obtain the binary and start reversing. I use the dumpdecrypted dylib. So I simply ssh'd into the device and ran the following:

 root@Jekyl (/var/root)# su mobile  
 mobile@Jekyl (/var/mobile/Documents)# DYLD_INSERT_LIBRARIES=/var/root/dumpdecrypted.dylib /private/var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind  
 mach-o decryption dumper  
 DISCLAIMER: This tool is only meant for security research purposes, not for application crackers.  
 [+] detected 64bit ARM binary in memory.  
 [+] offset to cryptid found: @0x1000d4f28(from 0x1000d4000) = f28  
 [+] Found encrypted data at address 00004000 of length 7995392 bytes - type 1.  
 [+] Opening /private/var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind for reading.  
 [+] Reading header  
 [+] Detecting header type  
 [+] Executable is a plain MACH-O image  
 [+] Opening teamblind.decrypted for writing.  
 [+] Copying the not encrypted start of the file  
 [+] Dumping the decrypted data into the file  
 [+] Copying the not encrypted remainder of the file  
 [+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset f28  
 [+] Closing original file  
 [+] Closing dump file  

Note that on iOS 9.3.3 running DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib from root results in the process you are injecting into being killed:

 root@Jekyl (/var/root)# DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /private/var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind  
 zsh: killed   DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib   
 root@Jekyl (/var/root)#   

The workaround is to first switch to mobile and then cd into /var/mobile/Documents as shown above. Note also that we are able to inject our own dylib because the BLIND app does not have the __RESTRICT Segment.

 LC_SEGMENT_64     Mem: 0x100008000-0x100008000     __RESTRICT  
      Mem: 0x100008000-0x100008000          __RESTRICT.__restrict  

This is a null segment (size 0) which serves to notify DLYD not to trust any DLYD* environment variables.

Identifying Endpoints
One of the things I usually do when looking at a binary is to dump the strings and search for URL endpoints. I then use that list to corroborate Burpsuite traffic.

 macho-reverser:BLIND macho-reverser$ jtool -d __TEXT.__cstring teamblind.decrypted | grep "http"  
 Address : 0x1006dcfd0 = Offset 0x6dcfd0  
 0x1006df366: https://api.linkedin.com/v1/people/~:(id,email-address,first-name,last-name,headline,num-connections,industry,summary,specialties,positions:(id,title,summary,start-date,end-date,is-current,company:(id,name,universal-name,type,size,industry,ticker,email-domains)),educations:(id,school-name,field-of-study,start-date,end-date,degree,activities,notes),associations,interests,num-recommenders,date-of-birth,publications:(id,title,publisher:(name),authors:(id,name),date,url,summary),patents:(id,title,summary,number,status:(id,name),office:(name),inventors:(id,name),date,url),languages:(id,language:(name),proficiency:(level,name)),skills:(id,skill:(name)),certifications:(id,name,authority:(name),number,start-date,end-date),courses:(id,name,number),recommendations-received:(id,recommendation-type,recommendation-text,recommender),honors-awards,three-current-positions,three-past-positions,volunteer)?format=json  
 0x1006df80e: http://us.teamblind.com  
 0x1006e19ad: https://api.linkedin.com/v1/people/~:(id,email-address)?format=json  
 0x1006e75df: https://m.facebook.com/settings/email  
 0x1006e760c: https://www.linkedin.com/m/settings/email  
 0x1006ea5ec: https://docs.google.com/forms/d/e/1FAIpQLSc_J26TtkDL7HXcLeFXC2jy6lb1PmJSPnh51_ng7fr1638p_Q/viewform  
 0x1006ee9c3: https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=%@&scope=%@&state=%@&redirect_uri=%@  
 0x1006f4865: https://krapi.teamblind.com  
 0x1006f4881: https://usapi.teamblind.com  
 0x1006f489d: http://kr.stage.teamblind.com:8080  
 0x1006f48c0: http://us.stage.teamblind.com:8080  
 0x1006f48e3: http://dev.teamblind.com:8080  
 0x1006f4901: http://us.dev.teamblind.com:8080  
 0x1006f4922: https://kr.teamblind.com  
 0x1006f493b: https://us.teamblind.com  
 0x1006f4954: https://krnotifier.teamblind.com  
 0x1006f4975: https://usnotifier.teamblind.com  
 ----

You can also use it to get a list of other potential targets as well. That leads us to Burpsuite. Let's fire it up and examine the traffic. Recall earlier I said that the BLIND app did not implement Certificate Pinning and that it wasn't all that of an issue because the app encrypted the data it sent to the backend. This is what a request looks like.

Sample request
The only thing we can confirm are some of the url's we identified earlier. So we are in fact BLIND. But if this data is in fact encrypted, then how and where is/are the encryption key(s) stored/generated? And can we see the plaintext data the app is sending to the server?

Extracting Classes 
To answer those questions, let's first dump the classes and see if anything looks interesting. We start by listing the segments and there we see references to Objective-C:

 macho-reverser:BLIND macho-reverser$ jtool -l teamblind.decrypted   
 LC 00: LC_SEGMENT_64     Mem: 0x000000000-0x100000000     __PAGEZERO  
 LC 01: LC_SEGMENT_64     Mem: 0x100000000-0x1007a4000     __TEXT  
      Mem: 0x100007a90-0x100663f18          __TEXT.__text     (Normal)  
      Mem: 0x100663f18-0x10066723c          __TEXT.__stubs     (Symbol Stubs)  
      Mem: 0x10066723c-0x10066a560          __TEXT.__stub_helper     (Normal)  
      Mem: 0x10066a560-0x100671ec0          __TEXT.__const       
      Mem: 0x100671ec0-0x1006dcfc9          __TEXT.__objc_methname     (C-String Literals)  
      Mem: 0x1006dcfd0-0x10074ca58          __TEXT.__cstring     (C-String Literals)  
      Mem: 0x10074ca58-0x100754bb2          __TEXT.__objc_classname     (C-String Literals)  
      Mem: 0x100754bb2-0x100767daa          __TEXT.__objc_methtype     (C-String Literals)  
      Mem: 0x100767daa-0x100768e18          __TEXT.__ustring       
      Mem: 0x100768e18-0x100788c4c          __TEXT.__gcc_except_tab       
      Mem: 0x100788c50-0x10078b967          __TEXT.__swift3_typeref       
      Mem: 0x10078b968-0x10078c6a0          __TEXT.__swift3_capture       
      Mem: 0x10078c6a0-0x10078d720          __TEXT.__swift3_fieldmd       
      Mem: 0x10078d720-0x10078e67d          __TEXT.__swift3_reflstr       
      Mem: 0x10078e680-0x10078edc8          __TEXT.__swift3_assocty       
      Mem: 0x10078edc8-0x10078f3c8          __TEXT.__swift2_proto       
      Mem: 0x10078f3c8-0x10078f478          __TEXT.__swift2_types       
      Mem: 0x10078f478-0x10078f4dc          __TEXT.__swift3_builtin       
      Mem: 0x10078f4dc-0x1007a3d20          __TEXT.__unwind_info       
      Mem: 0x1007a3d20-0x1007a4000          __TEXT.__eh_frame       
 LC 02: LC_SEGMENT_64     Mem: 0x1007a4000-0x100980000     __DATA  
      Mem: 0x1007a4000-0x1007a4ba8          __DATA.__got     (Non-Lazy Symbol Ptrs)  
      Mem: 0x1007a4ba8-0x1007a6dc0          __DATA.__la_symbol_ptr     (Lazy Symbol Ptrs)  
      Mem: 0x1007a6dc0-0x1007a6e00          __DATA.__mod_init_func     (Module Init Function Ptrs)  
      Mem: 0x1007a6e00-0x1007cfd20          __DATA.__const       
      Mem: 0x1007cfd20-0x10080f300          __DATA.__cfstring       
      Mem: 0x10080f300-0x100811498          __DATA.__objc_classlist     (Normal)  
      Mem: 0x100811498-0x1008114d0          __DATA.__objc_nlclslist     (Normal)  
      Mem: 0x1008114d0-0x100811890          __DATA.__objc_catlist     (Normal)  
      Mem: 0x100811890-0x1008118e8          __DATA.__objc_nlcatlist     (Normal)  
      Mem: 0x1008118e8-0x1008123b0          __DATA.__objc_protolist       
      Mem: 0x1008123b0-0x1008123b8          __DATA.__objc_imageinfo       
      Mem: 0x1008123b8-0x10092bf38          __DATA.__objc_const       
      Mem: 0x10092bf38-0x100944b20          __DATA.__objc_selrefs     (Literal Pointers)  
      Mem: 0x100944b20-0x100944c88          __DATA.__objc_protorefs       
      Mem: 0x100944c88-0x100946ee0          __DATA.__objc_classrefs     (Normal)  
      Mem: 0x100946ee0-0x100948918          __DATA.__objc_superrefs     (Normal)  
      Mem: 0x100948918-0x10094ee80          __DATA.__objc_ivar       
      Mem: 0x10094ee80-0x100965188          __DATA.__objc_data  
 ------

We can therefore dump the classes and their methods using the objc option of jtool - JCOLOR=1 jtool -v -d objc teamblind.decrypted:


Extract class information with jtool
Note that while I used IDA for this post there is nothing stopping you from using jtool for your disassembly needs. For example if you wanted to zero in on a particular class you could use jtool  like so jtool -d UserControl:getSecretUserDefaultString: teamblind.decrypted

Disassembling class info
Deciphering Encrypted Values
Ok so at this point we can intercept traffic, but alas, it's encrypted. And that's a bummer. Let's proceed with figuring out how the encryption is implemented. As you saw earlier, BLIND allows you to sign up using either your work email or linkedin. Past that screen that, you are presented with the Create Account option:
Sign Up
The settings for the app can be found in com.teamblind.blind.plist and is located at /private/var/mobile/Containers/Data/Application/<app_id>/Library/Preferences/com.teamblind.blind.plist. If you check the file at this point you will notice that it contains the plaintext email - and associated company - you signed up with. You can use the plutil utility to read the file.

plist snippet
Once you select your password and username and hit GET STARTED things change.


Now your email is no longer stored in plaintext but is encrypted(?), and your password, along with several other values get added. Keep in mind it's never a good idea to store passwords or anything sensitive for that matter in plist files.  The astute reader would have noticed that I did not black out the password_enc value. The name of the key suggests the value might be encrypted since it ends with _enc, but is it? One other thing I want to point out is that this value is integral to the encryption process. But we will get to that later. For now let's dig into this value some more.

As it turns out the "encrypted" password is nothing more than an md5 hash, and you can see this in the requestPassword method of the AuthCompleteViewController class.

Create password hash
At 0x000000010004EB50 we get the user supplied value and then calculate the md5 hash at 0x000000010004EB8C. To verify this, we fire up python where you see it is the same value from earlier(plist). Now you know my super secret password.

 >>> import hashlib  
 >>> m = hashlib.md5()  
 >>> m.update("password#1")  
 >>> print m.hexdigest()  
 5486b4af453c7830dcea12f347137b07  
 >>>   


Identifying ViewControllers
If you are wondering how I determined what class to check, I first navigated to the Create Account page and then using cycript determined the ViewController like so:

 root@Jekyl (/var/root)# ps aux | grep blind  
 mobile  4136  0.1 5.8  815696 59532  ?? Ss  4:10PM  0:06.85 /var/containers/Bundle/Application/3C411AB3-6018-4604-97D2-DC2A546EAB85/teamblind.app/teamblind  
 root   4139  0.0 0.0  657104  212 s000 R+  4:11PM  0:00.01 grep blind  
 root@Jekyl (/var/root)# cycript -p 4136  
 cy# [[[UIWindow keyWindow] rootViewController] _printHierarchy].toString()  
 "<UINavigationController 0x15615d000>, state: appeared, view: <UILayoutContainerView 0x157415a30>\n  | <RootViewController 0x1570db260>, state: disappeared, view: <UIView 0x157337ab0> not in the window\n  | <AuthCompleteViewController 0x155f587c0>, state: appeared, view: <UIView 0x155da07a0>"  
 cy#   

Recall as well that your was email encrypted. The encryption routine is also in the requestPassword method. Your email is first retrieved from the plist(NSUserDefaults) and then passed to the encryptHES256 method of NSString ([NSString encryptHES256:])

Get user email
The encryptHES256 method then generates an encryption key with a simple XOR of your password and some "random" value before handing off to the AES256EncryptWithKey method where the actual encryption takes place - technically this method calls another function but you get the picture. Can you spot the "randomness"?
Encrypt user email
Frida
With a little help from Frida we can see this in action. If you are not yet familiar with Frida I HIGHLY recommend that you familiarize yourself with it and add it to your arsenal. I cannot say enough about this tool. I also recommend checking out Frida CodeShare for ready to go scripts. Of course it goes without saying that you should review any code before running it.

Using Frida and the ObjC method observer script from CodeShare we can observe the AES256EncryptWithKey method in action:

 macho-reverser:BLIND macho-reverser$ frida -U --codeshare mrmacete/objc-method-observer -f com.teamblind.blind  
    ____  
   / _ |  Frida 10.6.15 - A world-class dynamic instrumentation framework  
   | (_| |  
   > _ |  Commands:  
   /_/ |_|    help   -> Displays the help system  
   . . . .    object?  -> Display information about 'object'  
   . . . .    exit/quit -> Exit  
   . . . .  
   . . . .  More info at http://www.frida.re/docs/home/  
 Spawned `com.teamblind.blind`. Use %resume to let the main thread start executing!  
 [iPhone::com.teamblind.blind]-> %resume  
 [iPhone::com.teamblind.blind]-> observeSomething('*[* *AES256EncryptWithKey:*]');  
 (0x125fcdca0) -[NSData AES256EncryptWithKey:]  
 AES256EncryptWithKey: password#1^0123456789abcdefghijk  
 0x1001b25cc teamblind!0x11e5cc  
 0x1000e2c7c teamblind!0x4ec7c  
 ----  

Ok so we know how our password and email are encrypted. And by the way figuring out the other encrypted values is basically repeating the above steps. Let's now move to the actual traffic.

As we saw earlier - while monitoring the traffic sent from the device - all requests included a payloadI searched IDA for this string, and after going through the results, found that most of the request parameters were set in the -[NetworkControl encRequestWithParams:showAlert:completionBlock:failBlock:] method.

Payload string search
encRequestWithParams
The method first tries to retrieve a previously generated encryption key and initialization vector (iv), and if that fails it calls out to the makeKeyAndIvForEnc (-[EncriptControl makeKeyAndIvForEnc]) method of the EncriptControl class, and yes that is Encript with i. Security through obscurity maybe..... :)

Generating the encrypted payload
makeKeyAndIvForEnc
This is where things get somewhat interesting, as it appears the encryption key is generated using a combination of the users password and some hardcoded value. Remember the encrypted password(password_enc) from earlier? The method will first try to retrieve it:

Retrieve encrypted password(password_enc)
The method will then generate another md5 hash based on a hardcoded value:

Generate some static value
If their was an issue retrieving the user password, another hash is generated:

Generate second md5 hash
Finally the key is set and this ends up being either a combo of hash1+hash2 or hash1+password_enc

Generate the actual key
So in our case the encryption key should be md5("QkdEhdk") + md5("password#1"), which gives us "c07bcdc2 3522ed81 fb76db0c 0c4387cf 5486b4af 453c7830 dcea12f3 47137b07".

The remainder of the method just sets the initialization vector (iv):

Generate IV
Giving Sight To The Blind
The encRequestWithParams method of the NetworkControl class calls makeKeyAndIvForEnc of the EncriptControl class to setup encryption. Once that is done the encRequestWithParams method goes on to call makePayloadDataWithJsonString of the EncriptControl class. This method then calls aesEncrypt from CocoaSecurity - using the encryption keys and IV from earlier - and returns the base64 encoded cipher-text which is what you see in Burp.
Encrypt the payload
Go back to the jtool -d objc dump for a second, and take note of the instances variables for the EncriptControl class:

Encript instance variables
We now have all the pieces of the puzzle and can therefore craft a Frida script to give us the instance variables i.e. encryption keys etc as well as the plaintext data and corresponding ciphertext:

 if(ObjC.available){  
   var makeKandIv = ObjC.classes.EncriptControl["- makePayloadDataWithJsonString:"];  
     Interceptor.attach(makeKandIv.implementation, {  
      onEnter: function(args) {  
        /* Get Class/Params */  
        var obj = ObjC.Object(args[0]);  
        var params = ObjC.Object(args[2]);  
        /* Get ivars */  
        var ivar = obj.$ivars;  
        // Print ivars values   
        console.log("-----------------------------------------------------------\n");  
        console.log("_encKey: " + ivar["_encKey"] + "\n");  
        console.log("_encIv: " + ivar["_encIv"] + "\n");  
        console.log("_encIvStr: " + ivar["_encIvStr"] + "\n");   
        console.log("_encKeyForDM: " + ivar["_encKeyForDM"] + "\n");   
        console.log("_encKeyForDM: " + ivar["_encIvForDM"] + "\n");   
        console.log("-----------------------------------------------------------\n");   
        console.log("PARAMS: " + params);  
       },  
      onLeave: function onLeave(retval) {  
         console.log("Encrypted Payload: " + new ObjC.Object(retval).toString() + "\n");  
      }  
   });  
 }  

And so our once encrypted burp traffic:
Encrypted burpsuite traffic
Now becomes:
Plaintext data w/ encryption keys

Additionally, hooking the convertDictionaryEncWithResultStr: method of the EncriptControl class prints the plaintext server response. At this point you could consider using the Brida Burpsuite plugin for other options.

Conclusion
Well that's all I had for today. As I pointed out earlier I didn't register a BLIND account and so I didn't have access to the member's only section or other member only functionality. I didn't care to, either, I was just interested in the blob of data I saw going across the wires in the Burp. Given the nature and claims of the app I wanted to know more about what was happening under the hood. As such no malicious traffic was sent to the BLIND servers.

Happy hacking!!!

Disclaimer: This blog is intended for educational purposes only. Any actions and or activities related to the material contained within this Website is solely your responsibility.The misuse of the information in this website can result in criminal charges brought against the persons in question. This author will not be held responsible in the event any criminal charges be brought against any individuals misusing the information in this website to break the law.

Friday, October 20, 2017

Reversing AppleMobileFileIntegrity (AMFI) Part 2

Recap
Let's start with a quick recap of part 1 :
  • We introduced MACF(Mandatory Access Control Framework)
  • Learned a little about KEXTs and how they relate to MACF
  • Got introduced to Policies in the context of MACF
  • Learned to identify policies on both macOS and iOS
  • Saw that AppleMobileFileIntegrity.KEXT is a policy module
  • Delved a little into policy registration to extract and identify callback functions

Ad-hoc Binaries
As discussed in part 1, AAPL has full control of all system binaries and added the hashes of these binaries in the KEXT. These binaries as we saw are referred to as being ad-hoc signed and their validation involves a simple lookup of the hash in the KEXT's TrustCache. 


The TrustCache is located in the __TEXT.__const  section of the KEXT. So if we extract the hash of the amfid binary we should be able to find it in the TrustCache. This is demonstrated below where we first extract the signature:

 macho-reverser:iOS10.3 macho-reverser$ jtool --sig amfid | grep CDHash  
           CDHash:        758a25a4549569ac0d36d3b69a92c937180a18e2 (computed)  
 macho-reverser:iOS10.3 macho-reverser$   

And then locate it in the TrustCache:

 macho-reverser:AMFI macho-reverser$ jtool -d __TEXT.__const com.apple.driver.AppleMobileFileIntegrity.kext  

You will have to search through the output as the CDHash is buried deep into the Cache.
amfid CDHash located in __TEXT.__const section
Now there was a bug here - and we will discuss it next - as for a while you could trick hijack amfid and take over it's operations. AAPL fixed this in iOS 9 and now the KEXT verifies the hash before proceeding - as seen at offset 0xFFFFFFF00644F018.

Verifying amfid CDHash in KEXT 
As a side note to help with symbolication I use joker with the -j option to generate a companion file.

Inter-Process Communication(IPC) 101
*OS is built on the XNU kernel. And at the core of XNU is Mach. This microkernel handles among other things Interprocess communications and messaging. Mach's IPC services rely on the notion of "ports" which serve as communication endpoints. Servers and clients alike can allocate ports, however servers require either some type of locator service to allow clients to find them or otherwise need to be well-known. This is where the bootstrap server comes in. It's accessible to all processes on the system which may communicate with it via the bootstrap_port. Clients can request over this port, that the server look up a given service by its name and match them with its port. Here the name is a fully qualified name like "com.apple.MobileFileIntegrity" for example.

launchd
It used to be that mach_init took on the role of bootstrap_server, however launchd has since taken over this role and claims the port(bootstrap_port) during its startup. launchd is the first user-space program to be started by the kernel and therefore has a pid of 1. It's mission is simple - launch jobs(processes) with a specified criteria. Because all processes are it's spawns they inherit access to the bootstrap_port. And so if a service wishes to register - pre-launchd it would have been via the now deprecated api's bootstrap_create_server and bootstrap_create_service - with launchd it can do so in the server's plist and call bootstrap_check_in which will result in launchd handing over the port when it is ready to service requests:

 kern_return_t bootstrap_check_in(mach_port_t bp, // bootstrap_port   
                                  const name_t service_name, // name of service  
                                  mach_port_t *sp); // out: server port  

Note also that launchd pre-registers the port in the server plist. This server port is usually ephemeral but may also be well known if the key HostSpecialPort is added. Finally on launchd, it runs both systems services(daemons) and per-user services(agents).

The server(daemon's) plists can be found in /System/Library/LaunchDaemons:


 macho-reverser:LaunchDaemons macho-reverser$ ls -l  
 total 48  
 -rw-r--r-- 1 root wheel  678 Jun 15 17:37 bootps.plist  
 -rw-r--r-- 1 root wheel  909 Apr 4 2017 com.apple.AirPlayXPCHelper.plist  
 -rw-r--r-- 1 root wheel  811 Jul 31 21:21 com.apple.AppleFileServer.plist  
 -rw-r--r-- 1 root wheel  729 Jan 26 2017 com.apple.AssetCache.builtin.plist  
 -rw-r--r-- 1 root wheel  433 Mar 1 2017 com.apple.AssetCacheActivatorService.plist  
 -rw-r--r-- 1 root wheel  448 Mar 1 2017 com.apple.AssetCacheLocatorService.plist  
 -rw-r--r-- 1 root wheel  437 Mar 1 2017 com.apple.AssetCacheTetheratorService.plist  
 --  

Ok, that was a lot of theory just now and you may be wondering how it all ties in. Well, if you look at amfid's server plist i.e. com.apple.MobileFileIntegrity.plist you will notice that the service will be registered using  HOST_SPECIAL_PORT(18):


amfid plist w/ host_special_port 
So what this means is that when the KEXT makes the up call to amfid it does so over this special port with a call to _host_get_special_port at offset 0xFFFFFFF00644EEE8. You can see the port being set at offset 0xFFFFFFF00644EEE0:
AMFI.kext up call to amfid
Stealing SPECIAL_PORTS
As we alluded to earlier, prior to iOS 9 amfid's special port could be usurped. This ccould be accomplished with a call to the host_set_amfid_port macro in <mach/host_special_ports.h>.

 #define host_set_amfid_port(host, port)     \  
      (host_set_special_port((host), HOST_AMFID_PORT, (port)))  

Let's look at an example of how we might be able to achieve this. Before proceeding however, let's first look at amfid's(225.50.12.0.0) normal initialization routine. So going back to jtool, we first list the segments:

 macho-reverser:iOS10.3 macho-reverser$ jtool -l amfid   
 LC 00: LC_SEGMENT_64     Mem: 0x000000000-0x100000000     __PAGEZERO  
 LC 01: LC_SEGMENT_64     Mem: 0x100000000-0x100004000     __TEXT  
      Mem: 0x100002bd8-0x100003778          __TEXT.__text     (Normal)  
      Mem: 0x100003778-0x1000039c4          __TEXT.__stubs     (Symbol Stubs)  
      Mem: 0x1000039c4-0x100003c28          __TEXT.__stub_helper     (Normal)  
      Mem: 0x100003c28-0x100003d48          __TEXT.__const       
      Mem: 0x100003d48-0x100003da6          __TEXT.__oslogstring     (C-String Literals)  
      Mem: 0x100003da6-0x100003fb8          __TEXT.__cstring     (C-String Literals)  
      Mem: 0x100003fb8-0x100004000          __TEXT.__unwind_info       
 LC 02: LC_SEGMENT_64     Mem: 0x100004000-0x100008000     __DATA  
      Mem: 0x100004000-0x100004090          __DATA.__got     (Non-Lazy Symbol Ptrs)  
      Mem: 0x100004090-0x100004218          __DATA.__la_symbol_ptr     (Lazy Symbol Ptrs)  
      Mem: 0x100004218-0x100004328          __DATA.__const       
      Mem: 0x100004328-0x100004348          __DATA.__cfstring       
      Mem: 0x100004348-0x100004350          __DATA.__data       
 LC 03: LC_SEGMENT_64     Mem: 0x100008000-0x100008000     __RESTRICT  
      Mem: 0x100008000-0x100008000          __RESTRICT.__restrict       
 LC 04: LC_SEGMENT_64     Mem: 0x100008000-0x10000c000     __LINKEDIT  
 LC 05: LC_DYLD_INFO       
 LC 06: LC_SYMTAB         
      Symbol table is at offset 0x8780 (34688), 69 entries  
      String table is at offset 0x8da0 (36256), 1464 bytes  
 LC 07: LC_DYSYMTAB        
        1 local symbols at index   0  
        1 external symbols at index 1  
        67 undefined symbols at index 2  
        No TOC  
        No modtab  
       116 Indirect symbols at offset 0x8bd0  
 LC 08: LC_LOAD_DYLINKER        /usr/lib/dyld  
 LC 09: LC_UUID             UUID: A3B3B122-E61E-3D39-B082-BEBE3FAA86CC  
 LC 10: LC_VERSION_MIN_IPHONEOS     Minimum iOS version:  10.3.0  
 LC 11: LC_SOURCE_VERSION        Source Version:     225.50.12.0.0  
 LC 12: LC_MAIN             Entry Point:       0x3134 (Mem: 0x100003134)  
 LC 13: LC_LOAD_DYLIB          /usr/lib/libmis.dylib  
 LC 14: LC_LOAD_DYLIB          /usr/lib/libMobileGestalt.dylib  
 LC 15: LC_LOAD_DYLIB          /System/Library/PrivateFrameworks/MobileKeyBag.framework/MobileKeyBag  
 LC 16: LC_LOAD_DYLIB          /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation  
 LC 17: LC_LOAD_DYLIB          /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit  
 LC 18: LC_LOAD_DYLIB          /usr/lib/libSystem.B.dylib  
 LC 19: LC_FUNCTION_STARTS       Offset: 34664, Size: 24 (0x8768-0x8780)   
 LC 20: LC_DATA_IN_CODE         Offset: 34688, Size: 0 (0x8780-0x8780)   
 LC 21: LC_CODE_SIGNATURE        Offset: 37728, Size: 384 (0x9360-0x94e0)   

And then we disassemble main (LC_MAIN) filtering on function calls. This will give us an idea of the general flow:

 macho-reverser:iOS10.3 macho-reverser$ jtool -d 0x100003134 amfid | grep BL  
 Disassembling from file offset 0x3134, Address 0x100003134 to next function, mmapped 0x11a561000  
   10000316c     BL   libSystem.B.dylib::_getopt     ; 0x100003928  
   10000319c     BL   libSystem.B.dylib::_os_log_create     ; 0x100003964  
   1000031dc     BL   libSystem.B.dylib::_openlog     ; 0x100003958  
   1000031e4     BL   libSystem.B.dylib::_setlogmask     ; 0x10000397c  
   1000031f4     BL   libSystem.B.dylib::_syslog     ; 0x1000039a0  
   100003210     BL   libSystem.B.dylib::_bootstrap_check_in     ; 0x1000038bc  
   100003218     BL   libSystem.B.dylib::___error     ; 0x100003898  
   100003220     BL   libSystem.B.dylib::_strerror     ; 0x100003988  
   100003234     BL   libSystem.B.dylib::_syslog     ; 0x1000039a0  
   100003270     BL   libSystem.B.dylib::_fprintf     ; 0x10000391c  
   100003278     BL   libSystem.B.dylib::_exit     ; 0x100003910  
   100003290     BL   libSystem.B.dylib::_dispatch_source_create     ; 0x1000038f8  
   1000032a8     BL   libSystem.B.dylib::_syslog     ; 0x1000039a0  
   1000032b0     BL   libSystem.B.dylib::_exit     ; 0x100003910  
   1000032b8     BL   libSystem.B.dylib::_dispatch_set_context     ; 0x1000038ec  
   1000032c8     BL   libSystem.B.dylib::_dispatch_source_set_event_handler_f     ; 0x100003904  
   1000032d0     BL   libSystem.B.dylib::_dispatch_resume     ; 0x1000038e0  
   1000032d4     BL   libSystem.B.dylib::_dispatch_main     ; 0x1000038c8  
   100003304     BL   libSystem.B.dylib::_syslog     ; 0x1000039a0  
   100003308     BL   libSystem.B.dylib::_xpc_transaction_begin     ; 0x1000039ac  
   10000331c     BL   libSystem.B.dylib::_dispatch_mig_server     ; 0x1000038d4  
   100003324     BL   libSystem.B.dylib::_xpc_transaction_end     ; 0x1000039b8  
   10000333c     BL   libSystem.B.dylib::_syslog     ; 0x1000039a0  
   100003350     BL   libSystem.B.dylib::_syslog     ; 0x1000039a0  
   100003418     BL   libSystem.B.dylib::_memchr     ; 0x100003934  
   1000034f8     BL   0x100002bd8  
   100003520     BL   libSystem.B.dylib::_strlen     ; 0x100003994  
   10000356c     BL   libSystem.B.dylib::___stack_chk_fail     ; 0x1000038a4  
   100003600     BL   0x100002f5c  
   1000036c0     BL   0x1000030ec  
   100003748     BLR   X8                      ; 0x100000cfeedfacf  

Note the call to _bootstrap_check_in that we discussed earlier. ARM'ed with this information, if we remove the calls we don't particularly care about like the logging routines etc,  we end up with the following PoC:

// Code inspired by Jonathan Levin 
#include <mach/mach.h>  
 #include <mach/mach_port.h>  
 #include <mach/mach_host.h>  
 #include <mach/host_priv.h>  
 #include <mach/host_special_ports.h>  
 #include <dispatch/dispatch.h>  
 #include <stdio.h>  
 #include <stdlib.h>  
 
#define CHECK_MACH_ERROR(a) do {kern_return_t rr = (a); if ((rr) != KERN_SUCCESS) \  
 { printf("Mach error %x (%s) on line %d of file %s\n", (rr), mach_error_string((rr)), __LINE__, __FILE__); abort(); } } while (0)  
void handler_f (void *arg)  
 {  
   // Just a skeleton....  
   printf("Handle yo biznizz!! \n");  
 }  
 int main(int argc, char **argv)  
 {  
      char *service_name = "com.apple.MobileFileIntegrity";  
      mach_port_t myhost = mach_host_self();  
      mach_port_t jackedAmfi = MACH_PORT_NULL;  
      host_priv_t host_priv;  
      kern_return_t err;  

      err = host_get_host_priv_port(myhost, &host_priv);  
      CHECK_MACH_ERROR(err);  

      err = mach_port_allocate(mach_task_self(), // task acquiring the port right,  
                               MACH_PORT_RIGHT_RECEIVE, // type of right,  
                               &jackedAmfi); // task's name for the port right;  
      
      // insert a send right: we will now have combined receive/send rights  
      err = mach_port_insert_right(mach_task_self(), jackedAmfi, jackedAmfi, MACH_MSG_TYPE_MAKE_SEND);  
      CHECK_MACH_ERROR(err); 
 
      // Bait and switch  
      err = host_set_amfid_port(host_priv, jackedAmfi);  
      CHECK_MACH_ERROR(err); 
 
      // GCD housekeeping  
      dispatch_source_t ms = dispatch_source_create(&_dispatch_source_type_mach_recv,   
         jackedAmfi,   
         0,          
         &_dispatch_main_q);  
      if (!ms) { printf("Error creating mig source"); exit(1); }  
      dispatch_set_context(ms, &ms);   
      dispatch_source_set_event_handler_f (ms, handler_f);  
      dispatch_resume (ms);  
      dispatch_main();  
 }  

It should be noted that the above PoC is incomplete as the handler logic needs to be added. But fear not, we get into this handler later on in the series.

Wrap Up
We have come to the end of part 2 in the series. To recap:
  • We got a HIGH level introduction to Mach IPC
  • Learned a bit about about launchd
  • Introduced the concept of special_ports
  • Determined how the KEXT communicated with it's daemon
  • Saw how special_ports can be usurped
That's all for now. Hope you enjoyed it! And as always if you notice anything that is not correct please don't hesitate to add a comment below...

References:
1. Mac OS X and iOS Internals: To the Apple's Core - For IPC & launchd section
2. MacOS and iOS Internals, Volume III: Security & Insecurity
3. Mac OS X Internals: A Systems Approach
4. *OS Internals - The Forum

Friday, October 13, 2017

Reversing AppleMobileFileIntegrity (AMFI) Part 1

Disclaimer: This topic has already been covered by countless authors. That said I am in the process of learning more about all things kernel - on both iOS/*OS - and figured I would make some notes as I went along. So consider this my scratch pad of sorts. Hopefully you find it useful.

Introduction
AppleMobileFileIntegrity(AMFI) is a core part of macOS and iOS security. And is by extension the bane of many a jailbreakers existence. It ensures the integrity of the code running on the OS and is the brains behind XNU's code signature verification.

AMFI consists of a KEXT(AppleMobileFileIntegrity.kext) and a usermode daemon(amfid). The KEXT by definition operates in kernel mode. Recall that AAPL fully controls ALL system binaries on iOS. This allowed them to compile a list of the individual hashes of these binaries and stash them in the kernel - more specifically in a area referred to as AMFI's Trust Cache. As a side note these pseudo signatures are referred to as ad-hoc. Validation is therefore a simple lookup of the hash in the Trust Cache. For third party apps however, things are a bit different. As in such instances AMFI calls up to it's partner in crime amfid the user-mode daemon for validation.

This series of posts will look at both AppleMobileFileIntegrity.kext and amfid. Let's begin by discussing the Mandatory Access Control Framework(MACF) a key component of APPL's ecosystem.

Background (MACF)
"The Mandatory Access Control Framework (MACF) is the substrate on top of which all of Apple's security, both MacOS and iOS, is implemented. By implementing a rich set of callouts for every user-controllable aspect of kernel functionality - system calls and Mach traps alike - it allows interested kernel components to enforce any set of rules - a policy - desired." - (Jonathan Levin, *OS Internals Volume III, p.45)

Of note is the fact that this a framework and the logic is implemented in kernel extensions(KEXTs) which must first  register their intention with the framework for any number of operations the framework intercepts. MACF will then call out to these extensions aka policies and allow them to do their thing. The general flow is as follows, let's say some process wants to perform a system call or mach trap:
  1. The corresponding function in the kernel is called(sysent/mac_trap_table -> syscall/trap #)
  2. This function reaches out to the MACF and basically asks "did any policy modules request to hook this functionality?"
  3. MACF then says "hold my beer, and calls out to the interested policies"
  4. The interested policy then says "ok dude you want to perform this operation...you're good to go....or nope not today"
  5. Now this is where it gets interesting, sometimes the policy module has a partner in crime operating in user mode and therefore has to make an up call. Enter in amfid. 
Ok, so how do we see these policies on the system? On macOS we can use jtool to(for i in *.kext; do if jtool -S $i 2> /dev/null | grep mac_policy > /dev/null; then echo $i; fi; done) iterate over the /System/Library/Extensions/ directory as shown below:

Enumerating policy clients on macOS
Kernel Extension(KEXTs)
Now because this blog series is about AMFI, the KEXT we are interested in is of course AppleMobileFileIntegrity.kext. If you are wondering what a KEXT is have no fear I got you. XNU by itself cannot provide all required functionality and so you need kernel extensions for things like drivers and plugins. In windows parlance a KEXT would be the equivalent of a driver (.sys). More importantly, KEXTs are also just Mach-O Bundles which it means it can be sliced and diced using a myriad of available tools.

The KEXT directory contains an Info.plist file that just has some metadata. Of note is the  CFBundleExecutable key which as the name implies contains the name of the binary that will be executed.
Info.plist snippet
The rest of the Contents/MacOS directory looks like this, where we see the actual AppleMobileFileIntegrity binary and verify that it is indeed a mach-O binary:

KEXT layout
One cool tool that can help with visuals is KextViewr from the Objective-See arsenal. While you are at it check out the rest of their AWESOME FREE tools.

Viewing KEXTs with KextViewr
The default location for KEXTs on macOS is - as we saw above - /System/Library/Extensions/. Not so on iOS though, as KEXTs are instead prelinked in the kernel.

So while getting the above information - policy clients - is straight forward on macOS, because the KEXTs are prelinked in iOS we have to extract them directly from the kernel. Before we do that we need to obtain said kernel. There are number of options available and of those I usually use one of the following:
  1. kdump from the iOS Kernel Utilites for pre iOS 10.x kernels. Keep in mind if you use this method certain parts of the kernel will be jettisoned (so there goes the symbol table.......).
  2. Extract from respective Firmware (starting with iOS 10 the kernel is no longer encrypted...thank the kernel gods)
Once obtained, you can use the joker tool with big K as shown below:
Enumerating policy clients on iOS
This will extract the KEXT's to /tmp directory. And then to identify policy kexts - recall from earlier that the kext has to register their interest with MACF - we can grep for mac_pocliy_register. More on this function later.

Enumerating callouts
Ok so at this this point we know that AppleMobileFileIntegrity.kext is a policy KEXT. But what is exactly is a policy and what does it look like from a reverse engineering perspective. Simply put, a policy is just a set rules that gets applied on full or partial kernel operation callouts. The interested KEXT first defines a mac_policy_conf structure and then registers it with MACF through a call to mac_policy_register. The mac_policy_conf structure is of paramount importance and can be found in /security/mac_policy.h.

mac_policy_conf structure
Of interest to us is the mpc_ops field which is a pointer to mac_policy_ops struct. This structure specifies the operation the KEXT is interested in and the name of the callback function to invoke when the operation is triggered. Now there are a ton of callouts available and the KEXT is usually only interested in a subset of those so it bzero()'s the structure and then sets the ones it is interested in.

mac_policy_ops structure snippet
Recall that the AppleMobileFileIntegrity.kext KEXT is a mach-O binary. So using jtool you can list the segments and the mac_policy_ops structure can be found in the __DATA.__const section.

Listing SEGMENTS on iOS 9.3.3
We then dump the __DATA.__const section (jtool -d __DATA.__const com.apple.driver.AppleMobileFileIntegrity.kext9.3.3)

Dumping __DATA.__const on iOS 9.3.3
And from that, offset five(5) i.e. 0xffffff801c740be8 points to the mac_policy_ops struct. Recall this was the fifth field in the mac_policy_conf structure. Navigating to that address we get an array of function pointers. And correlating this back to the mac_policy_ops structure looks like this:
mac_policy_ops function pointers in iOS 9.3.3
The seventh(7) pointer 0xffffff801c71608c points to mac_policy_ops.mpo_cred_check_label_update_execve, the twelfth(12) pointer 0xffffff801c716094 to mac_policy_ops.mpo_cred_label_associate and so on and so forth. The zeros you see goes back what we said earlier about the KEXT bzero'ing the structure and only setting the ones it is interested in.

As a casualty of the war against jailbreakers this doesn't work on iOS 10.x. Instead the initialization is done in code. So alas, the above steps will not work and a different approach is needed. Extracting the AppleMobileFileIntegrity.kext from an iOS 10 3.x kernel, opening it up in IDA and searching for references to mac_policy_register leads to:

AMFI KEXT iOS 10.x
Immediately you see references to "mac_policy_register failed" and if we work backwards, we get there by this branch instruction "BL sub_FFFFFFF0064514D8" at "0xFFFFFFF00644FA4C". It's therefore safe to assume that sub_FFFFFFF0064514D8 is indeed mac_policy_register(). Looking up this function reveals that it takes three arguments - pointer to a mac_policy_conf struct, out pointer to MACF policy handle and the second argument passed to the KEXT's entry point - as shown:

mac_policy_register
A call to ARMs
On ARM architectures, function arguments and return values are passed/stored in registers X0-X7. So in this case register X0 should hold the mac_policy_conf struct pointer. Let's remind ourselves of what this struct looks like:

mac_policy_struct again
ARMed (pun intended) with this knowledge, the previous disassembly should start making sense and can be annotated as follows:

 # mpc - mac_policy_conf  
 ----------------------------------------------------------------------------------------------------  
 # mpc->mpc_name="AMFI"
 __text:FFFFFFF00644F9EC STR       X9, [X8,#(qword_FFFFFFF006E68650 - 0xFFFFFFF006E68530)]  
 __text:FFFFFFF00644F9F0 ADRP      X9, #aAmfi_0@PAGE ; "AMFI"  
 __text:FFFFFFF00644F9F4 ADD       X9, X9, #aAmfi_0@PAGEOFF ; "AMFI"  
 __text:FFFFFFF00644F9F8 FMOV      D0, X9  
 # mpc=mpc_fullname="Apple Mobile File Integrity"  
 __text:FFFFFFF00644F9FC ADRP      X9, #aAppleMobileFil@PAGE ; "Apple Mobile File Integrity"  
 __text:FFFFFFF00644FA00 ADD       X9, X9, #aAppleMobileFil@PAGEOFF ; "Apple Mobile File Integrity"  
 __text:FFFFFFF00644FA04 INS       V0.D[1], X9  
 # Set mpc->name and mpc->fullname  
 __text:FFFFFFF00644FA08 ADRP      X0, #xmmword_FFFFFFF006E68FA8@PAGE  
 __text:FFFFFFF00644FA0C ADD       X0, X0, #xmmword_FFFFFFF006E68FA8@PAGEOFF  
 __text:FFFFFFF00644FA10 STR       Q0, [X0]  
 # Set mpc->mpc_labelname=""  
 __text:FFFFFFF00644FA14 ADRP      X9, #off_FFFFFFF006E695D8@PAGE  
 __text:FFFFFFF00644FA18 ADD       X9, X9, #off_FFFFFFF006E695D8@PAGEOFF  
 __text:FFFFFFF00644FA1C STR       X9, [X0,#(qword_FFFFFFF006E68FB8 - 0xFFFFFFF006E68FA8)]  
 # Set mpc->mpc_labelname_count=1  
 __text:FFFFFFF00644FA20 MOV       W9, #1  
 __text:FFFFFFF00644FA24 STR       W9, [X0,#(dword_FFFFFFF006E68FC0 - 0xFFFFFFF006E68FA8)]  
 # X8 has mac_policy_ops  
 __text:FFFFFFF00644FA28 STR       X8, [X0,#(qword_FFFFFFF006E68FC8 - 0xFFFFFFF006E68FA8)]  
 # Set mpc->mpc_loadtime_flags=0 and other fields  
 __text:FFFFFFF00644FA2C STR       WZR, [X0,#(dword_FFFFFFF006E68FD0 - 0xFFFFFFF006E68FA8)]  
 __text:FFFFFFF00644FA30 ADRP      X8, #dword_FFFFFFF00760B2E0@PAGE  
 __text:FFFFFFF00644FA34 ADD       X8, X8, #dword_FFFFFFF00760B2E0@PAGEOFF  
 __text:FFFFFFF00644FA38 STR       X8, [X0,#(qword_FFFFFFF006E68FD8 - 0xFFFFFFF006E68FA8)]  
 __text:FFFFFFF00644FA3C STR       WZR, [X0,#(dword_FFFFFFF006E68FE0 - 0xFFFFFFF006E68FA8)]  
 # call mac_policy_register - sub_FFFFFFF0064514D8  
 __text:FFFFFFF00644FA40 ADRP      X1, #unk_FFFFFFF006E6852C@PAGE  
 __text:FFFFFFF00644FA44 ADD       X1, X1, #unk_FFFFFFF006E6852C@PAGEOFF  
 __text:FFFFFFF00644FA48 MOV       X2, #0  
 __text:FFFFFFF00644FA4C BL       sub_FFFFFFF0064514D8  
 __text:FFFFFFF00644FA50 CBZ       W0, loc_FFFFFFF00644FA78  

And so in a nutshell this is how mpc_ops is populated. Where the struct is now stored in register X8. Let's look at an example of an assignment.

mpo_proc_check_get_task
The hook kicks in during the acquisition of task port rights. And you should keep in mind that if you obtain a task's port you own the task. So the hook is an important one. Opening the KEXT in IDA and searching for get-task-allow reveals the following:

get-task-allow search results
And after navigating through the results we eventually end up at the highlighted sub_FFFFFFF006450D50 function. The hook first checks if the target task has the get-task-allow entitlement at 0xFFFFFFF006450D7C:

get-task-allow check
If that fails, the hook then checks for the task_for_pid_allow entitlement at 0xFFFFFFF006450D9C. This is the "god-mode" entitlement as it allows the entitled task to access any other task via the task_for_pid Mach trap. Side-note: If you have ever performed debugging of iOS apps using debugserver then you would be familiar with this entitlement as you first have to sign the binary and grant the binary the entitlement.

 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">  
 <plist version="1.0">  
 <dict>  
      <key>com.apple.springboard.debugapplications</key>  
      <true/>  
      <key>get-task-allow</key>  
      <true/>  
      <key>task_for_pid-allow</key>  
      <true/>  
      <key>run-unsigned-code</key>  
      <true/>  
 </dict>  
 </plist>  

Likewise if you are doing any work on the kernel(pid 0) - like dumping a running kernel - you would call
 ret = task_for_pid(mach_task_self(), 0, &kernel_task);  // from ios-kern-utils kdump libkern.c
assuming of course that the jailbreak supports it. Going back to the hook and picking up where we left off if the calling task doesn't have the task_for_pid_allow entitlement then we see a call to sub_FFFFFFF006451014 which does a final check for unrestricted debugging:
final check for unrestricted debugging
Ok so now that we understand hook, how does it get assigned? Well if we search for cross references in IDA we land in a section just above the snippet we annotated earlier on:

mpo_proc_check_get_task hook initialization
And if we press k key in IDA at offset 0xFFFFFFF00644F9C4 we see *(X8 + arg_4F0) = X9 where #arg_4F0 as defined by IDA is hex 0x500 which translates to offset 160 in the mac_policy_ops struct which is mpo_proc_check_get_task.

 __text:FFFFFFF00644F9B8 0A 09 04 F9 STR       X10, [X8,#(qword_FFFFFFF006E68D40 - 0xFFFFFFF006E68530)]  
 __text:FFFFFFF00644F9BC 09 00 00 B0 ADRP      X9, #sub_FFFFFFF006450D50@PAGE  
 __text:FFFFFFF00644F9C0 29 41 35 91 ADD       X9, X9, #sub_FFFFFFF006450D50@PAGEOFF  
 __text:FFFFFFF00644F9C4 09 81 02 F9 STR       X9, [X8,#arg_4F0] 

Recall that register X8 holds the mac_policy_ops struct. And looking at the disassembly you see several other similar assignments.

Wrap Up
We have come to the end of part 1 in the series. To recap:
  • We introduced MACF
  • Learned a little about KEXTs and how they relate to MACF
  • Got introduced to Policies in the context of MACF
  • Learned to identify policies on both macOS and iOS
  • Saw that AppleMobileFileIntegrity.KEXT is a policy module
  • Delved a little into policy registration to extract and identify callback functions
In the next post we will look at little more into AppleMobileFileIntegrity.KEXT and how it communicates with it's user-mode partner in crime amfid. I hope you found this helpful. Also if you find errors please point them out in the comments. Like I said I am learning as I go along.

Happy hacking errrrm......reversing.....mi amigos !!