Hack team之后adobe和google合作对flash进行了大改,一度提高了flash的利用门槛,CVE-2015-7645作为第一个突破这些限制的漏洞利用方式,可以作为vetect利用方式之后的一个模板,应该是今年最后一篇技术文章了哈哈。
漏洞分析
POC由三个as文件superexternalizable,subexternalizable,externalizable组成。
该漏洞由IExternalizable导致,这个类导出两个函数readExternal和writeExternal,poc创建一个子类superexternalizable继承IExternalizable,并声明对应的readExternal和writeExternal函数。
创建subexternalizable类,该类继承superexternalizable,其中的红框中的triteExternal变量的赋值调用是造成漏洞的关键。
此时当subexternalizable或superexternalizable中存在同名的writeExternal的变量时会触发漏洞,编译的时候不要申明成writeExternal,因为默认编译器是不允许这么写的(野外的该漏洞exploit通过将该同名变量申明在namespace中绕过该编译器的检测)。
在externalizable类里,ByteArray对象在对subexternalizable类进行writeObject操作时(该处为一个反序列化操作,此时会去调用subexternalizable中的writeExternal函数),但是由于avm中的问题,对writeExternal函数的引用被混淆为对应变量writeExternal的引用,从而导致代码混淆可执行,该漏洞也是自adobe对flash中vector的length增加校验并配备隔离堆之后,首个突破次限制的漏洞。
编译好之后将其中的74改成77,即triteExternal->writeExternal.
漏洞触发时的地址如下,熟悉flash的同学可以发现此处实际上为一处对象中的虚函数调用。
下断点bp FlashPlayer+0063a67a,重启windbg之后断下,单步即可。
在avm中通过getproperty获取对应object的Property的类型,如下图所示,其中的getBinding根据属性名字返回对应的bindID,该id低三位表示对应的property类型,其余位表示对应的真正值。
下图为不同id值对应的property。
如下图漏洞触发后调试图所示,在进行sar eax,3这条指令时,此时eax为19a,该值即为对应的writeExternal的id,通过计算A=1010->010,此时可以看到为一个BKIND_VAR的类型,而不是我们需要的method类型。
之后去掉后三位,获取对应的取值,为0x33,通过该值获取method array中对应的writeExternal函数的地址。
之后的触发代码相当于执行call [eax+8],如下图此时由于0x33这个错误的id(注意此处的id是可控的,具体方式就是通过修改superexternalizable中变量的个数,每多一个变量增加一)导致计算的eax出错,call [eax+8]相当于call 0,从而报错。
接下来看看正常版本的情况,如下图所示将触发代码去掉后运行到漏洞触发点时,对应的eax为21。
后三位id标识为1,1=0001->001 BKIND_MOTHED类型,此时为正常的mothed类型。
此时对应的edx即为对应对象的vtable(虚函数列表对象,该对象偏移0x8的位置保存的methods数组保存了不同虚函数对象对应的MethodEnv,如eax所示,MethodEnv对象+0x8的位置保存了MethodInfo对象,MethodInfo偏移0x8的位置保存了该虚函数要调用的函数指针,通过该函数指针最终调用的writeExternal,具体如下图所示)。
对应Methods数组。
对应漏洞的源码如下,漏洞的问题主要在ClassInfo中,一开始获取对应wirteExternal的id时没有对该id的类型进行校验,此时攻击者通过特殊的构造可以导致获取的id值为对应的构造变量writeExternal的id。
之后A类对象在writeExternal函数的真实调用时,该id被用作索引,在MethodEnv中获取对应的函数指针,通过构造的writeExternal变量的id通常很大,从而导致vtable对象的越界,访问到相邻的vtable对象B中的方法中,如果此时B类的对象为攻击者可控,就会导致avm调用该B对象的方法,但是涉及到的内存操作却是A对象的内存,此时如果B类的内存空间远大于A类的话,B类函数的调用就有可能造成A内存操作的越界访问。
漏洞利用
整个漏洞利用思路如下:
- vtable维度上使subexternalizable类的vtable和可控MyExt类的vtable相邻
- 对象内存维度上使subexternalizable类的对象和MyBy(继承自ByteArray)对象相邻
- 触发漏洞修改MyBy中的length
Vtable布局
因此需要触发漏洞的A类(此处使用subexternalizable)的vtable对象和可控B类(此处使用MyExt3)的vtable对象在内存中相邻。如下图为对应的类MyExt3,在该类中包含了三个虚函数f1-f3。
下图为subexternalizable类对象和对应的vtable对象。
下图为MyExt3对象的vtable对象,明显处于低地址可以看到此时MyExt3的vtable对象要比subexternalizable对象的vtable小8个字节(/4=2),即少了两个虚函数,flash中的vtable是以对象的大小在内存中分配的,即大小相同的vtable的在内存中的相邻存储的。
superexternalizable类加对应继承类subexternalizable一共2+3=5个虚函数(要把sub中的三个算上),但是MyExt3中只有3个,因此需要在MyExt3中的增加两个虚函数。
补充之后重新编译,两个虚函数已经相邻了
修改后的代码,MyExt4-7分别继承MyExt3,并补充了增加的两个虚函数。
对应的MyExt4类,5-7类似。
此时再编译运行之后
Subexternalizable的vtable如下为044b6b20。
MyExt3对应的四个子类的对象分别如下,在044b6b60,044b6b80,044b6bc0,044b6be0四个地址
对应的sub myext myext三个vtable对象的内存情况如下,此时在漏洞触发代码调用即会索引044b6b20这个函数指针,如果id过大,就会访问到相邻044b6b80(即MyExt*对应的vtable中),由于id可控,即可控制调用任意的044b6b80中的函数,如下图所示origin函数指针为0455e20,混淆盗用的函数指针为0455fd00,即MyExt3中的f2函数,此时MyExt3类的大小结构就决定了之后如何覆盖相邻的对象。
堆fengshui
现在subexternalizable的vtable对象已经和MyExt3的vtable对象内存相邻了,接下来是让subexternalizable对象的内存和bytearray相邻(主要是vector在19.0.0.193之后就被增加了对应的安全机制),此处是通过将subexternalizable的vtable对象混淆成大空间MyExt3的vtable对象来实现对subexternalizable对象之后的bytearray对象的length修改,从而获取一个全内存读写的bytearray,下图中通过堆fengshui实现将bytearray对象稳定的分配到subexternalizable对象之后。
MyBy1为继承ByteArray之后的类,其中增加的变量主要用于后期的利用和定位。
此时的真实内存如下,可以看到相邻的subexternalizable和MyBy对象。
修改长度
此时vtable维度上subexternalizable的vtable和MyExt3的vtable内存相邻,对象维度上subexternalizable和MyBy内存相邻,混淆发生后,subexternalizable的vtable会被混淆为对应的MyExt3的vtable,即函数调用为MyExt3的vtable,而该函数的this指针却没变,因此此时该函数操作的内存空间是subexternalizable的内存,如果此时MyExt3内中的变量很多,对应函数操作的内存就能大到超过subexternalizable对象的内存空间,从而起到修改MyBy长度的作用。
如下图所示MyExt3中定义大量的uint变量,之后在混淆触发函数f2中对这些变量的操作实际上就已经超出了subexternalizable的内存范围,如下图中f2首先通过MyBy中的标记123,11223344判断位置,之后获取对应MyBy中的buf指针,并将其长度修改为0xFFFFFFF6.
如下图所示即为MyBy的判断指标及对应的bufaddr(通过该地址可以确认这个超长bytearray的位置)。
测试此时的ByteArray的length。
此时获取一个巨大的array。
此时对应的内存如下
此时该处为被修改的MyBy对象,由于在MyExt3中记录了偏移0x44的内容对应的地址,即0448c500的地址A(bufAddre),有了该地址通过减去偏移就可以算出该MyBy的地址04483ca0,及其中对应的a4(下图中0448f641),a5,a6的地址。
打印出的地址
通过该超长的ByteArray,可以访问内存任意地址,注意将infiniteBy设置成小端显示,由ByteArray的问题导致,设置position时需要减去287454020,这个地方导致MyBy的a0值需要被设置成了0。
通过调试确定虚函数及a4变量的地址分别在bufAddr地址-68,和+40的位置。
在externalizable中将trige设置为全局变量
通过设置a0,将position设置为0,这样的话读取的时候就不需要做-11223344的操作了,在GerAddr函数中将infiniteBy中的a4域作为对象容器,以后对于任意的对象只要将其赋值到infiniteBy.a4中,即可对该对象的地址进行读取,GetAddrV0用于读取对应的vector对象中的buf对象(shellcode会保存在该对象中)
此时拥有了全内存读写及对应的对象地址获取的能力之后,即可以对内存中的virtualprotect函数进行搜索,之后的代码可以借鉴HackTeam泄露出来的flash利用代码来实现。
首先按MZ搜索出对应pe文件。
搜索出对应的kernel32.dll
最后找到对应virtualprotect函数
如下图所示获取的virtualprotect函数地址。
CallVP调用virtualprotect将shellcode设置为可执行。
使用的Shellcode会保存在vector中。
获取vector中的content中的指针,即上图中对应的shellcode地址,其实就是获取vector对象偏移1c处的content指针(调试版本和正常版本有所区别),content指针偏移+8的位置即为对应的内容。
运行之后结果如下,此时的shellcode是不可执行的。
首先定义一个PayLoad函数,通过修改该函数的vtable函数实现virtualprotect函数的调用(即将其vtable函数替换为virtualprotect函数即可),如下图所示首先获取该函数对象的地址,之后分别保存对象指针,及vtable指针。
将vtable替换成virtualprotect函数,并设置对应参数(将对应的Payload vector中shellcode的地址,长度传入,可执行标志),调用virtualprotect,最后通过Set将修改的vtable修改回来。
运行之后可以发现此时shellcode获取了对应的执行权。
此时shellcode已经绕过dep,再次通过PayLoad将其vt函数修改为对应的shellcode地址
运行之后,熟悉的计算器。
转载请注明出处