We discovered a vulnerability in suhelperd, a helper daemon process for Software Update in macOS. A class inside suhelperd, SUHelper, provides an essential system service through the inter-process communication (IPC) mechanism. The process runs as root and is signed with special entitlements, such as com.apple.rootless.install, which grants the process permission to bypass System Integrity Protection (SIP) restrictions. This combination of functionalities presents an attractive opportunity for malicious actors to exploit the vulnerability.
Designated as CVE-2022-22639, the vulnerability could allow root privilege escalation if successfully exploited. After discovering the flaw, we reported it to Apple, hence the release of a patch through the macOS Monterey 12.3 security update
This report dives into the daemon process, enumerates all the services it provides, and discusses the vulnerabilities found therein.
The IPC service
The core logic of the daemon process is to register an IPC service by API bootstrap_check_in, named as com.apple.suhelperd.
The client process can find the service with names through API bootstrap_look_up, and then request the service routines through the IPC mechanism. (The IPC mechanism is discussed at length in chapter 11 of the book “MacOS and iOS Internals, Volume I: User Mode.”)
The IPC server provides 45 service routines, some of which are shown in the following figure. I renamed all the routines using the format IPC_NUMBER_XXX, according to their functions and the corresponding rights, for easy reference.
The IPC client is already implemented in the private SoftwareUpdate.framework. There are 45 exported functions with a one-to-one correspondence to their respective service routines.
Instead of reinventing the wheel, one can reuse the code from the framework. Fortunately, there is an Objective-C class named SUHelperProxy, which encapsulates all the IPC client interfaces that one can directly use.
The following is an example of a service routine handling flow.
Client authorization
It should be noted that not all 45 services are available to unprivileged clients, and that the server has a rights authorization mechanism to verify if a service request is from a legitimate client.
First, the client needs to generate an authorization object by API AuthorizationCreate, and then make it as an external form (32 bytes of data) to transfer the authorization object to the server for verification.
Second, when the server receives the authorization object, it determines whether specific rights can be granted to the client. At this stage, the server checks the client’s authorization object and uid.
Third, when the client requests a special service routine, the server checks whether the specific rights were previously granted to the client, otherwise it denies the request.
Old vulnerabilities
As mentioned earlier, not all the service routines are allowed because of the requisite client authorization. However, there were some essential routines that were left unprotected because the server did not validate the rights at the third step.
Here are two old vulnerabilities, for example, which were discovered by researchers at Xuanwu Lab. CVE-2021-30913 could allow malicious actors to edit NVRAM variables.
The vulnerability exists in the caller function of the function “-[SUHelper setNVRAMWithKey:value:]”. Its patch adds the validation code at line 9.
It validates the client rights with value 2, so I renamed the caller function as IPC_2_setNVRAMWithKey_value to mark the needed rights.
Next is CVE-2021-30912, a vulnerability that could grant malicious actors access to a user’s Keychain items.
The vulnerability exists in the caller function of the function “-[SUHelper lookupURLCredentialInSystemKeychainForHost:port:]”.
Its patch adds the validation code at line 10.
New finding: CVE-2022-22639
After reviewing the 45 service routines, I filtered out those with validation codes and found a few that had names starting with “IPC_0_”. A close inspection of these routines revealed that the function “-[SUHelper prepareInstallAssistantWithPath:(NSString *) path]” was exploitable. The caller function IPC_0_prepareInstallAssistantWithPath did not validate the client’s rights and called the real routine directly.
The implementation of the function is as follows, with the third parameter (NSString *) path that is passed from the client.
A look at the internal function reveals that it loads a bundle at line 70.
I debugged and found the bundle path as ${Assistant.app}/Contents/Frameworks/OSInstallerSetup.framework. An important finding is that the ${Assistant.app} is actually the third parameter (NSString *) path, which can be completely controlled by the client.
In a normal scenario, the ${Assistant.app} should be the real path to “Install macOS XXX.app”. It is extracted from InstallAssistant.pkg, which is downloaded from the Apple server. However, I discovered that a user could fake the path and contents of the ${Assistant.app} by exploiting this vulnerability.
It seems that I found a primitive to load any dylib into the target process to get the root privilege and the special entitlements. However, I failed to load a self-signed dylib directly because I found that hardened runtime is enabled by default for system processes when SIP is on, even though it is not signed with runtime flags. But I could load arbitrary Apple-signed dylib into it even if it was an old, vulnerable dylib.
Perhaps there are other methods to exploit the issue. Here, I let it load the original OSInstallerSetup.framework. Once the OSInstallerSetup.framework is loaded, it calls the function “-[OSISClient _startServer]”. At line 103, it launches another IPC service, com.apple.install.osinstallersetupd, by API SMJobSubmit. From line 48, it can be seen that if the current process is running as root, the newly submitted job runs at system domain with root privileges too.
Now, the current process is suhelperd, running as root, and the job executable path is toolPath, which is inside the bundle ${Assistant.app}/Contents/Frameworks/OSInstallerSetup.framework/Resources/osinstallersetupd. A malicious actor could put the payload in toolPath directly to attain root privilege escalation.
The full proof of concept can be found here and a video demonstration can be viewed here.
Patch
As mentioned earlier, Apple has addressed the CVE-2022-22639 issue through the macOS Monterey 12.3 security update. This patch now adds the validation code at line 9.
Security recommendations
End-users can mitigate the risks by regularly updating systems and applications with the latest patches to ensure that security flaws cannot be exploited for malicious activities.
Learn about Trend Micro™ Maximum Security for Mac so you can enjoy your digital life safely. It blocks viruses, spyware, ransomware, and other malicious software for your peace of mind.