Pwn Unlink堆攻击技术原理以及例题

2023-12-17 14:34:41

占位符

在这里插入图片描述

原理

通过构造fake chunk,触发unlink宏。我们可以获得任意地址写的能力。

源码

/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) {                                            \
    FD = P->fd;								      \
    BK = P->bk;								      \
    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;							      \
        if (!in_smallbin_range (P->size)				      \
            && __builtin_expect (P->fd_nextsize != NULL, 0)) {		      \
	    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;		      \
              }								      \
          }								      \
      }									      \
}

glibc 2.23中的unlink宏源码是这样的。
有个最重要条件是我们需要满足的:

if (!in_smallbin_range (P->size)				      \
            && __builtin_expect (P->fd_nextsize != NULL, 0)) {		      \
	    if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)	      \
		|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \

可以简化为这样:

P->fd->bk == P  // Chunk P的fd指针指向的chunk的bk指针是否指向P
P->bk->fd == P // Chunk P的bk指针指向的chunk的fd指针是否指向P

解析

光说可能不是很好理解,这里给出一个例子:
题:SUCTF_Unlink

addr                prev                size                 status              fd                bk                
0x23a6000           0x0                 0x30                 Freed                0x0              0x20
0x23a6030           0x20                0x90                 Used                None              None
0x23a60c0           0x0                 0x110                Used                None              None

现在有这么三个chunk,其中我们已经构造好了Unlink的Payload。

0x23a6000:	0x0000000000000000	0x0000000000000031
0x23a6010:	0x0000000000000000	0x0000000000000020 // fake chunk的size
0x23a6020:	0x00000000006020a8	0x00000000006020b0 // 0x00000000006020a8 fake chunk的fd, 0x00000000006020b0 fake chunk的bk
0x23a6030:	0x0000000000000020	0x0000000000000090 // fake chunk的size
0x23a6040:	0x000000000000000a	0x0000000000000000

根据P->fd->bk == P,也就是fake_chunk的fd指针指向的bk指针是否等于fake_chunk,我们直接查看这个fd指向了哪里。

0x6020a8 <completed>:	0x0000000000000000	0x0000000000000000
0x6020b8:				0x0000000000000000	0x00000000023a6010
0x6020c8 <buf+8>:		0x00000000023a6040	0x00000000023a60d0

乍一看可能有点一头雾水,那么如果我们把这段看成一个chunk结构体呢?

//						prev_size			size
0x6020a8 <completed>:	0x0000000000000000	0x0000000000000000
//						fd					bk
0x6020b8:				0x0000000000000000	0x00000000023a6010
//						data
0x6020c8 <buf+8>:		0x00000000023a6040	0x00000000023a60d0

现在就明朗起来了。P->fd->bk == P,P是fake_chunk,也就是0x00000000023a6010,fd是0x00000000006020a8 0x00000000006020a8的bk是0x00000000023a6010
因此表达式P->fd->bk == P成立。
我们再看P->bk->fd == Pfake_chunk的bk指针指向的fd指针的指向是否等于fake_chunk

0x6020b0:		0x0000000000000000	0x0000000000000000
0x6020c0 <buf>:	0x00000000023a6010	0x00000000023a6040

P是fake_chunk,0x00000000023a6010,bk是0x00000000006020b0 ,指向的fd是0x00000000023a6010,也就是我们的fake_chunk。

而这个fd和bk值是怎么算出来的呢?
这题的堆块指针位于bss段内,是0x6020C0,向前推0x18就是complete。
complete,或者说buf指向了0x00000000023a6010,这段恰好满足我们的unlink需求。
反之,另一个也是这个道理。因此我们只需要在chunk_ptr上减去0x180x10即可获得我们的fd和bk。

实际上 chunk_ptr - 0x18chunk_ptr - 0x10指向的都是同一个地方。

pwndbg> x/8gx 0x00000000006020C0 - 0x18
0x6020a8 <completed>:	0x0000000000000000	0x0000000000000000
0x6020b8:				0x0000000000000000	0x0000000000dc1010
0x6020c8 <buf+8>:		0x0000000000dc1040	0x0000000000dc10d0
0x6020d8 <buf+24>:		0x0000000000000000	0x0000000000000000

pwndbg> x/8gx 0x00000000006020C0 - 0x10
0x6020b0:				0x0000000000000000	0x0000000000000000
0x6020c0 <buf>:			0x0000000000dc1010	0x0000000000dc1040
0x6020d0 <buf+16>:		0x0000000000dc10d0	0x0000000000000000
0x6020e0 <buf+32>:		0x0000000000000000	0x0000000000000000

因此2个if条件都通过了,总结一下绕过方法是:

fake_chunk的fd指针就是 chunk_ptr - 0x18
fake_chunk的bk指针就是 chunk_ptr - 0x10

其他大佬的文章

一道题彻底理解 Pwn Heap Unlink

文章来源:https://blog.csdn.net/qq_29912475/article/details/135043502
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。