New Xagent Mac Malware Linked with the APT28

APT 1个月前 admin
63 0 0

Background 背景
On Valentine’s Day, BitDefender released a short writeup title, “New Xagent Mac Malware Linked with the APT28”. In the writeup, they discussed a new piece of Mac malware, (XagentOSX/Komplex.B) associated with APT28 (aka the ‘Russians’). They did not provide much technical detail, but did state, “this modular backdoor with advanced cyber-espionage capabilities is most likely planted on the system via the Komplex downloader.”
在情人节那天,BitDefender发布了一篇简短的文章标题,“与APT28链接的新Xagent Mac恶意软件”。在这篇文章中,他们讨论了与 APT28(又名“俄罗斯人”)相关的新 Mac 恶意软件 (XagentOSX/Komplex.B)。他们没有提供太多技术细节,但确实表示,“这个具有高级网络间谍功能的模块化后门很可能是通过 Komplex 下载器植入系统的。

I’d previously analyzed the Komplex downloader (Komplex.A), and discussed it in my recent RSA talk:
我之前分析过 Komplex 下载器 (Komplex.A),并在我最近的 RSA 演讲中讨论过它:

New Xagent Mac Malware Linked with the APT28
It’s a basic ‘first-stage’ implant, that as the BitDefender report notes, may (most often) be used to download more complex ‘second-stage’ implant (i.e XagentOSX/Komplex.B) on targets of interest.
这是一个基本的“第一阶段”植入物,正如BitDefender报告所指出的那样,它可能(最常)用于在感兴趣的目标上下载更复杂的“第二阶段”植入物(即XagentOSX / Komplex.B)。

Regardless, a claim of a new “advanced cyber-espionage” piece of Mac malware? Count me in – sounds exciting!
无论如何,声称一种新的“高级网络间谍”Mac恶意软件?把我算进去 – 听起来很令人兴奋!

Although the BitDefender report didn’t provide any hashes of the malware, digging around, I found the sample I believed matched. With a SHA256 hash of 2a854997a44f4ba7e307d408ea2d9c1d84dde035c5dab830689aa45c5b5746ea, this sample was submitted to VirusTotal on February 8th. At that time, it was only detected by Kaspersky (as the wise @noar pointed out, likely their heuristic signatures flagged it – nice!).

New Xagent Mac Malware Linked with the APT28
As of today, looking the VirusTotal report, we can see it’s now flagged by 25 anti-virus companies.
截至今天,查看 VirusTotal 报告,我们可以看到它现在已被 25 家防病毒公司标记。

Considering it was Valentine’s day, I posted what I believed was the XagentOSX/Komplex.B sample to share the love:
考虑到今天是情人节,我发布了我认为是 XagentOSX/Komplex.B 示例来分享这份爱:

New Xagent Mac Malware Linked with the APT28
Later in the day, PaloAlto Networks Unit42 (perhaps the original group that discovered the malware) posted a more technically comprehensive analysis of the malware titled, “XAgentOSX: Sofacy’s XAgent macOS Tool”. Their research contains a hash (matching the sample I posted) and delves into the malware’s command and control (C2) communications, commands, and infrastructure. It’s a great read.
当天晚些时候,PaloAlto Networks Unit42(也许是发现该恶意软件的原始组织)发布了一份技术上更全面的恶意软件分析,标题为“XAgentOSX:Sofacy的XAgent macOS工具”。他们的研究包含一个哈希值(与我发布的样本相匹配),并深入研究了恶意软件的命令和控制 (C2) 通信、命令和基础设施。这是一本很棒的读物。

However, triaging the binary, I noticed one part of the malware hadn’t been discussed either by BitDefender nor PaloAltoNetwork: code injection. Hooray, something for me to dig into 🙂

Code Injection 代码注入
One of the first things I always do with OS X malware sample is run it thru classdump. This utility, as its name implies, will extract (i.e. dump) all class information from an Objective-C binary. Due to the way the Objective-C runtime works, such binaries must retain both class and method names. This information can be highly informative and often makes reversing such binaries fairly simple:
我总是对 OS X 恶意软件示例做的第一件事就是通过类转储运行它。顾名思义,这个实用程序将从 Objective-C 二进制文件中提取(即转储)所有类信息。由于 Objective-C 运行时的工作方式,此类二进制文件必须同时保留类名和方法名。这些信息可能提供大量信息,并且通常使反转此类二进制文件变得相当简单:

New Xagent Mac Malware Linked with the APT28
The classdump output reveals an interesting class named ‘InjectApp’:
classdump 输出揭示了一个名为“InjectApp”的有趣类:


@interface InjectApp : NSObject
@interface InjectApp:NSObject



– (void)injectRunningApp;
– (无效)injectRunningApp;

– (void)sendEventToPid:(id)arg1;
– (无效)sendEventToPid:(id)arg1;

– (BOOL)isInjectable:(id)arg1;
– (BOOL)是可注射的:(id)arg1;

– (id)init; – (id)初始化;

Now, code injection is an excellent feature to add to one’s malware creation (think stealth, security tool bypasses, etc etc). However, as an ‘advanced feature’, it’s very rare in (known) Mac malware. As such, it was something I wanted to take a closer look at. As there are a variety of ways to perform code injection on OS X/macOS, my main goal was to see how XAgentOSX/Komplex.B implemented it.
现在,代码注入是一个很好的功能,可以添加到一个人的恶意软件创建中(想想隐身、安全工具绕过等)。但是,作为一项“高级功能”,它在(已知的)Mac 恶意软件中非常罕见。因此,这是我想仔细研究的东西。由于在OS X / macOS上执行代码注入的方法有很多种,我的主要目标是了解XAgentOSX / Komplex.B如何实现它。

Reversing the malware, we can see an instance of the InjectApp class is created and utilzied from the -[BootXLoader injectApplication] method:
反转恶意软件,我们可以看到 InjectApp 类的实例是从 -[BootXLoader injectApplication] 方法创建和使用的:

void -[BootXLoader injectApplication](void * self, void * _cmd)
void -[BootXLoader injectApplication](void * self, void * _cmd)

  rbx = [[InjectApp alloc] init];
rbx = [[InjectApp alloc] 初始化];

  [rbx injectRunningApp];
  rdi = rbx;
  [rdi release]; [RDI 发布];
  return; 返回;

Unfortunately, it appears that no other code invokes the -[BootXLoader injectApplication] method:
不幸的是,似乎没有其他代码调用 -[BootXLoader injectApplication] 方法:

New Xagent Mac Malware Linked with the APT28
In other words, it appears that this injection code, though present, is not utilized by the malware. Still, I wanted to dig into its internals.

When analyzing a piece of malware, I find that static and dynamic analysis, performed in parallel, are the best way to fully understand a piece of malware. Unfortunately since the malware doesn’t actually invoke the injection code (i.e. nobody invokes -[BootXLoader injectApplication] method), we can’t just set a debugger breakpoint and wait until it’s hit. No worries! Using the debugger we can coerce the malware to call it 🙂 Specifically we can simply change the instruction pointer, RIP, to point to the start of the -[BootXLoader injectApplication] method.
在分析恶意软件时,我发现并行执行的静态和动态分析是完全了解恶意软件的最佳方式。不幸的是,由于恶意软件实际上并没有调用注入代码(即没有人调用 -[BootXLoader injectApplication] 方法),我们不能只设置一个调试器断点并等待它被命中。不用担心!使用调试器,我们可以强制恶意软件将其称为:)具体来说,我们可以简单地将指令指针 RIP 更改为指向 -[BootXLoader injectApplication] 方法的开头。

The -[BootXLoader injectApplication] method’s address is at 0000000100014D8F in the binary:
-[BootXLoader injectApplication] 方法的地址位于二进制文件中的 0000000100014D8F:

New Xagent Mac Malware Linked with the APT28
We can confirm, that when loaded in memory (i.e. in a debugger) that method is still present at that address (it hasn’t been ASLR’d, etc):
我们可以确认,当加载到内存中(即在调试器中)时,该方法仍然存在于该地址(它没有被 ASLR 等):

$ lldb XAgentOSX
(lldb) target create “Xagent”
(lldb) 目标创建“Xagent”

Current executable set to ‘Xagent’ (x86_64).

(lldb) x/10i 0000000100014D8F
(lldb) x/10i 00000000100014D8F

0x100014d8f: 55 pushq %rbp
0x100014d8f: 55 pushq %rbp

0x100014d90: 48 89 e5 movq %rsp, %rbp
0x100014d90: 48 89 E5 MOVQ %RSP, %RBP

0x100014d93: 41 56 pushq %r14
0x100014d93: 41 56 pushq %r14

0x100014d95: 53 pushq %rbx
0x100014d95: 53 pushq %rbx

0x100014d96: 48 8b 3d f3 04 02 00 movq 0x204f3(%rip), %rdi ; (void *)0x0000000100035ac0: InjectApp
0x100014d96: 48 8B 3D F3 04 02 00 MOVQ 0x204f3(%RIP), %RDI ; (无效 *)0x0000000100035ac0:InjectApp

0x100014d9d: 48 8b 35 2c f5 01 00 movq 0x1f52c(%rip), %rsi ; “alloc”
0x100014d9d: 48 8B 35 2C F5 01 00 movq 0x1f52c(%rip), %rsi ;“阿洛克”

0x100014da4: 4c 8b 35 8d 63 01 00 movq 0x1638d(%rip), %r14 ; (void *)0x00007fffe5346b40: objc_msgSend
0x100014da4: 4c 8b 35 8d 63 01 00 movq 0x1638d(%rip), %r14 ; (无效 *)0x00007fffe5346b40:objc_msgSend

0x100014dab: 41 ff d6 callq *%r14
0x100014dab: 41 ff d6 callq *%r14

0x100014dae: 48 8b 35 23 f5 01 00 movq 0x1f523(%rip), %rsi ; “init”
0x100014dae: 48 8b 35 23 f5 01 00 movq 0x1f523(%RIP), %RSI ;“初始化”

0x100014db5: 48 89 c7 movq %rax, %rdi
0x100014db5: 48 89 C7 MOVQ %RAX, %RDI

To change the instruction pointer to point the this address, one can use the registry write command:

(lldb) register write $rip 0x0000000100014D8F
(lldb) 寄存器写$rip 0x0000000100014D8F

Normally, changing the instruction pointer to another address, for example the start of a method, causes all sorts of issues. For example stack and register values won’t be what the method is expecting (on x86_64 parameters are passed via registers). Luckily the -[BootXLoader injectApplication] method doesn’t take any parameters and is nicely self-contained. That is to say, it both initializes then invokes methods of the InjectApp class. Thus, changing RIP just kind of works in this instance 🙂
通常,将指令指针更改为另一个地址(例如方法的开头)会导致各种问题。例如,堆栈和寄存器值不会是方法所期望的(在x86_64参数通过寄存器传递)。幸运的是,-[BootXLoader injectApplication] 方法不采用任何参数,并且非常独立。也就是说,它既初始化,又调用 InjectApp 类的方法。因此,在这种情况下,更改 RIP 只是有效的:)

After creating an instance of the InjectApp class, the -[BootXLoader injectApplication] method invokes it’s injectRunningApp method:
创建 InjectApp 类的实例后,-[BootXLoader injectApplication] 方法调用其 injectRunningApp 方法:

void -[BootXLoader injectApplication](void * self, void * _cmd)
void -[BootXLoader injectApplication](void * self, void * _cmd)

  rbx = [[InjectApp alloc] init];
rbx = [[InjectApp alloc] 初始化];

  [rbx injectRunningApp];
  rdi = rbx;
  [rdi release]; [RDI 发布];
  return; 返回;

Setting a breakpoint on the InjectApp‘s injectRunningApp method, we can hit continue (‘c’) in the debugger and it will break at there. Neat!
在 InjectApp 的 injectRunningApp 方法上设置断点,我们可以在调试器中点击继续 (’c’),它会在那里中断。整洁!

(lldb) br s -a 0x000000010000fdff
(lldb) br s -a 0x000000010000fdff

Breakpoint 2: where = Xagent`-[InjectApp injectRunningApp], address = 0x000000010000fdff
断点 2:其中 = Xagent’-[InjectApp injectRunningApp],地址 = 0x000000010000fdff

(lldb) c (LLDB) C

Process 1045 resuming 进程 1045 恢复
Process 1045 stopped 进程 1045 已停止

* thread #1: Xagent`-[InjectApp injectRunningApp], stop reason = breakpoint 2.1
* 线程 #1:Xagent’-[InjectApp injectRunningApp],停止原因 = 断点 2.1

-> 0x10000fdff <+0>: pushq %rbp
-> 0x10000fdff <+0>:pushq %rbp

   0x10000fe00 <+1>: movq %rsp, %rbp
0x10000fe00 <+1>: movq %rsp, %rbp

   0x10000fe03 <+4>: pushq %r15
0x10000fe03 <+4>: pushq %r15

   0x10000fe05 <+6>: pushq %r14
0x10000fe05 <+6>:pushq %r14

The injectRunningApp method does three things:
injectRunningApp 方法执行三项操作:

  1. get a list of running apps
  2. calls ‘isInjectable:’ on each application
  3. calls ‘sendEventToPid:’ on each injectable application

First, injectRunningApp invokes NSWorkspace‘s ‘runningApplications’ method to get a list of running apps. This method returns a list of running apps that we can dump, after stepping over the call in the debugger:
首先,injectRunningApp 调用 NSWorkspace 的“runningApplications”方法来获取正在运行的应用程序的列表。此方法返回一个正在运行的应用列表,我们可以在调试器中单步执行调用后转储这些应用:

(lldb) po $rax (lldb) po $rax
<__NSArrayI 0x100206d50>(
<__NSArrayI 0x100206d50>(

<NSRunningApplication: 0x100205c00 ( – 101)>,
<NSRunning应用程序: 0x100205c00 ( – 101)>,

<NSRunningApplication: 0x100205e50 ( – 699)>,
<NSRunning应用程序: 0x100205e50 ( – 699)>,

<NSRunningApplication: 0x100205f50 ( – 698)>,
<NSRunning应用程序: 0x100205f50 ( – 698)>,

<NSRunningApplication: 0x100206050 ( – 704)>,
<NSRunning应用程序:0x100206050 ( – 704)>,

<NSRunningApplication: 0x100206150 ( – 700)>,
<NSRunning应用程序: 0x100206150 ( – 700)>,

<NSRunningApplication: 0x100206250 ( – 739)>,
<NSRunning应用程序: 0x100206250 ( – 739)>,

<NSRunningApplication: 0x100206450 ( – 756)>,
<NSRunning应用程序:0x100206450 ( – 756)>,

<NSRunningApplication: 0x100206550 ( – 749)>,
<NSRunning应用程序:0x100206550 ( – 749)>,

<NSRunningApplication: 0x100206650 ( – 747)>,
<NSRunning应用程序: 0x100206650 ( – 747)>,

<NSRunningApplication: 0x100206850 ( – 771)>,
<NSRunning应用程序: 0x100206850 ( – 771)>,

<NSRunningApplication: 0x100206950 ( – 758)>,
<NSRunning应用程序: 0x100206950 ( – 758)>,

<NSRunningApplication: 0x100206a50 ( – 797)>,
<NSRunningApplication: 0x100206a50 ( – 797)>,

<NSRunningApplication: 0x100206b50 ( – 954)>,
<NSRunning应用程序:0x100206b50 ( – 954)>,

<NSRunningApplication: 0x100206c50 ( – 967)>
<NSRunningApplication: 0x100206c50 ( – 967)>


Then, for each application, injectRunningApp gets the application’s localized name, then invokes the ‘isInjectable’ method on it. The isInjectable: method is part of the same InjectApp class. From the previous classdump output, we know it takes a single argument and returns a BOOL (YES/NO):
然后,对于每个应用程序,injectRunningApp 获取应用程序的本地化名称,然后对其调用“isInjectable”方法。isInjectable: 方法是同一 InjectApp 类的一部分。从前面的 classdump 输出中,我们知道它接受单个参数并返回一个 BOOL(YES/NO):

– (BOOL)isInjectable:(id)arg1;
– (BOOL)是可注射的:(id)arg1;

In the debugger, we can step into this method and then dump the passed in parameter (which will be in RDX):
在调试器中,我们可以单步执行此方法,然后转储传入的参数(将在 RDX 中):

(lldb) po $rdx (lldb) po $rdx
loginwindow 登录窗口

(lldb) po [$rdx class]
(lldb) po [$rdx类]


Ok, so easy to see that isInjectable: method takes a NSString (__NSCFConstantString) as its only parameter, and that this string is the name of the application to check for ‘injectability’
好的,很容易看出 isInjectable: 方法将 NSString (__NSCFConstantString) 作为其唯一参数,并且此字符串是要检查“可注入性”的应用程序的名称

In order to check if an application is injectable, the isInjectable: method simply checks the name of of the application that was passed in to the method, against a list of hardcoded application names:
为了检查应用程序是否可注入,isInjectable: 方法仅根据硬编码的应用程序名称列表检查传递给该方法的应用程序的名称:

New Xagent Mac Malware Linked with the APT28
The list of ‘blacklisted’ apps is named ‘appBListArray’ (more on this name later!) and contains the names of a variety of ‘system’ applications that presumably should not be injected into:

__data:00000001000369C0 _appBListArray
__data:00000001000369C0 _appBListArray

dq offset cfstr_Mdworker ; “mdworker”
DQ 偏移cfstr_Mdworker ;“MDWerer”

dq offset cfstr_Systemuiserver ; “SystemUIServer”
DQ 偏移cfstr_Systemuiserver ;“SystemUIServer”

dq offset cfstr_Dock ; “Dock”
DQ 偏移cfstr_Dock ;“码头”

dq offset cfstr_Launchd ; “launchd”
DQ 偏移cfstr_Launchd ;“已启动”

dq offset cfstr_Loginwindow ; “loginwindow”
DQ 偏移cfstr_Loginwindow ;“登录窗口”

dq offset cfstr_Usereventagent ; “UserEventAgent”
DQ 偏移cfstr_Usereventagent ;UserEventAgent

If the application name that was passed to the isInjectable: method matches any of the system, ‘blacklisted’ applications, the method will return ‘NO’ (0x0, false) to indicate that the application should not be injected into.
如果传递给 isInjectable: 方法的应用程序名称与任何系统“列入黑名单”的应用程序匹配,则该方法将返回“NO”(0x0,false),以指示不应将应用程序注入其中。

Now we know how the malware determines if an application is injectable or not. Back in the injectRunningApp method, if the isInjectable: method returns ‘YES’ (0x1, true) another method, sendEventToPid: is invoked:
现在我们知道恶意软件如何确定应用程序是否可注入。回到 injectRunningApp 方法,如果 isInjectable: 方法返回“YES”(0x1,true),则调用另一个方法 sendEventToPid:

if (!COND) {
  rbx = (r14, @selector(processIdentifier), rdx);
  rbx = ((@class(NSNumber), @selector(alloc), rdx), @selector(initWithInt:), rbx);
  (r15, @selector(sendEventToPid:), rbx);
  [rbx release];

From the above decompilation, we can see that the code retrieves the process identifier (PID) of the ‘injectable’ application and passes it to the sendEventToPid: method.
从上面的反编译中,我们可以看到代码检索“injectable”应用程序的进程标识符 (PID) 并将其传递给 sendEventToPid: 方法。

For example, the first application in my VM, that the code determines is injectable (i.e. is not in the blacklist) is SpotLight. We can dump various registers to confirm this:
例如,我的 VM 中代码确定可注入(即不在黑名单中)的第一个应用程序是 SpotLight。我们可以转储各种寄存器来确认这一点:

(lldb) po $rdi (lldb) po $rdi
<NSRunningApplication: 0x100206050 ( – 704)>
<NSRunning应用程序: 0x100206050 ( – 704)>

(lldb) po $rbx (lldb) po $rbx

The sendEventToPid: method is where things get interesting. Or so I thought!
sendEventToPid: 方法是事情变得有趣的地方。或者我是这么想的!

Reversing the method shows it using the SBApplication class to send various AppleEvent messages to the target process:
反转该方法显示它使用 SBApplication 类向目标进程发送各种 AppleEvent 消息:

r15 = [SBApplication applicationWithProcessIdentifier:rdx];
r15 = [SBApplication applicationWithProcessIdentifier:rdx];

(r15, @selector(setTimeout:), 0x1);
(r15, @selector(setTimeout:), 0x1);

(r15, @selector(setSendMode:), 0x1011);
(r15, @selector(setSendMode:), 0x1011);

rax = (r15, @selector(sendEvent:id:parameters:), 0x61736372, 0x67647574, 0x0);
rax = (r15, @selector(sendEvent:id:parameters:), 0x61736372, 0x67647574, 0x0);

r12 = (r13)(@class(NSNumber), @selector(numberWithInt:), getpid());
r12 = (r13)(@class(NSNumber), @selector(numberWithInt:), getpid());

(r15, @selector(setTimeout:), 0x1);
(r15, @selector(setTimeout:), 0x1);

(r15, @selector(setSendMode:), 0x1011);
(r15, @selector(setSendMode:), 0x1011);

(r15, @selector(sendEvent:id:parameters:), 0x4f504e65, 0x6f70656e, 0x7069646f, r12, 0x0);
(r15, @selector(sendEvent:id:parameters:), 0x4f504e65, 0x6f70656e, 0x7069646f, r12, 0x0);

0x1011 (in the setSendMode: call) maps to kAENoReply|kAENeverInteract|kAEDontRecord, while the parameters to the sendEvent:id:parameters: method call can be converted to ASCII:
0x1011(在 setSendMode: 调用中)映射到 kAENoReply|kAENeverInteract|kAEDontRecord,而 sendEvent:id:parameters: 方法调用的参数可以转换为 ASCII:

0x61736372 -> ‘ascr’
0x67647574 -> ‘gdut’
0x4f504e65 -> ‘OPNe’
0x6f70656e -> ‘open’
0x7069646f -> ‘pido’

In turn, several of these values map to Apple constants:
反过来,其中几个值映射到 Apple 常量:

#define kASAppleScriptSuite ‘ascr’
#define kGetAEUT ‘gdut’

Ok, so the malware is using AppleScript to inject code into a remote process. But wait a minute, we’ve seen this before? Where?? HackingTeam!
好的,所以恶意软件正在使用 AppleScript 将代码注入远程进程。但是等一下,我们以前见过这个吗?哪里??黑客团队!

Copy and Pasta 复制和粘贴
In 2015, I wrote a blog titled “Building HackingTeam’s OS X Implant For Fun & Profit”. In that blog I detailed how to build Hackteam’s RCS mac implant (while was leaked as part of a massive hack). An interesting feature of their code was their application injection logic which used AppleScript.
2015 年,我写了一篇博客,标题为“Building HackingTeam 的 OS X Implant For Fun & Profit”。在那篇博客中,我详细介绍了如何构建 Hackteam 的 RCS mac 植入物(虽然作为大规模黑客攻击的一部分泄露了)。他们代码的一个有趣功能是他们的应用程序注入逻辑,它使用了AppleScript。

In fact if we google the constants extracted from the injection code that we’ve been analyzing, there is only one hit on Google; HackingTeam’s leaked source code:

New Xagent Mac Malware Linked with the APT28Note that before the leaks, the venerable @osxreverser analyzed HackingTeam’s Mac capabilities in a talk at ShakaCon 6 titled: “F**k You Hacking Team” and discussed this injection technique:
请注意,在泄密之前,这位受人尊敬的@osxreverser在 ShakaCon 6 的一次演讲中分析了 HackingTeam 的 Mac 功能,标题为:“F**k You Hacking Team”,并讨论了这种注入技术:

New Xagent Mac Malware Linked with the APT28
Since @osxreverser previously covered this injection technique and source code (now) exists for it (see RCSMCore.m), we’ll end our binary analysis of the injection technique here. However, we’re faced with an interesting question. Does the XagentOSX/Komplex.B malware simply make use of the same injection technique? or does it use the exact same code? As we’ll show, I’m 100% confident it’s the latter.
由于@osxreverser之前介绍了这种注入技术,并且(现在)存在它的源代码(参见 RCSMCore.m),我们将在这里结束对注入技术的二进制分析。然而,我们面临着一个有趣的问题。XagentOSX/Komplex.B 恶意软件是否只是使用相同的注入技术?还是使用完全相同的代码?正如我们将要展示的,我 100% 相信是后者。

So why do I think XagentOSX/Komplex.B used the HackingTeam code versus simple re-used the same technique? Well first it’s alway easier (lazier?) to cut and paste from existing code than re-implement from scratch. And since the Russia supposedly bought stuff from HackingTeam or had access (like everybody else) to the leaked source code online – this seems the logical route.
那么,为什么我认为 XagentOSX/Komplex.B 使用了 HackingTeam 代码,而不是简单地重复使用相同的技术?首先,从现有代码中剪切和粘贴总是比从头开始重新实现更容易(更懒惰?)。而且由于俄罗斯据说从 HackingTeam 购买了东西,或者可以(像其他人一样)在线访问泄露的源代码——这似乎是合乎逻辑的路线。

We can make a much more compelling argument, that to me, proves without a doubt that it’s the same code, by comparing the compiled code in the XagentOSX/Komplex.B with the leaked HackingTeam source code.
我们可以提出一个更有说服力的论点,对我来说,通过将 XagentOSX/Komplex.B 中的编译代码与泄露的 HackingTeam 源代码进行比较,毫无疑问地证明它是相同的代码。

First injection-related method names and parameters count precisely match. HackingTeam’s source code (specifically RCSMCore.m) contains the following methods:

– (BOOL)isInjectable:(NSString*)appName;
– (void)sendEventToPid:(NSNumber *)thePid;
– (void)injectRunningApp;

It’s easy to see that XagentOSX/Komplex.B contains the same methods:
很容易看出 XagentOSX/Komplex.B 包含相同的方法:

New Xagent Mac Malware Linked with the APT28
Also global variable names and their values match. For example, the ‘appBListArray’ that was discussed earlier. Below is the array in HackingTeam’s code, followed by a dump of the array from the disassembly the malware. Note that the variable name (‘appBListArray’) and its values are the same in both:
此外,全局变量名称及其值也匹配。例如,前面讨论的“appBListArray”。下面是 HackingTeam 代码中的数组,然后是从反汇编恶意软件中转储数组。请注意,变量名称 (’appBListArray’) 及其值在两者中相同:

New Xagent Mac Malware Linked with the APT28

New Xagent Mac Malware Linked with the APT28
We can also look at HackingTeam’s source code and see that the compiled code in the malicious binary, matches almost verbatim. First, the Objective-C code of HackingTeam’s injectRunningApp method:
我们还可以查看 HackingTeam 的源代码,发现恶意二进制文件中的编译代码几乎逐字匹配。首先,HackingTeam 的 injectRunningApp 方法的 Objective-C 代码:

New Xagent Mac Malware Linked with the APT28Now here is the decompiled injectRunningApp method from XagentOSX/Komplex.B. Notice the same logic flow, API calls, etc:
现在,这是来自 XagentOSX/Komplex.B 的反编译 injectRunningApp 方法。请注意相同的逻辑流、API 调用等:

New Xagent Mac Malware Linked with the APT28
Finally we can also see that ‘mistakes’ in HackingTeam’s code made it directly into XagentOSX/Komplex.B. This, IMHO, is a very strong indicator that HackingTeam’s injection code was directly used (versus the malware author’s reimplementing the logic).
最后,我们还可以看到,HackingTeam代码中的“错误”直接进入了XagentOSX/Komplex.B。恕我直言,这是一个非常有力的指标,表明 HackingTeam 的注入代码被直接使用(而不是恶意软件作者重新实现逻辑)。

The following is from HackingTeam’s code:
以下是 HackingTeam 的代码:

New Xagent Mac Malware Linked with the APT28
This code, from the isInjectable method, is broken! How? Well if appName is nil, then and only then, will the second part of the if statement (![appName isKindOfClass: [NSString class]]) be executed. However, if appName is nil, executing that code makes no sense. Sending the isKindOfClass message to a nil object, will always just return nil.
这段来自 isInjectable 方法的代码被破坏了!如何?好吧,如果 appName 为 nil,那么也只有这样,if 语句的第二部分才会 (![appName isKindOfClass: [NSString 类]]) 被执行。但是,如果 appName 为 nil,则执行该代码毫无意义。将 isKindOfClass 消息发送到 nil 对象时,将始终只返回 nil。

As this code is at the start of the method, it appears to be an attempt at a sanity check on the method’s parameter. The check should be checking for a nil parameter, or for a parameter that is not an NSString. Which it would, if an || was used instead of an &&:
由于此代码位于方法的开头,因此它似乎是尝试对方法的参数进行健全性检查。检查应检查 nil 参数或非 NSString 的参数。如果 ||用于代替 &&:

if (appName == nil ||
if (appName == nil ||

   ![appName isKindOfClass: [NSString class]])
![appName isKindOfClass:[NSString 类]])

Why is this relevant? Well because this ‘bug’ from HackingTeam’s code ended up in the malware. Let’s look at a decompilation of the malware’s isInjectable method:
为什么这很重要?好吧,因为 HackingTeam 代码中的这个“错误”最终进入了恶意软件。让我们看一下恶意软件的 isInjectable 方法的反编译:

char -[InjectApp isInjectable:](void * self, void * _cmd, void * arg1)
char -[InjectApp isInjectable:](void * self, void * _cmd, void * arg1)

   r12 = [arg1 retain];
r12 = [arg1 保留];

   if (r12 != 0x0) goto continue;
if (r12 != 0x0) 转到继续;

   ;this will only be executed if r12 is nil, wtf!?
;这只会在 R12 为 nil、wtf!?

   if ([r12 isKindOfClass:[NSString class]] == 0x0) goto leave;
if ([r12 isKindOfClass:[NSString class]] == 0x0) 转到离开;

continue: 继续:
   ;do injection  ;做注射

It’s actually ‘easier’ to see this bug in the malware’s decompiled code (vs. the HackingTeam source code).
实际上,在恶意软件的反编译代码(与 HackingTeam 源代码相比)中看到此错误“更容易”。

The register, $r12 contains the parameter, appName. First it’s checked to make sure it’s not 0x0 (nil). If it’s not nil, the code jumps logic that continues. Otherwise, if the parameter appName is nil, the code will execute the second half of the if statement (![appName isKindOfClass: [NSString class]])). Again, I don’t see any reason why one would call isKindOfClass on a nil object.
寄存器 $r 12 包含参数 appName。首先,检查它以确保它不是0x0(无)。如果它不是 nil,则代码会跳转继续的逻辑。否则,如果参数 appName 为 nil,则代码将执行 if 语句 (![appName isKindOfClass: [NSString 类]]))。同样,我看不出有什么理由会在 nil 对象上调用 isKindOfClass。

At this point it should be pretty clear that the injection code in the XAgent/Komplex.B malware is HackingTeam’s original code, most likely copied and pasted. However, if we continue our analysis, we can see that certain parts of HackingTeam’s code (i.e. code not related to the app injection) didn’t make it into the malware’s binary. In other words, it was a selected copy and paste. How can we tell? Let’s take a closer look.
在这一点上,应该很清楚 XAgent/Komplex.B 恶意软件中的注入代码是 HackingTeam 的原始代码,很可能是复制和粘贴的。但是,如果我们继续分析,我们可以看到 HackingTeam 代码的某些部分(即与应用程序注入无关的代码)没有进入恶意软件的二进制文件。换句话说,它是选定的复制和粘贴。我们怎么知道?让我们仔细看看。

Take for example, the following disassembled code from the malware’s -(void)sendEventToPid:(NSNumber *)thePid method:
例如,以下来自恶意软件的 -(void)sendEventToPid:(NSNumber *)thePid 方法的反汇编代码:

New Xagent Mac Malware Linked with the APT28
A few odd things; first there is this:

[*_gControlFlagLock lock];

[*_gControlFlagLock unlock];

A lock of a global variable (gControlFlagLock), immediately followed by an unlock seems pointless. Normally one locks a variable, then executes some critical section of code, then unlocks the variable.
锁定全局变量 (gControlFlagLock),然后立即解锁似乎毫无意义。通常,人们会锁定一个变量,然后执行一些关键的代码部分,然后解锁变量。

Morever the _gControlFlagLock variable only is referenced in the lock/unlock calls and nowhere else in the malware’s binary. This means it is never initialized, and thus will be be nil.

New Xagent Mac Malware Linked with the APT28
Following this pointless locking (which wouldn’t do anything anyways, as _gControlFlagLock is always nil), there is a string comparison that makes no sense:

rdx = @”STOP”; rdx = @“停止”;
if ([@”START” isEqualToString:rdx] == 0x0) {
if ([@“START” isEqualToString:rdx] == 0x0) {

“STOP” will never equal “START” so again this check pointless, leaving one wondering why it’s even in the binary.

Both of these ‘issues’ be explained by a ‘bad’ copy and paste job, where some unneeded code was deleted, while other code was still left in (though it could have been removed, as it was now spurious). Looking at HackingTeam’s code we see:
这两个“问题”都可以用“坏”的复制和粘贴作业来解释,其中删除了一些不需要的代码,而其他代码仍然保留在里面(尽管它本可以被删除,因为它现在是虚假的)。查看 HackingTeam 的代码,我们看到:

New Xagent Mac Malware Linked with the APT28
The taskManager variable is of type __m_MTaskManager. This appears to be a class that, as its name implies, deals with delegating tasks and/or otherwise controlling various components of the entire HackingTeam implant. Obviously if one is looking just to rip out (off?) the injection part of the code, the ‘Task Manager’ code/logic isn’t needed and thus would be removed.
taskManager 变量的类型为 __m_MTaskManager。顾名思义,这似乎是一个处理委派任务和/或以其他方式控制整个 HackingTeam 植入物的各种组件的类。显然,如果只是想撕掉(掉?)代码的注入部分,则不需要“任务管理器”代码/逻辑,因此将被删除。

The removal of ‘Task Manager’ code/logic explains both why the code in the compiled XAgent/Komplex.B malware had a call to [gControlFlagLock lock] immediately followed by a call to [gControlFlagLock unlock], as well as comparison between two hard-code strings (that would never match).
删除“任务管理器”代码/逻辑解释了为什么编译的 XAgent/Komplex.B 恶意软件中的代码立即调用了 [gControlFlagLock lock],然后调用了 [gControlFlagLock unlock],以及两个硬代码字符串之间的比较(永远不会匹配)。

Conclusion 结论
Sometimes reversing sessions take interesting twists and turns. Here, I started by looking that the ‘app injection’ logic in XAgent/Komplex.B with the goal of uncovering the injection mechanism. However, during this investigation, we discovered that the malware was clearly utilizing code from HackingTeam’s leaked Mac implant. By comparing various pieces of HackingTeam’s source code with code compiled into the binary, hopefully it’s quite obvious that the XAgent/Komplex.B authors used the HackingTeam’s code directly – versus reimplementing it themselves.
有时,逆转过程会经历有趣的曲折。在这里,我首先查看了 XAgent/Komplex.B 中的“应用程序注入”逻辑,目的是揭示注入机制。但是,在这次调查中,我们发现该恶意软件显然使用了 HackingTeam 泄露的 Mac 植入物中的代码。通过将 HackingTeam 的各种源代码与编译到二进制文件中的代码进行比较,希望很明显 XAgent/Komplex.B 的作者直接使用了 HackingTeam 的代码,而不是自己重新实现它。

If I had to guess, I’d say that even though Russia supposedly bought stuff from HackingTeam (which could have included source code?), the malware authors likely just copied and pasted from the HackingTeam leaked source code. Though we’ll likely never know all the answers, its surprising how much one can ascertain by simple reversing a binary!

原文始发于Objective See:New Xagent Mac Malware Linked with the APT28

版权声明:admin 发表于 2024年4月17日 下午6:55。
转载请注明:New Xagent Mac Malware Linked with the APT28 | CTF导航