-
-
[原创]从p0sixspwn源码看越狱流程、原理、目的
-
发表于:
2014-10-31 13:00
25406
-
[原创]从p0sixspwn源码看越狱流程、原理、目的
本文word版本: 从p0sixspwn源码看越狱流程、原理、目的.docx.7z
p0sixspwn的源码在:
e37K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6H3x3s2y4A6P5s2y4H3N6$3&6Q4x3V1k6H3x3s2y4A6P5s2y4H3N6$3^5`.
这个工具可以实现iOS6.1.3~6.1.6的越狱。
本文的目的在于针对p0sixspwn源码,分析越狱操作的流程、然后理解原理,最终总结出通用的东西例如目的、理念等。
流程和原理:
1. 首先检查iOS设备是可以越狱。
检查产品类型,固件版本。不支持,结束。
2. 检查iOS设备是否已经越狱。
检查是否可以启动afc2、检查是否/Applications目录是一个符号连接、检查是否存在/private/etc/launchd.conf文件
已越狱,结束。
Note1:越狱前/Applications目录是一个正常目录,当Cydia第一次运行完之后就成了符号链接。
Applications -> /var/stash/_.k0EtTL/Applications/
这是因为/Applications目录下的文件被移动到了用户分区下,这样做是为了给系统分区节省空间,来放别的东东。
参考:c4aK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4c8Z5k6h3W2H3K9r3!0F1k6i4N6A6K9$3W2Q4x3X3g2U0L8$3#2Q4x3V1k6%4K9h3E0A6i4K6u0r3i4K6u0r3M7s2u0A6N6X3q4@1k6g2)9J5c8Y4k6S2M7W2)9J5c8Y4y4@1j5i4y4Z5
Note2:launchd.conf是越狱后写入的,目的是让iOS版本的init程序(/sbin/launchd)来加载执行内核内存补丁程序:/private/var/untether/untether,该程序主要目的是去掉内核对执行文件的完整性检查(Apple Mobile File Integrity),可写的内存区域就不会有执行权限,等限制。
这里要注意的是用户分区是挂接在/private/var目录下的:
/dev/disk0s1s2 on /private/var
在没有越狱的情况下/etc目录也是一个符号连接到/private/etc
/etc -> /private/etc/
所以/private/etc/launchd.conf就是/etc/launchd.conf,但这个文件又是只读的,因为他属于root分区,fstab定义root分区是只读的:
/dev/disk0s1s1 / hfs ro 0 1
Note3:检查有没有越狱只要看能不能开启afc2就够了。至于Cydia有没有运行过,或者untether程序有没有放置只是辅助。
3. 越狱的第一步,利用com.apple.mobilebackup2漏洞,创建文件:/var/db/launchd.db/com.apple.launchd/overrides.plist,内容如下:
char* overrides_plist = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"47cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3q4H3M7r3I4W2i4K6u0W2j5$3!0E0i4K6u0r3c8q4c8p5M7#2)9J5c8W2m8J5L8%4m8W2M7Y4c8&6e0r3W2K6N6q4)9J5k6o6q4Q4x3X3f1H3i4K6u0W2k6s2c8V1i4K6g2o6">\n"
"<plist version=\"1.0\">\n"
"<dict>\n"
" <key>com.apple.syslogd</key>\n"
" <dict>\n"
" <key>Disabled</key>\n"
" <true/>\n"
" </dict>\n"
"</dict>\n"
"</plist>\n";
字面上看是要关闭syslog服务。
Note1:com.apple.mobilebackup2漏洞利用的方法类似于在游戏上经常使用的存档漏洞。
流程是:首先生成备份。在PC端对备份文件进行修改。通过恢复修改后的备份文件触发漏洞。
这个漏洞的原理并不是堆栈溢出,而是恢复备份时对存在的符号链接的情况没有做安全过滤。
例如创建/var/db/launchd.db/com.apple.launchd/overrides.plist的流程:
a. Create backup
b. backup_mkdir("Media/Recordings")
c. backup_symlink("Media/Recordings/.haxx", "/var/db/launchd.db/com.apple.launchd") 也就是/var/db/launchd.db/com.apple.launchd -> Media/Recordings/.haxx
d. backup_add_file("Media/Recordings/.haxx/overrides.plist")
e. restore backup
Note2:使用afc对文件操作的范围仅限于:/private/var/mobile/Media,而且只能是普通用户(501、mobile)的权限
/usr/libexec/afcd --xpc -d /private/var/mobile/Media
com.apple.mobilebackup2程序,也就是/usr/libexec/BackupAgent2,虽然是root权限,但也不能写只读的分区,例如/private/etc/launchd.conf。
/System/Library/Lockdown/Services.plist:
<key>com.apple.mobilebackup2</key>
<dict>
<key>InstanceLimit</key>
<integer>5</integer>
<key>Label</key>
<string>com.apple.mobilebackup2</string>
<key>ProgramArguments</key>
<array>
<string>/usr/libexec/BackupAgent2</string>
<string>--lockdown</string>
</array>
</dict>
Note3:我不明白关闭com.apple.syslogd有什么实质意义。
4. 越狱的第二步,利用漏洞DeveloperDiskImage race condition
先交代背景:
把iPhone同你的Mac连上, 打开Xcode, 点击 “User For Development”, 一个磁盘映像就会上传到你的iPhone中。它就是所谓的 DevelopeerDiskImage.dmg
这个DMG可以在你的Mac机器上找到, 比如
/Developer/Platforms/iPhoneOS.platform/DeviceSupport/3.1.3/DeveloperDiskImage.dmg
通过SSH登录到iPhone上,运行mount命令
#mount
/dev/disk1 on /Developer (hfs, local, read-only) Note1:这个/Developer目录是iOS系统原始情况下就有的
扩展应用就是:可以挂接任何dmg到这个/Developer目录,只要同时具有这个dmg文件的有效签名。
例如:0dcK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3q4H3M7r3I4V1L8X3I4V1i4K6u0W2j5i4m8H3L8r3g2Q4x3X3g2U0L8$3#2Q4x3V1k6A6e0#2x3$3i4K6u0r3x3o6b7I4i4K6u0V1z5o6f1I4z5q4)9J5k6e0t1H3x3e0t1I4x3o6t1&6i4K6u0W2b7@1y4J5N6o6W2Q4x3V1k6A6e0#2y4g2M7r3c8S2N6r3g2J5i4K6u0W2K9i4m8S2
解开获取:iOSUpdaterHelper.dmg、iOSUpdaterHelper.dmg.signature
运行下面命令同样可以完成挂接:
$ ideviceimagemounter.exe -t Developer ./iOSUpdaterHelper.dmg
Uploading ./iOSUpdaterHelper.dmg --> afc:///PublicStaging/staging.dimage
done.
Mounting...
Done.
Status: Complete
这个race condition是说,按正常流程将dmg和其签名,通过lockdown告诉了com.apple.mobile.mobile_image_mounter之后的某个时刻,如果用另外一个dmg的内容替换了afc:///PublicStaging/staging.dimage,会怎么样?
结果是,如果这个时间点拿捏的比较好的话,另一个dmg(hax.dmg)会取代iOSUpdaterHelper.dmg被挂接到/Developer目录!
而这个时间点就是com.apple.mobile.mobile_image_mounter完成了dmg完整性校验,但还没有开始挂接dmg文件。
从程序上看,这个时间点是1000 ~ 3900(1000+29*100)us,程序会以100us为步长,通过29轮来尝试这个碰撞。
我印象中race condition都很短,有的甚至用FPGA之类的硬件来实现,例如xbox360。估计现在的版本apple不会再留这么多的时间间隔了吧...
这个hax.dmg hfs文件系统镜像里面只有2个plist文件:

貌似/usr/libexec/lockdownd程序非常关注这个"/Developer/Library/Lockdown/ServiceAgents"目录。

5. 越狱的第三步,通过lockdown运行r.plist和com.apple.afc2.plist
简单说就是通过root权限执行下面两条命令,第一条remount让root分区具有写权限(android的root,:-)),第二条启动熟悉的afc2服务
/sbin/mount –u –o rw,suid,dev /
/usr/libexec/afcd --lockdown -d /
r.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "a0cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3q4H3M7r3I4W2i4K6u0W2j5$3!0E0i4K6u0r3c8q4c8p5M7#2)9J5c8W2m8J5L8%4m8W2M7Y4c8&6e0r3W2K6N6q4)9J5k6o6q4Q4x3X3f1H3i4K6u0W2k6s2c8V1">
<plist version="1.0">
<dict>
<key>Label</key>
<string>r</string>
<key>ProgramArguments</key>
<array>
<string>/sbin/mount</string>
<string>-u</string>
<string>-o</string>
<string>rw,suid,dev</string>
<string>/</string>
</array>
</dict>
</plist>
com.apple.afc2.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "706K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3q4H3M7r3I4W2i4K6u0W2j5$3!0E0i4K6u0r3c8q4c8p5M7#2)9J5c8W2m8J5L8%4m8W2M7Y4c8&6e0r3W2K6N6q4)9J5k6o6q4Q4x3X3f1H3i4K6u0W2k6s2c8V1">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.apple.afc2</string>
<key>ProgramArguments</key>
<array>
<string>/usr/libexec/afcd</string>
<string>--lockdown</string>
<string>-S</string>
<string>-d</string>
<string>/</string>
</array>
</dict>
</plist>
6. 越狱的第四步,类似于第一步继续使用com.apple.mobilebackup2漏洞上传untether payload和Cydia安装包(当然afc2都起来,没有必要一定需要利用这个漏洞了)
例如/etc/launchd.conf、/private/var/untether/_.dylib、/private/var/untether/untether
# cat /etc/launchd.conf
unload /System/Library/LaunchDaemons/com.apple.MobileFileIntegrity.plist
bsexec .. /sbin/mount -u -o rw,suid,dev /
load /System/Library/LaunchDaemons/com.apple.MobileFileIntegrity.plist
setenv DYLD_INSERT_LIBRARIES /private/var/untether/_.dylib
bsexec .. /private/var/untether/untether
unsetenv DYLD_INSERT_LIBRARIES
bsexec .. /bin/rm -f /var/untether/sock
bsexec .. /bin/ln -f /var/tmp/launchd/sock /var/untether/sock
目的是每次系统启动后,都可以通过/sbin/launchd来执行上面这些命令。
7. 到此越狱结束。
其实还要再说几句,因为还有几个漏洞的利用没有登场。
第一个是AMFID_code_signing_evasi0n7
untether本身的目的是在内存上patch掉内核的限制。但_.dylib和untether都是没有签名的程序,在没有被patch之前又如何运行呢?
_.dylib里_TEXT是空的,他也就没有签名,他存在的真实目的告诉动态库加载器dyld,用CoreFoundation里的_CFEqual函数来替换掉libmis.dylib里的_MISValidateSignature
我理解re-export只是一个特征,基于这个特征的确是比较危险,算他是漏洞吧。
$ dyldinfo -export _.dylib
export information (from trie):
[re-export] _kMISValidationOptionValidateSignatureOnly (_kCFUserNotificationTokenKey from CoreFoundation)
[re-export] _kMISValidationOptionExpectedHash (_kCFUserNotificationTimeoutKey from CoreFoundation)
[re-export] _MISValidateSignature (_CFEqual from CoreFoundation)
参考CoreFoundation的源码:
845K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3!0H3k6h3&6K6L8%4g2J5j5$3g2Q4x3X3g2S2M7s2m8D9k6g2)9J5k6h3y4G2L8g2)9J5c8Y4c8S2M7X3u0S2L8r3I4K6i4K6u0r3b7@1k6Q4x3V1k6o6c8W2)9J5k6o6R3#2y4g2)9J5k6e0p5%4i4K6u0W2N6r3q4J5i4K6u0W2k6%4Z5`.
Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) {
if (NULL == cf1) { CRSetCrashLogMessage("*** CFEqual() called with NULL first argument ***"); HALT; }
if (NULL == cf2) { CRSetCrashLogMessage("*** CFEqual() called with NULL second argument ***"); HALT; }
if (cf1 == cf2) return true;
CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf1, isEqual:, cf2);
CFTYPE_OBJC_FUNCDISPATCH1(Boolean, cf2, isEqual:, cf1);
__CFGenericAssertIsCF(cf1);
__CFGenericAssertIsCF(cf2);
if (__CFGenericTypeID_inline(cf1) != __CFGenericTypeID_inline(cf2)) return false;
if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal) {
return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf1)]->equal(cf1, cf2);
}
return false;
}
libmis.dylib里的_MISValidateSignature是这样的:
int __fastcall MISValidateSignature(int a1, int a2)
{
return MISValidateSignatureAndCopyInfo(a1, a2, 0);
}
因为a1和a2是不同的东西,所以CFEqual一定返回false也就是0。对于MISValidateSignature,0就是校验通过!
虽然
setenv DYLD_INSERT_LIBRARIES /private/var/untether/_.dylib
bsexec .. /private/var/untether/untether
这两句看样子是说在untether执行的时候做这个符号替换,但实际这个_.dylib真不是为他准备的。untether根本就不会去调用MISValidateSignature函数!
事实上_.dylib是给amfid这个程序准备的。因为内核在加载untether做校验时,启动了用户态程序amfid来判断是否合法,不知道apple为什么这样设计,但事实就是这样。
这样amfid总会告诉内核,他在加载的程序是合法的,于是untether就运行了。
这里摘录《D2T1 - Pod2g, Planetbeing, Musclenerd and Pimskeks aka Evad3rs - Swiping Through Modern Security Features.pdf》说明amfid的调用关系:

Note: lauchd加载untether程序流程
1. lauchd调用fork(spawn)
2. 在fork的子进程里调用execv
3. 内核装载器调用amfid检查untether有效性,通过后进行装载
4. 内核装载器发现untether是要用到动态库的,就顺便装载dyld,把返回用户态后的fork出来的进程的pc指向dyld的入口函数
5. dyld完成剩下dylib的加载(包括执行dylib里的init函数),最后执行untether的入口函数。
剩下的漏洞利用在于untether执行起来之后如何去找到对应的指令进行patch,据说因为ASLR的存在,这个事情很难办到。
目前还没有看到这部分,无法确认细节,据说是下面这两个:
posix_spawn kernel information leak (by i0n1c)
posix_spawn kernel exploit (CVE-2013-3954) (by i0n1c)
最后做下小结
越狱的目的是什么?或者说越狱对iOS设备造成了什么影响?
目的就是突破iOS上的限制。
1. Remount rootfs获取写权限
2. 启动afc2访问所有文件
3. Patch内核,去除执行文件完整性检查机制(unsigned code to run)、可写的内存区域没有执行权限(MobileSubstrate可以工作)、…
4. 安装Cydia(Appstore不是唯一来源)
[培训]科锐逆向工程师培训第53期2025年7月8日开班!