前段时间,在对某站群系统进行代码审计时,发现某处DWR-AJAX接口存在文件上传漏洞
代码层:
public String upload(InputStream is, String fileName) {
String fileUploadPath = getFileUploadPath();
String file = String.valueOf(fileUploadPath) + "/" + IDCreator.getRandom() + "/" + fileName;
String fileExtName = FilenameUtils.getExtension(fileName);
File f = new File(file);
Properties pro = new Properties();
try {
long size = is.available();
FileUtils.copyInputStreamToFile(is, f);
pro.put("extName", fileExtName);
pro.put("originName", fileName);
pro.put("url", file);
pro.put("size", Long.valueOf(size));
} catch (IOException e) {
e.printStackTrace();
log.error("+ e);
return WebTools.getCallBackJSON(0, ");
}
return WebTools.getCallBackJSON(1, JSON.toJSONString(pro));
}
可以清楚的看到这里是一处文件上传漏洞,在进行存储的过程中,未进行任何文件类型效验操作。
由于是dwr框架,可以通过dwr.xml 查看映射关系
注:
在没有源码的情况下,可以访问dwr框架的DEBUG页面
默认路径:
http://localhost/dwr/index.html
有点类似于.NET平台下的ASMX接口文件,且里面提供了测试方法。
DWR框架所创建的ajax请求与常见的POST请求有所不同
DWR框架请求格式:
page 为来访页面
scriptSessionId 一般为自动生成或对应Cookie中的DWRSESSIONID
c0-scriptName 调用脚本名 对应 dwr.xml 中的 javascript
如: scriptName = javascript = ExtAjax
其中param为调用自定义的类
methodName 为类中的方法名
c0-param0为参数,如方法接收3个参数,那么就是0到2
如:c0-param0 ,c0-param1,c0-param2 按顺序传递
由于目标系统是文件上传,需要输入流InputStream,那么格式应该还会有所不同,所以这里我按照网上的相关方法,在本地搭建了一个环境
参考文章:https://my.oschina.net/u/1474779/blog/610668
所需jar包: (要实现文件上传必须引入commons-fileupload包)
commons-logging-1.1.1.jar
dwr.jar
commons-fileupload-1.3.1.jar
commons-io-2.4.jar
Upload.Java
package com.ajax;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
public class Upload {
public String upload(InputStream is, String fileName) throws IOException{
//dwr通过WebContext取得HttpServletRequest
WebContext wc = WebContextFactory.get();
HttpServletRequest req = wc.getHttpServletRequest();
String realpath = req.getSession().getServletContext().getRealPath("upload");
String fn = FilenameUtils.getName(fileName);
String filepath = realpath + "/" + fn;
FileUtils.copyInputStreamToFile(is, new File(filepath));//将输入流直接copy成文件
return filepath;
}
}
在web.xml中配置dwr
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>
org.directwebremoting.servlet.DwrServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
</web-app>
在dwr.xml 配置映射关系 com.ajax.Upload 为功能处理类所在的位置
<dwr>
<allow>
<create creator="new" javascript="Upload">
<param name="class" value="com.ajax.Upload" />
</create>
</allow>
</dwr>
创建jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>dwr上传文件</title>
</head>
<script type='text/javascript' src='dwr/engine.js'></script>
<script type='text/javascript' src='dwr/util.js'></script>
<script type="text/javascript" src="dwr/interface/Upload.js"></script>
<script type="text/javascript">
function upload(){
var file = dwr.util.getValue("myfile");
Upload.upload(file,file.value,function(data){
alert(data);
});
}
</script>
<body>
<input type="file" id="myfile"/>
<input type="button" value="上传" onclick="upload();"/>
</body>
</html>
这里需要注意:
dwr/interface/Upload.js
是dwr.xml中的映射关系,只要配置了映射关系,就会自动生成。无需创建
如果启动过程中,Tomcat出现报错,请讲当前目录PUT到ROOT目录
成功访问:
这里使用写好的JSP文件尝试上传文件。
用Burp开启抓包功能。
看起来没什么区别,就是转成了multipart/form-data。
那么要对目标系统上进行上传,只需更改scriptName和methodName即可
成功Getshell
在对某高校系统进行测试时,通过主页的HTMl源代码中发现了其系统使用DWR框架
尝试访问/dwr/index.html 获取接口目录
返回500,要么是dwr.xml配置错误,或者是做了防护手段,如果说配置错误的话,接口可能都用不了。这里还是选择了测一下。
由于/dwr/index.html无法访问,那么只能手动收集接口信息。
在各个HTML ,JSP页面上查找路径为
/dwr/interface/*.js
的js文件。此文件只有配置dwr.xml的映射才会生成,所以一般要调用ajax接口,就必须引入此js文件。
如下:
前面为scriptName,后面为methodName。p0,p1,p2 代表需要3个参数
将其各个接口进行了整合。然后提取methodName。尝试进行SQL注入。
这里需要注意:
如果返回结果提示
java.lang.Throwable
造成这个错误的原因有很多:
1.参数类型不对 2. 缺少必要参数 3. 接口自身问题
个人建议使用string: 数字
如下:
这里我构造请求方法,对每个接口都进行测试。
请求地址为:
http://localhost/dwr/call/plaincall/{{ scriptName }}.{{ methodName }}.dwr
正文:
callCount=1
page={{ 任意JSP地址均可 }}
httpSessionId= {{ 可留空 }}
scriptSessionId={{Cookie中的 DWRSESSION}}
c0-scriptName={{ scriptName }}
c0-methodName={{ methodName }}
c0-id=0
c0-param0=string(类型): 值 {{ 参数 1}}
batchId=0
最终在某个接口下面发现SQL注入:
单数单引号(错误)
双数双引号(正常)
由于服务器没有WAF,尝试使用SQLMAP.
注意需要 --level 3 不然跑不出来的