阅读:3622回复:0
cve-2014-7911安卓提权漏洞分析
◆0 简介
CVE-2014-7911是由Jann Horn发现的一个有关安卓的提权漏洞,该漏洞允许恶意应用从普通应用权限提权到system用户执行命令,漏洞信息与POC见(1]。漏洞的成因源于在安卓系统(>> system_server GetIntField(obj, gBinderProxyOffsets.mOrgue); LOGDEATH("Destroying BinderProxy %p: binder=%p drl=%pn", obj, b, drl); env->SetIntField(obj, gBinderProxyOffsets.mObject, 0); env->SetIntField(obj, gBinderProxyOffsets.mOrgue, 0); drl->decStrong((void*)javaObjectForIBinder); b->decStrong((void*)javaObjectForIBinder); IPCThreadState::self()->flushCommands();}最终native代码调用上述decStrong方法,从 #!javaDeathRecipientList* drl = (DeathRecipientList*) env->GetIntField(obj, gBinderProxyOffsets.mOrgue);这一行可以看出,drl就是mOrgue,可以被攻击者控制。 所以,drl->decStrong方法调用使用的this指针可由攻击者控制。 再看一下RefBase类中的decStrong方法 #!c++void RefBase::decStrong(const void* id) const{ weakref_impl* const refs = mRefs; refs->removeStrongRef(id); const int32_t c = android_atomic_dec(&refs->mStrong);#if PRINT_REFS ALOGD("decStrong of %p from %p: cnt=%dn", this, id, c);#endif ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); if (c == 1) { refs->mBase->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { delete this; } } refs->decWeak(id);}注意上述refs->mBase->onLastStrongRef(id)最终导致代码执行。 汇编代码分析: 下面看一下发生异常时最后调用的RefBase:decStrong的汇编代码。将libutils.so拖入IDA Pro,查看Android::RefBase::decStrong函数。分析时需要牢记的是,攻击者能够控制r0(this指针) 图片:2015062503005461992.png 首先对r0的使用,是在decStrong的前下面三行代码之中 #!c++weakref_impl* const refs = mRefs;refs->removeStrongRef(id);const int32_t c = android_atomic_dec(&refs->mStrong);对应的汇编代码如下 ldr r4, [r0, #4] # r0为this指针,r4为mRefsmov r6, r1mov r0, r4blx首先,mRefs被加载到r4。(r0是drl的this指针,mRefs是虚函数表之后的第一个私有变量,因此mRefs为r0+4所指向的内容) 然后,android_atomic_dec函数被调用,传入参数&refs->mStrong. #!c++const int32_t c = android_atomic_dec(&refs->mStrong);这被翻译为 #bashmov r0, r4 # r4指向mStrong,r0指向mStrongblx作为函数参数,上述r0就是&refs->mStrong。注意,mStrong是refs(类weakref_impl)的第一个成员变量,由于weakref_impl没有虚函数,所以没有虚函数表,因此mStrong就是r4所指向的内容。 另外,refs->removeStrongRef(id);这一行并没有出现在汇编代码中,因为这个函数为空实现,编译器进行了优化。如下所示。 #!c++void removeStrongRef(const void* /*id*/) { }在调用android_atomic_dec后,出现的是以下代码 #!c++if (c == 1) { refs->mBase->onLastStrongRef(id);对应的汇编代码 #!bashcmp r0, #1 # r0 = refs->mStrongbne.n d1ealdr r0, [r4, #8] # r4 = &refs->mStrongmov r1, r6ldr r3, [r0, #0]ldr r2, [r3, #12]blx r2注意,android_atomic_dec函数执行强引用计数减1,返回的是执行减1操作之前所指定的内存地址存放的值。为了调用refs->mBase->onLastStrongRef(id)(即:blx r2),攻击者需要使refs->mStrong为1. 至此,可以看出攻击者为了实现代码执行,需要满足如下约束条件: [*]drl(就是mOrgue,第一个可控的指针,在进入decStrong函数时的r0)必须指向可读的内存区域; [*]refs->mStrong必须为1; [*]refs->mBase->onLastStrongRef(id)需要执行成功。并最终指向可执行的内存区域。即满足 #!c++if(*(*(mOrgue+4))==1){ refs = *(mOrgue+4) r2 = *(*(*(refs+8))+12) blx r2 ----->获取控制权}除此以外,攻击者还必须克服Android中的漏洞缓解技术——ASLR和DEP。 ◆3漏洞利用 为了成功获得任意代码执行,攻击者可以使用堆喷射、堆栈转移(stack pivoting)和ROP等技术。受限于笔者目前水平,这里就不再分析了,具体分析和retme大牛的POC可参见[2,3,4]。 值得注意的是,尽管Android采用了ASLR机制,但为了获得正确的地址,可以基于这样一个事实:system_server和攻击者app都是从同一个进程-Zygote fork而来,具有相同的基址,攻击者通过分析自己进程的map文件(位于/proc//maps)就可以知道system_server以及所加载模块的内存布局。 另外一个有趣的话题是,洞主Jann Horn对PAN的分析进行了评论(2],认为不需要复杂的ROP gadgets就可以实现命令执行,而PAN对此进行了澄清。这从侧面反映了漏洞利用技术也是一门大的学问,有时即使漏洞发现者也未必能够真正理解漏洞利用的挑战。 最后Jann Horn谈到了发现此漏洞的灵感(5],源于他在大学时听到的一次讲座,涉及到某个PHP web应用在反序列化攻击者输入数据时出现的漏洞,这使他思考其他应用是否也有类似的问题。他知道Java的反序列化由ObjectInputStream处理不可信的输入数据,android也许忘了进行检查。是的,漏洞就在那里。 参考文献 (1] http://seclists.org/fulldisclosure/2014/Nov/51 (2] http://researchcenter.paloaltonetworks.com/2015/01/cve-2014-7911-deep-dive-analysis-android-system-service-vulnerability-exploitation (3] https://github.com/retme7/CVE-2014-7911_poc (4] https://github.com/retme7/My-Slides/blob/master/xKungfooSH%40retme.pdf (5] https://www.reddit.com/r/netsec/comments/2mr9cz/cve20147911_android_50_privilege_escalation_using/ (6]http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/os/IUserManager.java#IUserManager.Stub.Proxy.setApplicationRestrictions%28java.lang.String%2Candroid.os.Bundle%2Cint%29 |
|