-
-
[原创]从p0sixspwn源码看越狱流程、原理、目的
-
发表于:
2014-10-31 13:00
25022
-
[原创]从p0sixspwn源码看越狱流程、原理、目的
本文word版本: 从p0sixspwn源码看越狱流程、原理、目的.docx.7z
p0sixspwn的源码在:
https://github.com/p0sixspwn/p0sixspwn
这个工具可以实现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目录下的文件被移动到了用户分区下,这样做是为了给系统分区节省空间,来放别的东东。
参考:http://theiphonewiki.com/wiki//private/var/stash
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\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\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文件的有效签名。
例如:http://appldnld.apple.com/iOS6/041-8518.20121029.CCrt9/iOSUpdater.ipa
解开获取: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" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<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" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<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的源码:
www.opensource.apple.com/tarballs/CF/CF-855.17.tar.gz
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不是唯一来源)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)