内存喷射在安卓Root利用中的应用 陈良@KeenTeam 关于我 • KeenTeam 高级研究员 • 主要研究漏洞利用技术: – 移动端提权、Root技术 – Safari, Chrome, Internet Explorer – 沙盒逃逸技术 议程 • 内存喷射技术介绍 • 内存喷射与安卓Root利用 • 案例分析: – 漏洞介绍 – 利用策略 内存喷射技术的历史 • 最早提出于2001年 • 被广泛应用于浏览器漏洞利用 – Heap Spraying • 系统内核提权(例如Windows) – Pool Spray 为什么要内存喷射 • 内存随机化机制的引入 – 栈、堆的随机 – ASLR的引入 • 大大降低了传统利用的成功率 • 内存喷射技术 – 通过特定软件的机制,分配大量内容可控内存 – 使特定位置填入预期的内容 安卓Root利用技术 • 漏洞方面 – 安卓/Linux核心漏洞较少出现,但三方驱动漏洞较多 – 直接实现任意地址写的漏洞较多(相对浏览器漏洞) • 利用方面 – 利用任意写漏洞改写syscall table(大多数品牌的安卓手机) – Patch setresuid – DKOM (修改task_struct-‐>cred) – 无ASLR – 许多机器无PXN(32位机型) 安卓Root与内存喷射 • 过去并不需要内存喷射技术 – 漏洞品质较好,任意内存读+写配合 – 可以写出稳定性很好的Root提权 • 近两年Android内核安全有所改进 – Kernel TEXT只读 (patch setresuid等方法无效) – Syscall table的地址无法轻易获取 – 容易利用的漏洞大幅减小 • 结果:内存喷射技术逐渐应用于安卓Root – 例如:Wen Xu@KeenTeam Blackhat USA 2015 议题<AH! UNIVERSAL ANDROID ROOTING IS BACK> 案例分析:mtk[漏洞 • 漏洞介绍 – 由nforest@KeenTeam发现,已于 2015年年初报给厂商 In mtk[.c – 适用所有mt658x & mt6592机型的 安卓手机 – 目前许多设备最新ROM上能触发 • 0day! – /dev/graphics/[0 • shell可打开 crw-‐rw-‐-‐-‐-‐ system graphics 29, 0 2015-‐07-‐09 15:08 [0 问题在哪里??? mtkb[漏洞细节 Cmd与arg通过ioctl调用传入 displayid是一个有符号整数 displayid的值可由用户态传入参数控制 当displayid是个负数的时候,即可绕过检查 越界写8字节的0 越界数据可传回用户态 mtkb[漏洞细节 • 数组index可为负数 • 偏移0x20与0x24的两个unsigned int被置为0 – DISP_GetPhysicalHeight() 与DISP_GetPhysicalWidth()并不能控制,只 能是0 • 其余Field内容可泄漏到用户态 – 泄漏的同时,副产品是其中8个连续字节被置为0 0x34大小的结构 问题一:可以写任意值? • 显然不是,只能连续8字节写0 – 如果是浏览器,存在许多利用方法和手段 • 利用Type Confusion或者某些结构的巧妙特性 • 例如: hhp://www.contexis.com/resources/blog/windows-‐miigaton-‐bypass/ – 安卓下结构体并没有浏览器丰富,存在这种特性 • thread_info-‐>addr_limit 是个比较好的选择 – Linux系统对每个task可以访问的内存范围是有限制的 – 对于32位安卓4+,thread_info-‐>addr_limit 为0xbf000000 addr_limit Internals 可置0,意为该task执行时,可被内核抢占 初始0xbf000000,置为0的意义? 不可置0,会直接内核panic 33位操作,置0意味着userland 可读写全地址,包括kernel! 结论:可选择写thread_info-‐>preempt_count和 addr_limit这8字节为0,实现kernel任意地址读写! 问题2:任意地址写? • thread_info以0x2000字节对齐 – 即(addr & 0x1fff) == 0 – 漏洞必须具备任意地址写0的能力,或者具备任意(addr & 0x1fff) == 4 的地址连续写8字节的能力 • 能以0x34字节为单位,写偏移0x20的位置的8字节? – 可写范围有限? – 需满足 (addr -‐ dispif_info) ≡ 0x20 mod 0x34 – 32位系统,addr + 0x1,0000,0000 == addr !! • 0x1,0000,0000 ≡ -‐4 mod 0x34 • 0x1,0000,0000 = 0x34 * 4EC4EC5 – 4 • 结论: – dispif_info[displayid].physicalHeight 如果写到地址A,那么写地址A+4只需dispif_info[displayed + 0x4EC4EC5].physicalHeight – 任意地址写! 初步利用策略 1. 泄漏dispif_info地址 2. 计算出所有displayid取值,displayid取值需满足: • displayid为负数 • (&(dispif_info[displayid].physicalHeight)) & 0x1fff == 4 (thread_info-‐>preempt_count位置) • (&(dispif_info[displayid].physicalHeight))地址在0xc8000000与0xefff0000之间 (通常此地址范围存放thread_info的可能 性较大) 3. 起尽可能多的线程 4. 使其处于睡眠状态 5. 随机选取步骤2中的displayid取值,并触发漏洞 • 有机会修改到thread_info-‐>addr_limit并置0 6. 唤醒所有线程,并试探这些线程是否可以读kernelland地址空间 7. 如果不可以,则重复4-‐6步骤,知道可以为止 8. 一旦可以,kernel任意内存读写即已实现,可很容易实现Root • 例如:修改task_struct-‐>cred 泄漏dispif_info地址 • 根据大量机型测试, dispif_info在0xc0000000-‐0xc3000000之间 • 可借助在用户态Map内存的方法,获取dispif_info地址: • 选取display_id为0xA04EC4EC,因为0xA04EC4EC*0x34 ==0x208FFFFFF0 == 0x8FFFFFF0,可确保写0操作 落在map的地址范围内。 display_id取值的计算 • 算法可以更优化,但不是重点 thread_info喷射 A.起线程,进行内存喷射 B. 进入睡眠 C. 触发漏洞 D. 唤醒线程 E. 试探是否Kernel内存可读 初步结果 • 大约1/5 Root成功率 • Panic几率很大 • 原因: – 很容易将系统重要数据结构破坏 改进思路 • 配合信息泄露漏洞 – 判断dispif_info[displayid].physicalWidth的原值,如为0xbf000000再触发内存写 – 可实现100%成功率的Root • mtk[漏洞的读内存特性回顾: – 可以实现任意地址读 – 每读一次带来写8字节的副产品 越界写8字节的0 越界数据可传回用户态 非完美读内存 mtk_dispif_info: thread_info: field … physicalWidth physicalHeight isConnected lcmOriginalWidth lcmOriginalHeight 写 0 写 0 读值 读值 读值 addr field 0xCFE34000 flags 0xCFE34004 preempt_count 0xCFE34008 addr_limit 0xCFE3400C task 0xCFE34010 exec_domain … 若读出isConnect值为0xbf000000,再将displayid值增加0x4EC4EC5,改addr_limit为0 结果:还是1/5 Root成功率
2015-《内存喷射在安卓Root利用中-陈良》
温馨提示:如果当前文档出现乱码或未能正常浏览,请先下载原文档进行浏览。
本文档由 张玉竹 于 2022-04-07 17:47:03上传分享