From e267749ada308b6dec51e078d18fca0d55df17a4 Mon Sep 17 00:00:00 2001 From: Luke Noble Date: Tue, 28 Nov 2023 21:44:12 +0000 Subject: [PATCH] Add support for multi-exploit Info.plist fast path Deprecates TSBundlePreSigned in favour of TSPreAppliedExploitType --- RootHelper/main.m | 52 +++++++++++++------------ Shared/TSUtil.h | 21 +++++++++- Shared/TSUtil.m | 97 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 143 insertions(+), 27 deletions(-) diff --git a/RootHelper/main.m b/RootHelper/main.m index 367d213..74dd0ab 100644 --- a/RootHelper/main.m +++ b/RootHelper/main.m @@ -531,36 +531,38 @@ int signApp(NSString* appPath) if(!mainExecutablePath) return 176; if(![[NSFileManager defaultManager] fileExistsAtPath:mainExecutablePath]) return 174; - - NSObject *tsBundleIsPreSigned = appInfoDict[@"TSBundlePreSigned"]; - if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]]) - { - // if TSBundlePreSigned = YES, this bundle has been externally signed so we can skip over signing it now - NSNumber *tsBundleIsPreSignedNum = (NSNumber *)tsBundleIsPreSigned; - if([tsBundleIsPreSignedNum boolValue] == YES) - { - NSLog(@"[signApp] taking fast path for app which declares it has already been signed (%@)", mainExecutablePath); - return 0; - } - } - // XXX: There used to be a check here whether the main binary was already signed with bypass - // In that case it would skip signing aswell, no clue if that's still needed - // With the new bypass adhoc signing should fail and reapplying the bypass should produce an identical binary - /*SecStaticCodeRef codeRef = getStaticCodeRef(mainExecutablePath); - if(codeRef != NULL) + // Check if the bundle has had a supported exploit pre-applied + EXPLOIT_TYPE declaredPreAppliedExploitType = getDeclaredExploitTypeFromInfoDictionary(appInfoDict); + if(isPlatformVulnerableToExploitType(declaredPreAppliedExploitType)) { - if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) - { - NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", mainExecutablePath); - CFRelease(codeRef); - return 0; - } + NSLog(@"[signApp] taking fast path for app which declares use of a supported pre-applied exploit (%@)", mainExecutablePath); + return 0; } else { - NSLog(@"[signApp] failed to get static code, can't derive entitlements from %@, continuing anways...", mainExecutablePath); - }*/ + NSLog(@"[signApp] app (%@) declares use of a pre-applied exploit that is not supported on this device. Proceeding to re-sign...", mainExecutablePath); + } + + // If the app doesn't declare a pre-applied exploit, and the host supports fake custom root certs, + // we can also skip doing any work here when that app is signed with fake roots + // If not, with the new bypass, a previously modified binary should failed to be adhoc signed, and + // reapplying the bypass should produce an identical binary + if(isPlatformVulnerableToExploitType(EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1)) + { + SecStaticCodeRef codeRef = getStaticCodeRef(mainExecutablePath); + if(codeRef != NULL) + { + if(codeCertChainContainsFakeAppStoreExtensions(codeRef)) + { + NSLog(@"[signApp] taking fast path for app signed using a custom root certificate (%@)", mainExecutablePath); + CFRelease(codeRef); + return 0; + } + + CFRelease(codeRef); + } + } NSURL* fileURL; NSDirectoryEnumerator *enumerator; diff --git a/Shared/TSUtil.h b/Shared/TSUtil.h index 2b44578..d3c1195 100644 --- a/Shared/TSUtil.h +++ b/Shared/TSUtil.h @@ -36,6 +36,22 @@ typedef enum PERSISTENCE_HELPER_TYPE_ALL = PERSISTENCE_HELPER_TYPE_USER | PERSISTENCE_HELPER_TYPE_SYSTEM } PERSISTENCE_HELPER_TYPE; +// EXPLOIT_TYPE is defined as a bitmask as some devices are vulnerable to multiple exploits +// +// An app that has had one of these exploits applied ahead of time can declare which exploit +// was used via the TSPreAppliedExploitType Info.plist key. The corresponding value should be +// (number of bits to left-shift + 1). +typedef enum +{ + // CVE-2022-26766 + // TSPreAppliedExploitType = 1 + EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1 = 1 << 0, + + // CVE-2023-41991 + // TSPreAppliedExploitType = 2 + EXPLOIT_TYPE_CMS_SIGNERINFO_V1 = 1 << 1 +} EXPLOIT_TYPE; + extern LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes); typedef struct __SecCode const *SecStaticCodeRef; @@ -60,4 +76,7 @@ extern CFStringRef kSecPolicyLeafMarkerOid; extern SecStaticCodeRef getStaticCodeRef(NSString *binaryPath); extern NSDictionary* dumpEntitlements(SecStaticCodeRef codeRef); extern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString *binaryPath); -extern NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData); \ No newline at end of file +extern NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData); + +extern EXPLOIT_TYPE getDeclaredExploitTypeFromInfoDictionary(NSDictionary *infoDict); +extern bool isPlatformVulnerableToExploitType(EXPLOIT_TYPE exploitType); diff --git a/Shared/TSUtil.m b/Shared/TSUtil.m index 1c439b6..05eb89c 100644 --- a/Shared/TSUtil.m +++ b/Shared/TSUtil.m @@ -5,6 +5,8 @@ #import #import +static EXPLOIT_TYPE gPlatformVulnerabilities; + @interface PSAppDataUsagePolicyCache : NSObject + (instancetype)sharedInstance; - (void)setUsagePoliciesForBundle:(NSString*)bundleId cellular:(BOOL)cellular wifi:(BOOL)wifi; @@ -521,4 +523,97 @@ NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData) [[NSFileManager defaultManager] removeItemAtURL:tmpURL error:nil]; } return entitlements; -} \ No newline at end of file +} + +EXPLOIT_TYPE getDeclaredExploitTypeFromInfoDictionary(NSDictionary *infoDict) +{ + NSObject *tsPreAppliedExploitType = infoDict[@"TSPreAppliedExploitType"]; + if([tsPreAppliedExploitType isKindOfClass:[NSNumber class]]) + { + NSNumber *tsPreAppliedExploitTypeNum = (NSNumber *)tsPreAppliedExploitType; + int exploitTypeInt = [tsPreAppliedExploitTypeNum intValue]; + + if(exploitTypeInt > 0) + { + // Convert versions 1, 2, etc... for use with bitmasking + return (1 << (exploitTypeInt - 1)); + } + else + { + NSLog(@"[getDeclaredExploitTypeFromInfoDictionary] rejecting TSPreAppliedExploitType Info.plist value (%i) which is out of range", exploitTypeInt); + } + } + + // Legacy Info.plist flag - now deprecated, but we treat it as a custom root cert if present + NSObject *tsBundleIsPreSigned = infoDict[@"TSBundlePreSigned"]; + if([tsBundleIsPreSigned isKindOfClass:[NSNumber class]]) + { + NSNumber *tsBundleIsPreSignedNum = (NSNumber *)tsBundleIsPreSigned; + if([tsBundleIsPreSignedNum boolValue] == YES) + { + return EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1; + } + } + + // No declarations + return 0; +} + +void determinePlatformVulnerableExploitTypes(void *context) { + size_t size = 0; + + // Get the current build number + int mib[2] = {CTL_KERN, KERN_OSVERSION}; + + // Get size of buffer + sysctl(mib, 2, NULL, &size, NULL, 0); + + // Get the actual value + char *os_build = malloc(size); + if(!os_build) + { + // malloc failed + perror("malloc buffer for KERN_OSVERSION"); + return; + } + + if (sysctl(mib, 2, os_build, &size, NULL, 0) != 0) + { + // sysctl failed + perror("sysctl KERN_OSVERSION"); + free(os_build); + return; + } + + + if(strncmp(os_build, "19F5070b", 8) <= 0) + { + // iOS 14.0 - 15.5 beta 4 + gPlatformVulnerabilities = (EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1 | EXPLOIT_TYPE_CMS_SIGNERINFO_V1); + } + else if(strncmp(os_build, "19G5027e", 8) >= 0 && strncmp(os_build, "19G5063a", 8) <= 0) + { + // iOS 15.6 beta 1 - 5 + gPlatformVulnerabilities = (EXPLOIT_TYPE_CUSTOM_ROOT_CERTIFICATE_V1 | EXPLOIT_TYPE_CMS_SIGNERINFO_V1); + } + else if(strncmp(os_build, "20G81", 5) <= 0) + { + // iOS 14.0 - 16.6.1 + gPlatformVulnerabilities = EXPLOIT_TYPE_CMS_SIGNERINFO_V1; + } + else if(strncmp(os_build, "21A5248v", 8) >= 0 && strncmp(os_build, "21A331", 6) <= 0) + { + // iOS 17.0 + gPlatformVulnerabilities = EXPLOIT_TYPE_CMS_SIGNERINFO_V1; + } + + free(os_build); +} + +bool isPlatformVulnerableToExploitType(EXPLOIT_TYPE exploitType) { + // Find out what we are vulnerable to + static dispatch_once_t once; + dispatch_once_f(&once, NULL, determinePlatformVulnerableExploitTypes); + + return (exploitType & gPlatformVulnerabilities) != 0; +}