Merge pull request #604 from luken11/signing-fast-path-v2

Add support for multi-exploit Info.plist fast path
This commit is contained in:
Lars Fröder 2023-11-29 16:02:42 +01:00 committed by GitHub
commit 9daa349a68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 27 deletions

View File

@ -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;

View File

@ -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);
extern NSDictionary* dumpEntitlementsFromBinaryData(NSData* binaryData);
extern EXPLOIT_TYPE getDeclaredExploitTypeFromInfoDictionary(NSDictionary *infoDict);
extern bool isPlatformVulnerableToExploitType(EXPLOIT_TYPE exploitType);

View File

@ -5,6 +5,8 @@
#import <sys/sysctl.h>
#import <mach-o/dyld.h>
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;
}
}
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;
}