从p0sixspwn源码看越狱流程、原理、目的

本文word版本:从p0sixspwn源码看越狱流程、原理、目的.docx.zip (182 KB)

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/disk0s1s2on /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"
    “\n”
    “<plist version=“1.0”>\n”
    “\n”
    " com.apple.syslogd\n"
    " \n"
    " Disabled\n"
    " \n"
    " \n"
    “\n”
    “\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:
    com.apple.mobilebackup2

    InstanceLimit
    5
    Label
    com.apple.mobilebackup2
    ProgramArguments

    /usr/libexec/BackupAgent2
    –lockdown


    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/disk1on /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轮来尝试这个碰撞。
    我印象中racecondition都很短,有的甚至用FPGA之类的硬件来实现,例如xbox360。估计现在的版本apple不会再留这么多的时间间隔了吧…

这个hax.dmghfs文件系统镜像里面只有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:

<?xmlversion="1.0" encoding="UTF-8"?>

<plistversion=“1.0”>

Label
r
ProgramArguments

/sbin/mount
-u
-o
rw,suid,dev
/


com.apple.afc2.plist:

<?xmlversion="1.0" encoding="UTF-8"?>

<plistversion=“1.0”>

Label
com.apple.afc2
ProgramArguments

/usr/libexec/afcd
–lockdown
-S
-d
/



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 -orw,suid,dev /
load/System/Library/LaunchDaemons/com.apple.MobileFileIntegrity.plist
setenv DYLD_INSERT_LIBRARIES/private/var/untether/_.dylib
bsexec …/private/var/untether/untether
unsetenvDYLD_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 fromCoreFoundation)
[re-export]_kMISValidationOptionExpectedHash (_kCFUserNotificationTimeoutKey fromCoreFoundation)
[re-export]_MISValidateSignature (_CFEqual from CoreFoundation)
参考CoreFoundation的源码:
www.opensource.apple.com/tarballs/CF/CF-855.17.tar.gz
BooleanCFEqual(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_spawnkernel information leak (by i0n1c)
posix_spawnkernel exploit (CVE-2013-3954) (by i0n1c)

最后做下小结
越狱的目的是什么?或者说越狱对iOS设备造成了什么影响?
目的就是突破iOS上的限制。

  1. Remount rootfs获取写权限
  2. 启动afc2访问所有文件
  3. Patch内核,去除执行文件完整性检查机制(unsigned code to run)、可写的内存区域没有执行权限(MobileSubstrate可以工作)、…
  4. 安装Cydia(Appstore不是唯一来源)
1 个赞