Yuebin Sun(@yuebinsun2020) of Tencent Security Xuanwu Lab
Summary
COVID-19 outbreak keep me from going out,I have been researching macOS’s Security framework in the past two weeks of homeworking.
In this blog, I will try to analyze Security framework, especially Keychain, and previous vulnerabilities of the Secuirty framework。
Security Framework
Security framework is responsible for providing authentication and authorization, secure data storage and transportation, code signing, encryption/decryption services. Apps can use this services by using API of Security framework directly without knowing or caring about its implementation details.
But what are the components which composes the Security framework, and how the components collaborate with each other?
Unfortunately the official document site did not updating the architecture diagram of this framework from macOS 10.7, but I find a outdated diagram in the book 《Mac OS X Internals》, It can still be used as a reference, Not too much has changed from that time.
Keychain
Keychain play a significant role in the Security framework, passwords of WiFi and passwords of Safari autofilling are all saved and managed by Keychain.
Keychain was first introduced in Mac OS X 8.6, and was used for storing login credentials of the mail servers for PowerTalk mail system. Today, it improve a lot, many new data types are supported including many kinds of passwords, encryption keys, certificates and private notes. PowerTalk is no longer exists, but Keychain’s clients is replaced by many builtin Apps and thirdparty Apps.
Keychains in iOS and macOS are slightly different.
In iOS, there is only a single Keychain, it can be accessed when the device is unlocked, otherwise it will be locked too.
In macOS, users are allowed to create any number of Keychains for private use, Security framework provide SecKeychain{Create, Delete, Open, …} APIs for macOS users, with this API users can create, delete, open Keychain.
By default, two Keychains are already exist in macOS:
- ~/Library/Keychains/login.keychain-db
- /Library/Keychains/System.keychain
The login Keychain will be unlocked when user login macOS. System keychain, in contrast, is locked and encrypted, its decryption key is stored in /var/db/SystemKey, only root process can access it.
Apple offers Keychain Access.app to common users to view/search/create Keychains and Keychain Items, obtaining sensitive data, such as passwords, will trigger a authentication dialog.
How Keychain stores a new password
Example code of Apple Documentation as below shows how to store a new website password to Keychain.
1 | static let server = "www.example.com" |
The most important is SecItemAdd API, we will analyze this API step by step to see how it implements.
In the abstract, data stored in query param will deliver to Keychain Service over SecItemAdd API, service will package the data as an Keychain Item, password in query will be encrypted, and the Keychain Item will continue to be written to Keychain database at disk.
From a components perspective, SecItemAdd API is implemented by Security shared library(/System/Library/Frameworks/Security.framework/Versions/A/Security), Security shared library will be loaded into the current App process. After SecItemAdd of Security shared library are called, the query data will be forwarded to XPC Service(com.apple.securityd.xpc) over SECURITYD_XPC macro, this XPC service are hosted by secd process(/usr/libexec/secd), secd run as current user.
When data is send to secd process, according to operation, securityd_xpc_dictionary_handler(simplified for better reading) dispatch message to different internal functions. In our case, query param is passed on to _SecItemAdd directly, and there is also another important param, client of SecurityClient struct, SecurityClient is responsible for identifying client process, subsequently ACL checking is based on this struct. In addition, accessGroups field of SecurityClient is used for implementing the Shared Web Credentials(share credentials between iOS apps and their website counterparts). Apps and Web use Associated Domains Entitlement to create association, more details can read Supporting Associated Domains in Your App
1 | static void securityd_xpc_dictionary_handler(const xpc_connection_t connection, xpc_object_t event) { |
In _SecItemAdd, query data will be translated to Sqlite3 sql statement, and in the end data will be inserted into sqlite3 database. What needs to be pointed out is that password will be encryptd before insert into database, and other non-secret fields will keep plain, this plain fields provide Keychain item searching support.
So far, inserting new website password to Keychain based on SecItemAdd API finished. The newly inserted Keychain item is save to Login Keychain, Login Keychain will be locked when user logout or machine poweroff.
1 | static CFStringRef SecDbItemCopyInsertSQL(SecDbItemRef item, bool(^use_attr)(const SecDbAttr *attr)) { |
SecurityServer 与 SecurityAgent
Login Keychain is unlocked when user unlock the device, so wo do not see Keychain decryption or unlocking in the previous inserting keychain item.
However System Keychain or private Keychain(user created) will need Keychain encryption/decryption, locking/unlocing, Security Server is responsible for that.
Security Server(/usr/sbin/securityd) is a daemon service process which run as root, as the architecture diagram shows, Security Server offsers CSP/DL plugin for CDSA, namely data encryption and storage.
Security Server host service based on ucsp MIG interface, clients can access the internal Server object through mig interface. Fortunately any process can use this ucsp MIG interface.
By reading the source code, I see many features provided by Security Server:
- Managing Security Server’s clients(Session, Connection)
- Authentication and authrization
- Managing Keychain databases
- Code signature generating/verifying
- Data encryption and decryption(ucsp_server_encrypt, ucsp_server_decrypt)
- Key, key pair generating(ucsp_server_generateKey, ucsp_server_generateKeyPair, ucsp_server_wrapKey, ucsp_server_unwrapKey)
- Code Signing Hosting(disappeared in 10.15, not yet in-depth analysis)
It is obvious that Security Server(securityd) has many highly privileged operations, and it manages a lot of sensitive data, run as root, so if we can find a vulnerability in this service process, it will have a big impact.
KeySteal is such a vulnerability occurred in securityd, when successfully exploited, any process can access passwords hold by Keychain.
But how can we interact with Security Server(securityd) based on MIG interface?
The ucsp MIG definition file exists in the source code of Security framework(OSX/libsecurityd/mig/ucsp.defs). Unfortunately only a few documentations about MIG can be found, and none of them can be found about interacting with Security Server certainly. In the end, I extract the code snippet which meet our needs from Linus Henze’s KeySteal Exploit, based on the snippet, I create a simple ucsp client accessing Security Server’s ucsp_server_setup interface.
1 |
|
SecurityAgent
As mentioned above, Security Server is also responsible for managing authentication and authroization.
When client request Security Server to launch authentication/authroization verifying, if Security Server need to interact with the user (enter password) to verify identity, Security Server daemon will talk to Security Agent through XPC communication. The Security Agent, run as current user, pop up the interactive dialog to user, and then passwords typed by user will send back to Security Server, Security Server processing the actual verification. Throughout all the verification process, client DO NOT touch any sensitive information(such as passwords), all it can get is the verification result, true or false, yes or not. This mechanism can avoid the leakage of sensitive information and at the same time it will be transparent to the client when system add new authentication extension.
Vulnerabilities History
After we have learned some of the necessary Security framework architecture content above, let’s move on to the Security framework vulnerabilities which occured in macOS 10.14.*, and try to understand how the vulnerability works and what components the vulnerability is in.
What needs to be declared in advance is that Apple do not provide any details about the patched vulnerabilities except short title. The following are my own analysis based on the source code diff, so it also means that the results may not be entirely correct. If you find any mistakes or omissions, please don’t hesitate to point them out.
CVE-2019-8604 (fixed in 10.14.5)
By comparing the two versions of the source code, vulerability patch of CVE-2019-8604 can be found.
This vulnerablity exists in Security Server Daemon(securityd), when Security Server deal with Keychain database name, ucsp_server_setDbName of mig interface can set any length of dbname to Security Server object, ucsp_server_getDbName of interface will copy the given dbname to name parameter, the name parameter is designed to length PATH_MAX. So, if we pass a overlong dbname, ucsp_server_getDbName will trigger OOB write when memcpy.
1 | --- a/Security-58286.251.4/securityd/src/transition.cpp |
It is quite natural that the patch add a checking to length of dbname, to avode overlong dbname triggering oob memory copying. One more point to explain, both std::string and strlen can and only can be truncated by “\0”, so the way of setDbName and getDbName treating dbname is consistent.
CVE-2019-8520 (fixed in 10.14.4)
By comparing the two versions of the source code, vulerability patch of CVE-2019-8520 can be found.
This vulnerablity exists also in Security Server Daemon(securityd), when Security Server deal with authroization or authentication, if it need to interact with the user (enter password) to verify identity, it will ask for Security Agent’s help, Security Agent finally pop up a diaglog to user.
The communication between Security Server and Security Agent is based on XPC, when receiving response from Security Agent, in xpcArrayToAuthItemSet, the data(AUTH_XPC_ITEM_VALUE) and the length(AUTH_XPC_ITEM_SENSITIVE_VALUE_LENGTH) are passed by two separate fields. If providing a small data and a big length, sensitiveLength bytes of data will be copied from data to dataCopy, if sensitiveLength exceeds the actual size of data. OOB read will occurs.
1 | --- a/Security-58286.240.4/securityd/src/agentquery.cpp |
The patch add a checking, to ensure that sensitiveLength is not greater than actual size of data.
CVE-2019-8526 (fixed in 10.14.4)
By comparing the two versions of the source code, vulerability patch of CVE-2019-8526 can be found.
1 |
|
This is the KeySteal vulnerability that I have read paper before, vulnerablity exists in Security Server Daemon(securityd), Security Server provides a feature named Hosting Guest Code. Two problems can be seen from the vulnerability patch:
The first one exists in implementing Hosting Guest Code. When Security Server creeate SecCode object, it use SecCodeCreateWithPID API, this API identifies the client process by pid, so as the comment code in the patch says, there is a problem with PID Reuse. The way fix this problem is replacing SecCodeCreateWithPID with SecCodeCreateWithAuditToken, SecCodeCreateWithAuditToken API identifies the client process by audit token. Reasons and attack methods for pid reuse has already been fully explained in Samuel Groß’s 《Don’t Trust the PID!》
The second is the reference counting problem of mach port. CodeSigningHost::reset() calls destory() to forcibly release mach port. But the destroyed mach port may still be referenced by some data structures, and the mach port in the user-mode process itself is a Mach Port Name, it’s just a number. Since it’s number, there’s a possibility of reuse, so, UAF can be implemented if we can reoccupy it before the next use. It’s easy to fix the problem, replace destory() with a reference-counted version of deallocate().
CVE-2018-4400 (fixed in 10.14.1)
The vulnerability is described in Apple bulletin that denies service when processing S/MIME messages. Through code comparison, I got a suspected patch, and I am not sure.
1 | --- a/Security-58286.200.222/OSX/libsecurity_smime/lib/smimeutil.c |
I am not very familiar with certificate management and related data structures for the time being, so I will not analyze further.
The above are some of the identified vulnerabilities and patches that I have found so far. Because Apple open source code lags behind version updates, this vulnerabilities exists in 10.14.*.
Conclusion
This blog is what I researched and did during this time about Security Framework. The source code of Security Framework is huge and I only focused on Keychain and Security Server components that have been found to have more vulnerabilities in history。Other components like Auth will be analyzed when I have time, and, of course, I will continue to share with community through this site.
If you find something wrong above, or you are also interested in macOS, welcome to ping me @yuebinsun2020.
References
[1] Documentation of Security Framework
https://developer.apple.com/documentation/security?language=objc
[2] Apple Open Source Code
[3] KeySteal Vulnerability
https://www.pinauten.de/resources/KeySteal_OBTS_2019.pdf
[4] Keychain Wikipedia