最新消息:图 床

HITB GSEC CTF WIN PWN解題全記錄之BABYSTACK

COOL IAM 353浏览 0评论

作者: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 出來。

我們想到的棧布局如下。


转载请注明:IAMCOOL » HITB GSEC CTF WIN PWN解題全記錄之BABYSTACK

0 0 vote
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x