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.