进程注入:用一个漏洞打破所有的macOS安全层

作者:Sec-Labs | 发布时间:

如果您使用 Xcode 13.2 创建了一个新的 macOS 应用程序,您可能会注意到模板中的这个新方法:

- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app {
	return YES;
}

 

这被添加到 Xcode 模板中以解决我们报告的进程注入漏洞!

在 macOS 12.0.1 Monterey 中,Apple 修复了 CVE-2021-30873。 这是一个进程注入漏洞,影响(本质上)所有基于 macOS AppKit 的应用程序。 我们向 Apple 报告了此漏洞,以及使用此漏洞逃离沙箱、提升 root 权限并绕过 SIP 的文件系统限制的方法。 在这篇文章中,我们将首先描述什么是进程注入,然后是这个漏洞的细节,最后是我们如何滥用它。

进程注入

进程注入是一个进程在不同进程中执行代码的能力。 在 Windows 中,使用它的一个原因是逃避防病毒扫描程序的检测,例如通过称为 DLL 劫持的技术。 这允许恶意代码伪装成不同可执行文件的一部分。 在 macOS 中,由于两个应用程序可以拥有的权限不同,这种技术的影响要大得多。

在经典的 Unix 安全模型中,每个进程都以特定用户身份运行。 每个文件都有一个所有者、组和标志,这些标志确定允许哪些用户读取、写入或执行该文件。 以同一用户身份运行的两个进程具有相同的权限:假设它们之间没有安全边界。 用户是安全边界,流程不是。 如果两个进程作为同一个用户运行,那么一个进程可以作为调试器附加到另一个进程,允许它读取或写入另一个进程的内存和寄存器。 root 用户是一个例外,因为它可以访问所有文件和进程。 因此,root 始终可以访问计算机上的所有数据,无论是在磁盘上还是在 RAM 中。

从本质上讲,在引入 SIP(​​也称为“无根”)之前,这与 macOS 的安全模型相同。 这个名称并不意味着不再有 root 用户,但现在它本身的功能已经不那么强大了。 例如,某些文件不能再被 root 用户读取,除非该进程也具有特定的权利。 权利是为可执行文件生成代码签名时包含的元数据。 检查进程是否具有特定权利是 macOS 中许多安全措施的重要组成部分。 Unix 所有权规则仍然存在,这是在它们之上的额外权限检查层。 某些敏感文件(例如 Mail.app 数据库)和功能(例如网络摄像头)不再仅具有 root 权限,但需要额外的权利。 换句话说,权限提升不足以完全破坏 Mac 上的敏感数据。

例如,使用以下命令我们可以看到 Mail.app 的权利:

$ codesign -dvvv --entitlements - /System/Applications/Mail.app

在输出中,我们看到以下权利:

... [Key] com.apple.rootless.storage.Mail [Value] [Bool] true ...

这就是授予 Mail.app 读取受 SIP 保护的邮件数据库的权限的原因,而其他恶意软件将无法读取它。

除了权利之外,还有信任、透明度和控制 (TCC) 处理的权限。 这是应用程序可以请求访问例如网络摄像头、麦克风和(在最近的 macOS 版本中)文件(例如 Documents 和 Download 文件夹中的文件)的机制。 这意味着即使不使用 Mac 应用程序沙箱的应用程序也可能无法访问某些功能或文件。

当然,如果任何进程都可以作为调试器附加到同一用户的另一个进程,那么权利和 TCC 权限将毫无用处。 如果一个应用程序可以访问网络摄像头,但另一个没有,那么一个进程可以作为调试器附加到另一个进程并注入一些代码来窃取网络摄像头视频。 为了解决这个问题,调试其他应用程序的能力受到了严格限制。

将使用了数十年的安全模型更改为更具限制性的模型是很困难的,尤其是在像 macOS 这样复杂的系统中。 附加调试器只是一个例子,有许多类似的技术可用于将代码注入不同的进程。 苹果已经压制了其中许多技术,但许多其他技术可能仍未被发现。

除了 Apple 自己的代码之外,这些漏洞也可能出现在第三方软件中。 在特定应用程序中发现进程注入漏洞是很常见的,这意味着该应用程序的权限(TCC 权限和权利)可供所有其他进程获取。 修复这些问题是一个艰难的过程,因为许多第三方开发人员并不熟悉这种新的安全模型。 报告这些漏洞通常需要充分解释这个新模型! 尤其是 Electron 应用程序 臭名昭著 因其易于注入

比一个应用程序中的进程注入漏洞更危险的是影响多个甚至 所有 应用程序的进程注入技术。 这将允许访问大量不同的权利和 TCC 权限。 影响所有应用程序的通用进程注入漏洞是一个非常强大的工具,我们将在本文中进行演示。

保存状态漏洞

关闭 Mac 时,它会提示您询问是否应该在下次登录时重新打开当前打开的窗口。这是功能上称为“已保存状态”或“持久 UI”的一部分。

b018d7ad23141027

 

 

重新打开窗口时,它甚至可以恢复某些应用程序中尚未保存的新文档。

它用于更多的地方,而不仅仅是在关机时。 例如,它还用于名为 App Nap 的功能。 当应用程序有一段时间处于非活动状态(不是焦点应用程序,没有播放音频等)时,系统可以告诉它保存其状态并终止进程。 macOS 不断显示应用程序窗口的静态图像,并且在 Dock 中它似乎仍在运行,但实际上并非如此。 当用户切换回应用程序时,它会快速启动并恢复其状态。 在内部,这也使用相同的保存状态功能。

在使用 AppKit 构建应用程序时,对保存状态的支持在很大程度上是自动的。 在某些情况下,应用程序需要在保存状态中包含自己的对象,以确保可以恢复完整状态,例如在基于文档的应用程序中。

每次应用程序失去焦点时,它都会写入文件:

~/Library/Saved Application State/<Bundle ID>.savedState/windows.plist
~/Library/Saved Application State/<Bundle ID>.savedState/data.data

windows.plist文件包含应用程序所有打开窗口的列表。 (以及其他一些看起来不像窗口的东西,例如菜单栏和 Dock 菜单。)

例如,一个 windows.plist对于 TextEdit.app 可能如下所示:

<?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">
<array>
	<dict>
		<key>MenuBar AvailableSpace</key>
		<real>1248</real>
		<key>NSDataKey</key>
		<data>
		Ay1IqBriwup4bKAanpWcEw==
		</data>
		<key>NSIsMainMenuBar</key>
		<true/>
		<key>NSWindowID</key>
		<integer>1</integer>
		<key>NSWindowNumber</key>
		<integer>5978</integer>
	</dict>
	<dict>
		<key>NSDataKey</key>
		<data>
		5lyzOSsKF24yEcwAKTBSVw==
		</data>
		<key>NSDragRegion</key>
		<data>
		AAAAgAIAAADAAQAABAAAAAMAAABHAgAAxgEAAAoAAAADAAAABwAAABUAAAAb
		AAAAKQAAAC8AAAA9AAAARwIAAMcBAAAMAAAAAwAAAAcAAAAVAAAAGwAAACkA
		AAAvAAAAPQAAAAkBAABLAQAARwIAANABAAAKAAAAFQAAABsAAAApAAAALwAA
		AD0AAAAJAQAASwEAAD4CAADWAQAABgAAAAwAAAAJAQAASwEAAD4CAADXAQAA
		BAAAAAwAAAA+AgAA2QEAAAIAAAD///9/
		</data>
		<key>NSTitle</key>
		<string>Untitled</string>
		<key>NSUIID</key>
		<string>_NS:34</string>
		<key>NSWindowCloseButtonFrame</key>
		<string>{{7, 454}, {14, 16}}</string>
		<key>NSWindowFrame</key>
		<string>177 501 586 476 0 0 1680 1025 </string>
		<key>NSWindowID</key>
		<integer>2</integer>
		<key>NSWindowLevel</key>
		<integer>0</integer>
		<key>NSWindowMiniaturizeButtonFrame</key>
		<string>{{27, 454}, {14, 16}}</string>
		<key>NSWindowNumber</key>
		<integer>5982</integer>
		<key>NSWindowWorkspaceID</key>
		<string></string>
		<key>NSWindowZoomButtonFrame</key>
		<string>{{47, 454}, {14, 16}}</string>
	</dict>
	<dict>
		<key>CFBundleVersion</key>
		<string>378</string>
		<key>NSDataKey</key>
		<data>
		P7BYxMryj6Gae9Q76wpqVw==
		</data>
		<key>NSDockMenu</key>
		<array>
			<dict>
				<key>command</key>
				<integer>1</integer>
				<key>mark</key>
				<integer>2</integer>
				<key>name</key>
				<string>Untitled</string>
				<key>system-icon</key>
				<integer>1735879022</integer>
				<key>tag</key>
				<integer>2</integer>
			</dict>
			<dict>
				<key>separator</key>
				<true/>
			</dict>
			<dict>
				<key>command</key>
				<integer>2</integer>
				<key>indent</key>
				<integer>0</integer>
				<key>name</key>
				<string>New Document</string>
				<key>tag</key>
				<integer>0</integer>
			</dict>
		</array>
		<key>NSExecutableInode</key>
		<integer>1152921500311961010</integer>
		<key>NSIsGlobal</key>
		<true/>
		<key>NSSystemAppearance</key>
		<data>
		YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9i
		amVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGkCwwRElUk
		bnVsbNINDg8QViRjbGFzc18QEE5TQXBwZWFyYW5jZU5hbWWAA4ACXxAUTlNB
		cHBlYXJhbmNlTmFtZUFxdWHSExQVFlokY2xhc3NuYW1lWCRjbGFzc2VzXE5T
		QXBwZWFyYW5jZaIVF1hOU09iamVjdAgRGiQpMjdJTFFTWF5jan1/gZidqLG+
		wQAAAAAAAAEBAAAAAAAAABgAAAAAAAAAAAAAAAAAAADK
		</data>
		<key>NSSystemVersion</key>
		<array>
			<integer>12</integer>
			<integer>2</integer>
			<integer>1</integer>
		</array>
		<key>NSWindowID</key>
		<integer>4294967295</integer>
		<key>NSWindowZOrder</key>
		<array>
			<integer>5982</integer>
		</array>
	</dict>
</array>
</plist>

 

data.data文件包含自定义二进制格式。 它由一个记录列表组成,每条记录都包含一个 AES-CBC 加密的序列化对象。 这 windows.plist文件包含密钥 ( NSDataKey) 和一个 ID ( NSWindowID) 作记录 data.data它对应。 1

例如:

00000000  4e 53 43 52 31 30 30 30  00 00 00 01 00 00 01 b0  |NSCR1000........|
00000010  ec f2 26 b9 8b 06 c8 d0  41 5d 73 7a 0e cc 59 74  |..&.....A]sz..Yt|
00000020  89 ac 3d b3 b6 7a ab 1b  bb f7 84 0c 05 57 4d 70  |..=..z.......WMp|
00000030  cb 55 7f ee 71 f8 8b bb  d4 fd b0 c6 28 14 78 23  |.U..q.......(.x#|
00000040  ed 89 30 29 92 8c 80 bf  47 75 28 50 d7 1c 9a 8a  |..0)....Gu(P....|
00000050  94 b4 d1 c1 5d 9e 1a e0  46 62 f5 16 76 f5 6f df  |....]...Fb..v.o.|
00000060  43 a5 fa 7a dd d3 2f 25  43 04 ba e2 7c 59 f9 e8  |C..z../%C...|Y..|
00000070  a4 0e 11 5d 8e 86 16 f0  c5 1d ac fb 5c 71 fd 9d  |...]........\q..|
00000080  81 90 c8 e7 2d 53 75 43  6d eb b6 aa c7 15 8b 1a  |....-SuCm.......|
00000090  9c 58 8f 19 02 1a 73 99  ed 66 d1 91 8a 84 32 7f  |.X....s..f....2.|
000000a0  1f 5a 1e e8 ae b3 39 a8  cf 6b 96 ef d8 7b d1 46  |.Z....9..k...{.F|
000000b0  0c e2 97 d5 db d4 9d eb  d6 13 05 7d e0 4a 89 a4  |...........}.J..|
000000c0  d0 aa 40 16 81 fc b9 a5  f5 88 2b 70 cd 1a 48 94  |..@.......+p..H.|
000000d0  47 3d 4f 92 76 3a ee 34  79 05 3f 5d 68 57 7d b0  |G=O.v:.4y.?]hW}.|
000000e0  54 6f 80 4e 5b 3d 53 2a  6d 35 a3 c9 6c 96 5f a5  |To.N[=S*m5..l._.|
000000f0  06 ec 4c d3 51 b9 15 b8  29 f0 25 48 2b 6a 74 9f  |..L.Q...).%H+jt.|
00000100  1a 5b 5e f1 14 db aa 8d  13 9c ef d6 f5 53 f1 49  |.[^..........S.I|
00000110  4d 78 5a 89 79 f8 bd 68  3f 51 a2 a4 04 ee d1 45  |MxZ.y..h?Q.....E|
00000120  65 ba c4 40 ad db e3 62  55 59 9a 29 46 2e 6c 07  |e..@...bUY.)F.l.|
00000130  34 68 e9 00 89 15 37 1c  ff c8 a5 d8 7c 8d b2 f0  |4h....7.....|...|
00000140  4b c3 26 f9 91 f8 c4 2d  12 4a 09 ba 26 1d 00 13  |K.&....-.J..&...|
00000150  65 ac e7 66 80 c0 e2 55  ec 9a 8e 09 cb 39 26 d4  |e..f...U.....9&.|
00000160  c8 15 94 d8 2c 8b fa 79  5f 62 18 39 f0 a5 df 0b  |....,..y_b.9....|
00000170  3d a4 5c bc 30 d5 2b cc  08 88 c8 49 d6 ab c0 e1  |=.\.0.+....I....|
00000180  c1 e5 41 eb 3e 2b 17 80  c4 01 64 3d 79 be 82 aa  |..A.>+....d=y...|
00000190  3d 56 8d bb e5 7a ea 89  0f 4c dc 16 03 e9 2a d8  |=V...z...L....*.|
000001a0  c5 3e 25 ed c2 4b 65 da  8a d9 0d d9 23 92 fd 06  |.>%..Ke.....#...|
[...]

 

每当启动应用程序时,AppKit 都会读取这些文件并恢复应用程序的窗口。 这会自动发生,无需应用程序执行任何操作。 读取这些文件的代码非常小心:如果应用程序崩溃,那么状态可能也已损坏。 如果应用程序在恢复状态时崩溃,那么下次状态将被丢弃并重新启动。

我们发现的漏洞是加密的序列化对象存储在 data.data文件 使用“安全编码”。 为了解释这意味着什么,我们将首先解释序列化漏洞,特别是在 macOS 上。

序列化对象

许多面向对象的编程语言都增加了对二进制序列化的支持,它将对象转换为字节串并返回。 与 XML 和 JSON 不同,它们是自定义的、特定于语言的格式。 在某些编程语言中,对类的序列化支持是自动的,在其他语言中,类可以选择加入。

在其中许多语言中,这些功能导致漏洞。 许多实现中的问题是首先创建一个对象, 然后 检查其类型。 在创建或销毁这些对象时,可能会调用这些对象的方法。 通过以不寻常的方式组合对象,有时可以在反序列化恶意对象时获得远程代码执行。 因此,对可能通过网络从不受信任方接收的任何数据使用这些序列化函数并不是一个好主意。

对于 Python pickle和红宝石 Marshall.load远程代码执行很简单。 在 Java 中 ObjectInputStream.readObject和 C#,如果使用某些常用的库,RCE 是可能的。 ysoserial ysoserial.net 工具可用于根据使用的库生成有效负载 在 PHP 中,RCE 的可利用性很少见。

Objective-C 序列化

在 Objective-C 中,类可以实现 NSCoding协议可序列化。 的子类 NSCoder, 如 NSKeyedArchiverNSKeyedUnarchiver, 可用于序列化和反序列化这些对象。

这在实践中是如何工作的如下。 一个实现的类 NSCoding必须包含一个方法:

- (id)initWithCoder:(NSCoder *)coder;

在这个方法中,这个对象可以使用 coder解码其实例变量,使用方法如 -decodeObjectForKey:, -decodeIntegerForKey:, -decodeDoubleForKey:等。当它使用 -decodeObjectForKey:,编码器会递归调用 -initWithCoder:在那个对象上,最终解码整个对象图。

苹果也意识到反序列化不可信输入的风险,所以在 10.8 中, NSSecureCoding添加了协议。 该 文档 指出:

一种协议,能够以对对象替换攻击具有鲁棒性的方式进行编码和解码。

这意味着在解码对象时需要包含一组允许的类,而不是先创建对象然后检查其类型。

因此,不要使用不安全的构造:

id obj = [decoder decodeObjectForKey:@"myKey"];
if (![obj isKindOfClass:[MyClass class]]) { /* ...fail... */ }

必须使用以下内容:

id obj = [decoder decodeObjectOfClass:[MyClass class] forKey:@"myKey"];

这意味着当创建安全编码器时, -decodeObjectForKey:不再允许,但 -decodeObjectOfClass:forKey:必须使用。

这使得可利用的漏洞变得更加困难,但它仍然可能发生。 这里要注意的一件事是允许指定类的子类。 例如,如果 NSObject指定类,然后所有类实现 NSCoding仍然被允许。 要是 NSDictionary是预期的,并且导入的框架包含一个很少使用且易受攻击的子类 NSDictionary,那么这也可能造成漏洞。

在 Apple 的所有操作系统中,这些序列化对象被广泛使用,通常用于进程间的数据交换。 例如, NSXPCConnection严重依赖安全序列化来实现远程方法调用。 在 iMessage 中,这些序列化的对象甚至可以通过网络与其他用户进行交换。 在这种情况下,始终启用安全编码非常重要。

创建恶意序列化对象

在里面 data.data保存状态的文件,对象使用 NSKeyedArchiver没有启用安全编码。 这意味着我们可以包含任何实现 NSCoding协议。 造成这种情况的可能原因是应用程序可以使用自己的对象扩展已保存状态,并且因为已保存状态功能比 NSSecureCoding,Apple 不能只将其升级为安全编码,因为这可能会破坏第三方应用程序。

为了利用这一点,我们需要一种方法来构造一个允许我们执行任意代码的对象链。 但是,似乎不存在与 Objective-C 的 ysoserial 类似的项目,我们也找不到在 macOS 中滥用不安全反序列化的其他示例。 在 Remote iPhone Exploitation Part 1: Poking Memory via iMessage 和 CVE-2019-8641 ,Google Project Zero 的 Samuel Groß 描述了 通过滥用 NSSharedKeyDictionary,一个不常见的子类 NSDictionary. 由于此漏洞现已修复,我们无法使用它。

通过反编译大量 -initWithCoder:在 AppKit 中的方法,我们最终找到了 2 个对象的组合,我们可以使用它们在另一个反序列化对象上调用任意 Objective-C 方法。

我们从 NSRuleEditor. 这 -initWithCoder:此类的方法创建与来自同一存档的对象的绑定,其密钥路径也从存档中获得。

绑定 是 Cocoa 中的一种反应式编程技术。 它可以直接将模型绑定到视图,而无需控制器的样板代码。 每当模型中的值更改或用户在视图中进行更改时,更改都会自动传播。

创建一个绑定调用该 方法

- (void)bind:(NSBindingName)binding 
    toObject:(id)observable 
 withKeyPath:(NSString *)keyPath 
     options:(NSDictionary<NSBindingOption, id> *)options;

 

这绑定了属性 binding接收器的 keyPathobservable. 一个 keypath 一个字符串,例如,可以用来访问对象的嵌套属性。 但更常见的创建绑定的方法是在 Xcode 中将它们创建为 XIB 文件的一部分。

例如,假设模型是一个类 Person, 它有一个属性 @property (readwrite, copy) NSString *name;. 然后,您可以将文本字段的“值”绑定到人的“名称”键路径,以创建一个显示(并且可以编辑)人名的字段。

在 XIB 编辑器中,这将创建如下:

 

f33c2d62e4141300

 

keypath 的不同选项实际上是 相当复杂 的。 例如,当与“foo”键路径绑定时,它会首先检查是否有一个方法 getFoo, foo, isFoo_foo存在。 这通常用于访问对象的属性,但这不是必需的。 创建绑定时,会在创建绑定时立即调用该方法,以提供初始值。 该方法是否实际返回 void 并不重要。 这意味着通过在反序列化期间创建绑定,我们可以使用它来调用其他反序列化对象的零参数方法!

ID NSRuleEditor::initWithCoder:(ID param_1,SEL param_2,ID unarchiver)
{
	...

	id arrayOwner = [unarchiver decodeObjectForKey:@"NSRuleEditorBoundArrayOwner"];

	...

	if (arrayOwner) {
	  keyPath = [unarchiver decodeObjectForKey:@"NSRuleEditorBoundArrayKeyPath"];
	  [self bind:@"rows" toObject:arrayOwner withKeyPath:keyPath options:nil];
	}

	...
}

 

在这种情况下,我们使用它来调用 -draw在下一个对象上。

我们使用的下一个对象是 NSCustomImageRep目的。 这会从存档中获得一个选择器(一个方法名称)作为字符串和一个对象。 当。。。的时候 -draw方法被调用时,它从对象上的选择器中调用方法。 它将自己作为第一个参数传递:

ID NSCustomImageRep::initWithCoder:(ID param_1,SEL param_2,ID unarchiver)
{
	...
	id drawObject = [unarchiver decodeObjectForKey:@"NSDrawObject"];
	self.drawObject = drawObject;
	id drawMethod = [unarchiver decodeObjectForKey:@"NSDrawMethod"];
	SEL selector = NSSelectorFromString(drawMethod);
	self.drawMethod = selector;
	...
}

...

void ___24-[NSCustomImageRep_draw]_block_invoke(long param_1)
{
  ...
  [self.drawObject performSelector:self.drawMethod withObject:self];
  ...
}

 

通过反序列化这两个类,我们现在可以调用零参数方法和多参数方法,尽管第一个参数是 NSCustomImageRepobject 和剩余的参数将是仍然在这些寄存器中的任何内容。 尽管如此,它是一个非常强大的原语。 我们将在以后的博客文章中介绍我们使用的链的其余部分。

开发

沙盒逃生

首先,我们利用这个漏洞逃过了 Mac 应用程序沙箱。 为了解释这一点,需要更多关于已保存状态的背景知识。

在沙盒应用程序中,许多文件将存储在 ~/Library而是存储在单独的容器中。 因此,不要将其状态保存在:

~/Library/Saved Application State/<Bundle ID>.savedState/

沙盒应用程序将其状态保存到:

~/Library/Containers/<Bundle ID>/Data/Library/Saved Application State/<Bundle ID>.savedState/

显然,当系统在应用程序仍在运行时关闭时(当显示提示询问用户是否下次重新打开窗口时),第一个位置通过符号链接到第二个位置 talagent. 我们不确定为什么,这可能与将应用程序升级到沙盒的新版本有关。

其次,大多数应用程序无法访问所有文件。 沙盒应用程序当然受到很大限制,但添加 TCC 后,即使访问下载、文档等文件夹也需要用户批准。 如果应用程序会打开一个打开或保存面板,如果用户只能看到该应用程序有权访问的文件,那将非常不方便。 为了解决这个问题,打开这样的面板时会启动一个不同的进程: com.apple.appkit.xpc.openAndSavePanelService. 尽管窗口本身是应用程序的一部分,但它的内容是由 openAndSavePanelService 绘制的。 这是一个 XPC 服务,可以完全访问所有文件。 当用户在面板中选择一个文件时,应用程序将获得对该文件的临时访问权限。 这样,即使在无权列出这些文件的应用程序中,用户仍然可以浏览整个磁盘。

b8f01f43ad141412

 

由于它是一个 XPC 服务,服务类型为 Application,因此它是为每个应用程序单独启动的。

我们注意到的是,这个 XPC 服务读取了它的保存状态,但是使用了启动它的应用程序的捆绑 ID! 由于此面板可能是多个应用程序的已保存状态的一部分,因此需要将每个应用程序的状态分开是有一定意义的。

事实证明,它从 外部 ,但使用应用程序的捆绑 ID:

~/Library/Saved Application State/<Bundle ID>.savedState/

 

但正如我们提到的,如果应用程序在用户关闭计算机时打开,那么这将是容器路径的符号链接。

因此,我们可以通过以下方式逃离沙箱:

  1. 如果符号链接尚不存在,则在应用程序打开时等待用户关闭。
  2. 写恶意 data.datawindows.plist应用程序自己的容器中的文件。
  3. 打开一个 NSOpenPanel或者 NSSavePanel.

com.apple.appkit.xpc.openAndSavePanelService进程现在将反序列化恶意对象,让我们在非沙盒进程中执行代码。

这比其他问题更早修复,如 macOS 11.3 中的 CVE-2021-30659。 苹果通过不再从同一位置加载状态来解决这个问题 com.apple.appkit.xpc.openAndSavePanelService.

权限提升

通过将我们的代码注入到具有特定权限的应用程序中,我们可以将我们的权限提升到 root。 为此,我们可以应用 A2nkF 在 Unauthd-Logic bugs FTW

某些应用程序有权 com.apple.private.AuthorizationServices包含值 system.install.apple-software. 这意味着允许此应用程序在未经用户授权的情况下安装具有 Apple 生成的签名的包。 例如,“Install Command Line Developer Tools.app”和“Bootcamp Assistant.app”具有此权利。 A2nkF 还发现了一个由 Apple 签名的包,其中包含一个漏洞: macOSPublicBetaAccessUtility.pkg. 当此软件包安装到特定磁盘时,它将从该磁盘运行(以 root 身份)安装后脚本。 该脚本假定它被安装到包含 macOS 的磁盘上,但未选中此项。 因此,通过在同一位置创建恶意脚本,可以通过安装此包以 root 身份执行代码。

漏洞利用步骤如下:

  1. 创建一个 RAM 磁盘并将恶意脚本复制到将要执行的路径 macOSPublicBetaAccessUtility.pkg.
  2. 将我们的代码注入到应用程序中 com.apple.private.AuthorizationServices权利包含 system.install.apple-software通过创建 windows.plistdata.data该应用程序的文件,然后启动它。
  3. 使用注入的代码安装 macOSPublicBetaAccessUtility.pkg打包到 RAM 磁盘。
  4. 等待安装后脚本运行。

在 A2nkF 的文章中,安装后脚本在没有 SIP 的文件系统限制的情况下运行。 它从安装过程中继承了这一点,安装过程需要它,因为包安装可能需要写入受 SIP 保护的位置。 Apple 已解决此问题:安装后和预安装脚本不再是 SIP 豁免。 但是,仍然可以使用该软件包及其权限提升,因为 Apple 仍然使用相同的易受攻击的安装程序软件包。

SIP文件系统绕过

既然我们已经逃离了沙箱并将我们的权限提升为 root,我们也确实想绕过 SIP。 为此,我们查看了所有可用的应用程序以找到具有合适权利的应用程序。 最终,我们在 macOS Big Sur Beta 安装盘镜像中发现了一些东西:“macOS Update Assistant.app”有 com.apple.rootless.install.heritable权利。 这意味着该进程可以写入所有受 SIP 保护的位置(并且它是可遗传的,这很方便,因为我们可以只生成一个 shell)。 虽然它应该只在 beta 安装期间使用,但我们可以将它复制到正常的 macOS 环境并在那里运行。

对此的利用非常简单:

  1. 创建恶意 windows.plistdata.data“macOS Update Assistant.app”的文件。
  2. 启动“macOS 更新助手.app”。

当免除 SIP 的文件系统限制时,我们可以从受保护的位置读取所有文件,例如用户的 Mail.app 邮箱。 我们还可以修改 TCC 数据库,这意味着我们可以授予自己访问网络摄像头、麦克风等的权限。我们还可以将我们的恶意软件持久保存在受 SIP 保护的位置,使其很难被 Apple 以外的任何人删除。 最后,我们可以更改已批准内核扩展的数据库。 这意味着我们可以静默加载新的内核扩展,无需用户批准。 当与易受攻击的内核扩展(或允许签署内核扩展的代码签名证书)结合使用时,我们将能够获得内核代码执行,这也将允许禁用所有其他限制。

演示

我们录制了以下视频来演示不同的步骤。 它首先显示应用程序“Sandbox”已被沙盒化,然后它逃离其沙盒并启动“Privesc”。 这会将权限提升到 root 并启动“SIP 绕过”。 最后,这会打开一个不受 SIP 文件系统限制的反向 shell,这可以通过在 /var/db/SystemPolicyConfiguration(已批准内核模块的数据库存储位置):

 

 

修复

Apple 在 11.3 中首先修复了沙盒逃逸,不再读取应用程序的保存状态 com.apple.appkit.xpc.openAndSavePanelService(CVE-2021-30659)。

修复漏洞的其余部分更加复杂。 第三方应用程序可能会将他们自己的对象存储在已保存状态,而这些对象可能不支持安全编码。 这让我们回到介绍中的方法: -applicationSupportsSecureRestorableState:. 应用程序现在可以通过返回来选择要求对其保存状态进行安全编码 TRUE从这个方法。 除非应用程序选择加入,否则它将继续允许非安全编码,这意味着进程注入可能仍然存在。

这确实突出了这些安全措施当前设计的一个问题:降级攻击。 应用程序的代码签名(以及因此的权利)将长期有效,如果应用程序降级,应用程序的 TCC 权限仍然有效。 非沙盒应用程序可以静默下载一个较旧的、易受攻击的应用程序版本并加以利用。 对于 SIP 绕过,这是行不通的,因为“macOS Update Assistant.app”不能在 macOS Monterey 上运行,因为某些私有框架不再包含必要的符号。 但这是一个巧合的修复,在许多其他情况下,较旧的应用程序可能仍然可以正常运行。 因此,只要与旧的 macOS 应用程序向后兼容,这个漏洞就会一直存在!

不过,如果您编写 Objective-C 应用程序,请确保添加 -applicationSupportsSecureRestorableState:返回 TRUE并为用于保存状态的所有类调整安全编码!

结论

在当前 macOS 的安全架构中,进程注入是一种强大的技术。 一个通用的进程注入漏洞可用于逃离沙箱、提升 root 权限以及绕过 SIP 的文件系统限制。 我们已经演示了如何在加载应用程序的保存状态时使用不安全的反序列化来注入任何 Cocoa 进程。 Apple 在 macOS Monterey 更新中解决了这个问题。


  1. 目前还不清楚这里的 AES 加密是为了增加什么安全性,因为密钥就存储在它旁边。 没有 MAC,因此没有对密文进行完整性检查。 ↩︎

原文地址

https://sector7.computest.nl/post/2022-08-process-injection-breaking-all-macos-security-layers-with-a-single-vulnerability/

标签:漏洞分享, 学习笔记