iOS14.8: Patch CVE-2021-1740 again silently
As well known, iOS14.8 patched two 0 days in the wild, one of which is the pegasus 0-click vulnerability. You can get the root cause and more interesting findings by reading my analysis from here.
By the way, it also patched CVE-2021-1740
again silently, no CVE assigned (Updated: iOS15 released on Sep, 20th and it seems that CVE-2021-30855
matches this new patch).
When I study the DefCon topic Caught you - reveal and exploit IPC logic bugs inside Apple , I found the patch of CVE-2021-1740
is still vulnerable.
The DefCon slides has already given the details of the root causes, and also shared 2 exploitations.
The first exploitation (Arbitrary File Read) :
The second exploitation (Arbitrary File Write) :
The old patch (Still vulnerable) :
- Yes, it can stop the second exploitation. We have no time window to replace the target temporary file with a symbolic link, because we can’t know the temporary file name before the API
clonefileat
. - Unfortunately, it cannot stop the first exploitation. If the
plist_file
is replaced with a symbolic link before the APIclonefileat
, then the temporary file with a random name could still be cloned. Therefore, the arbitrary file read issue still exists because the temporary file is accessible by common users.
Proof Of Concept
It is a TOCTOU
issue, so I made a quick POC through my debugger :
-
Attach my debugger to the
cfprefsd
process, set a breakpoint before the API callclonefileat
in the function-[CFPDSource cloneAndOpenPropertyListWithoutDrainingPendingChangesOrValidatingPlist]
-
Create a normal plist file (whose size larger than 0x100000 = 1M) at
/tmp
directory. -
Fire a XPC request to the
cfprefsd
, for reading the preference value in the plist file. -
Then it hit my breakpoint, it is time to replace the plist file with a symbolic link now (point to a privacy file).
-
Step over the API
clonefileat
, we can see the privacy file was cloned to a file with a random name as expected. The temporary file is readable by common users even it is a random name ! -
Before the
unlink
of the temporary file, we still have a time window to backup it. -
How can we know the random file name ? There could be many methods to get it. The simplest way could be enumerating the files inside the directory :
chdir("/tmp/test"); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ DIR *dp; struct dirent *ep; int cnt=0; while (1) { dp = opendir ("./"); while ((ep = readdir (dp))) { if (strlen(ep->d_name) == 21) { //strlen(abc.plist.cfp.XXXXXXX)==21 copyfile(ep->d_name, "secret_backup", 0, COPYFILE_ALL); printf("Got it:%s.\n", ep->d_name); goto END; } } closedir (dp); cnt++; } END: printf("done with %d loops.\n", cnt); });
New fix in iOS14.8
We can see it uses API fclonefileat
now, and the src_fd
is returned by API openat
, with flag=0x100
(O_NOFOLLOW) to stop following the symlinks attack.