1 分钟用 Java 原生层漏洞搞定 Win7+JRE7 一个漏洞, 三种利用
今天我们不会讲如何挖掘 Java 原生 层漏洞, 而是讲如何 料理 它们
自我介绍 趋势科技中国研发中心, 架构师 研究方向包括 漏洞挖掘, 沙盒技术, APT 攻击解决方案 重度动漫宅
议程 背景介绍 今天要使用的漏洞 Exploit 方法 1 Exploit 方法 2 Exploit 方法 3 总结
何为 Java 原生层漏洞? 存在于 Java 原生层代码中的漏洞 (C/C++ 代码 ) 栈溢出 堆溢出 数组越界读写 也被称为 Java 内存破坏漏洞
Java 原生层漏洞趋势
Java 原生层漏洞利用 JRE 6 没有 DEP, ASLR 找个小学生教会他 Heap Spray, 搞定 JRE 7 默认启用了 DEP, ASLR windows 7, windows 8 平台 额 看起来棘手多了? 其实也没那么难, 这就是我们今天的主题
议程 背景介绍 今天要使用的漏洞 Exploit 方法 1 Exploit 方法 2 Exploit 方法 3 总结
CVE-2013-1491 Joshua J. Drake (jduck) 在今年的 Pwn2013 上用它搞定了 JRE 7 + Windows8 (Accuvant Lab's White Paper) 我们在年初的 java 字体 Fuzz 过程中, 也发现了这个漏洞, 二月份得到的 crash, 四月份写了 Exploit, 然后四月份漏洞被补掉了
CFF 字体指令 CFF 字体, 也被称为 Type2 字体 字体文件中可以包含指令 ( 类似字节码 ), 可以动态执行用来完善字体形状 0A: call sub routine 0B: return from sub routine 0C 0A: add 0C 0B: sub 0C 0C: div 0C 0D: load private static native long stack
相关数据结构 TopDictInfo buildchararray 动态分配的数组 reg_weightvector 结构体中的静态数组
两条存在漏洞的指令 store [0, j, index, count] load [0, index, count] 在读写数组时没有检查是否越界!
通过这个漏洞我们可以 以一个有符号的 16 位数字作为下标, 任意读写 buildchararray 和 regweightvector 通过覆盖 buildchararray 指针, 我们可以实现任意地址读写 ( 而不仅仅局限于 16 位下标 )
实现任意地址读写 - 例子 0x2000000 0x20007b4 0x200087c T->topDictData buildchararray reg_weightvector 0x2100000 初始状态
第一步 put(0, 0x0c0c0c0c) buildchararray[0] = 0x0c0c0c0c; 0x2000000 0x20007b4 0x200087c T->topDictData buildchararray reg_weightvector 0x2100000 0c0c0c0c
第二步 store(0, -18, 0, 1) reg_weightvector[-18] = buildchararray[0]; 0x2000000 T->topDictData 0c0c0c0c 0x20007b4 0x200087c buildchararray reg_weightvector 0x2100000
第三步 put(0, 0x41414141) buildchararray[0] = 0x41414141; 0x2000000 0x20007b4 0x200087c T->topDictData buildchararray reg_weightvector 0x0c0c0c0c 41414141
议程 背景介绍 今天要使用的漏洞 Exploit 方法 1 Exploit 方法 2 Exploit 方法 3 总结
Information Leak + ROP
信息泄露 从结构体中读取某个函数指针 将函数指针地址减去一个预先计算好的偏移地址, 得到 t2k.dll 的基地址 从 t2k.dll 的导入表中获取其他 dll( 例如 msvcrt) 的函数地址, 进而得到该 dll 的地址
ROP struct TopDictInfo { tsimemobject *mem; } struct tsimemobject { jmp_buf env; } 1. 将 ROP gadgets 写入 buildchararray 2. 设置 jmp_buf->eip, 指向我们的第一条 ROP 指令 3. 设置 jmp_buf->esp 为 buildchararray 的地址 4. 触发程序内部异常, 引发 longjmp 的调用, 从而控制流程转向 jmp_buf->eip esp eip
议程 背景介绍 今天要使用的漏洞 Exploit 方法 1 Exploit 方法 2 Exploit 方法 3 总结
覆盖数组长度字段 + Statement
Java 数组内存布局 8 字节 4 字节 Object Head length a[0] a[1] a[n] 如果我们能够覆盖这 4 个字节的长度字段, 我们就可以越界读 / 写这个数组之后的内存数据
数组喷射
覆盖数组长度字段 将 buildchararray 指针设置为 0x23ad27d8 ( 在不同平台上该地址可能不一样, 取决于数组喷射的具体情况 ) 将 0x23ad27d8 处的四个字节覆盖为 0x7fffffff, 于是数组的新长度就变成了 0x7fffffff
覆盖 Statement 对象的 ACC 成员变量 Statement: 用来调用 Java 对象的某个方法, 有点像 eval AccessControlContext: Statement 的一个成员变量, 用来检查调用者所具有的权限
覆盖 Statement 对象的 ACC 成员变量 新建一个 statement 对象时,acc 成员代表了当前调用上下文的权限 如果在低权限的代码中创建了一个 statement, 那么 acc 成员也相应地是一个低权限的 ACC 我们的目标是在内存中将低权限的 acc 覆盖为一个高权限的 acc Statement Object memory layout Object Head acc target Powerful ACC
Method 2 Exploit Procedure 1. 分配数组 length 3. 覆盖数组长度 2. 紧跟在数组后面, 分配 Statement 对象 data statement acc 4. 覆盖 statement 对象的 acc 成员 Memory Space new length powerful acc
演示 利用数组长度覆盖 +Statement 来 Exploit CVE- 2013-1491
方法二 限制 你的漏洞必须能够修改 Java 对象堆中的内存 原生层代码默认使用这个堆 Java 原生堆 Java 对象 Java 数组 Java 对象堆 JVM
议程 背景介绍 今天要使用的漏洞 Exploit 方法 1 Exploit 方法 2 Exploit 方法 3 总结
JIT Spray
JIT Spray 历史 Dion Blazakis - interpreter exploitation: pointer inference and spraying Alexey Sintsov- Writing JIT shellcode for fun and profit TT Tsai - The Flash JIT Spraying is Back
JIT Spray 历史 主要集中在 flash 的漏洞利用 Java JIT 这块, 还没有实际可用的 POC 或相关研究
Java JIT 编译器 Java 编译器, 编译成字节码保存在 class 文件中 JIT 编译器, 将字节码编译成本地代码 ( 汇编代码 )
Java JIT 编译器 ( 继 ) 查看 JIT 生成的原生代码 -XX:+UnlockDiagnosticVMOptions - XX:+PrintAssembly JIT 编译阙值 只有当某个函数被调用的次数 > 阙值, 它才会被编译成原生代码 Java 客户端的默认阙值 : 1500
Java JIT 编译器对 XOR 的处理 public int spray(int a) { int b = a; b ^= 0x90909090; b ^= 0x90909090; b ^= 0x90909090; return b; } 0x01c21507: cmp 0x4(%ecx),%eax 0x01c2150a: jne 0x01bbd100 ; 0x01c21510: mov %eax,0xffffc000(%esp) 0x01c21517: push %ebp 0x01c21518: sub $0x18,%esp 0x01c2151b: xor $0x90909090,%edx 0x01c21521: xor $0x90909090,%edx 0x01c21527: xor $0x90909090,%edx 0x01c21539: ret
Java JIT 编译器对 XOR 的处理 ( 续 ) XOR 代码被编译成 6 个字节的指令 81 F2 90 90 90 3C xor edx, 0x3C909090 中间有 3 个字节, 我们可以用来写我们的 shellcode
让 EIP 跳到 XOR 指令中间 EIP $0: 81 F2 90 90 90 3C : xor edx, 0x3C909090 $6: 81 F2 90 90 90 3C : xor edx, 0x3C909090 $12: 81 F2 90 90 90 3C : xor edx, 0x3C909090 EIP $0: 81 F2 $2: 90 nop $3: 90 nop $4: 90 nop $5: 3C 81 cmp al, 81 $7: F2 repne $8: 90 nop $9: 90 nop $10: 90 nop $11: 3C 81 cmp al, 81
寻找一个稳定的 EIP 跳转地址 0x02cd70b7 在我们测试过的系统上, 相对比较稳定 : windows xp sp3, windows 7 home edition, windows 7 enterprise edition, windows 8 home edition
运行时 spray 多个 JIT 函数 ClassLoader.loadClass JIT00001.class JIT00002.class Exploit.class
性能 第一版 : Spray 2400 个函数, 用时 20 ~ 40 秒 原因是我们必须调用每个函数 1500 次, 才能让它被编译成原生代码 使用预热技术 : 时间减少为 7 ~ 9 秒
Shellcode Shellcode 分为 2 个阶段 Stage0: 通过 JIT Spray 进内存, 执行后会寻找下个阶段的 shellcode, 实现通用性 Stage1: 在 Java 源代码中定义, 完成真正的功能
演示 使用 JIT Spray 来 Exploit CVE-2013-1491
一分钟将 JIT Spray 加入你自己的 Java 原生层漏洞 POC 演示 给 CVE-2013-0809 POC 加入 JIT Spray 演讲结束后我们会公开所有相关的代码
可选演示 JRE 7 原生层 0day + Win8 + Java JIT Spray
Java JIT Spray 限制 目前只能在 32 位平台上工作 你的漏洞需要能够精确控制 EIP
议程 背景介绍 今天要使用的漏洞 Exploit 方法 1 Exploit 方法 2 Exploit 方法 3 总结
总结 我们今天介绍了三种 Java 原生层漏洞的 Exploit 方法, 都可以过 DEP+ASLR 实际操作的时候, 你需要选择适合你的漏洞特性的方式
总结 如果是 32 位系统, 并且你可以控制 EIP, 那就可以用 JIT Spray 如果你可以覆盖 Java 对象堆中的内容, 可以考虑 Array + Statement 如果你是 Vupen 的人, 那就用 Information Leak + ROP
Heapsprays 是给尔等屌丝用的 JIT sprays 也一样 "
Thank you! Q & A