51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

multipart/form-data PHP和Java通用的WAF绕过方法

依旧是multipart/form-data,去年的时候说道了利用PHP的特性去绕过WAF.轻松绕各种WAF的POST注入、跨站防御(比如安全狗)
原文简单的描述了PHP在处理POST请求的时候会解析multipart/form-data的内容。
那么这个multipart/form-data到底是个啥呢?
1.png
1.png


大概长成上面这样.HTML代码就更加简单了:

<!DOCTYPE html> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<title>yzmm - p2j.cn</title> 
</head> 
<body> 
  <form action="http://192.168.199.151/index.php" method="POST" enctype="multipart/form-data"> 
       File:<input type="file" name="file" /><br/> 
        ID:<input type="text" name="id" value="select 1 from mysql.user--" style="width:250px;" / ><br/> 
        <input type="submit" value="提交" /> 
    </form> 
</body> 
</html>

这个特性其实并不只是PHP的专利,许多其他语言的MVC框架为了简化操作也有可能会做类似PHP FILES解析。虽说原生的JSP/Servlet是不支持解析multipart的.但在Java语言中当今最火的SpringMVC、Struts2都做了一样的事情。你可能经常会看到如下的Spring MVC代码:

import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestParam; 
import org.springframework.web.multipart.MultipartFile; 
@Controller
public class TestController {
@RequestMapping("/test1.aspx")
public void test1(HttpServletRequest request,HttpServletResponse response){
System.out.println("test1.aspx:"+request.getParameter("username"));
}
@RequestMapping("/test2.aspx")
public void test2(@RequestParam(value = "file", required = false) MultipartFile file,HttpServletRequest request,HttpServletResponse response){
System.out.println("test2.aspx:"+request.getParameter("username"));
System.out.println("文件名:"+file.getOriginalFilename());
}
}

然后是HttpClient客户端测试代码,用于发送HTTP Multipart测试请求:

import java.io.File; 
import java.io.IOException; 
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
public class MultipartTest {
public static void main(String[] args) {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost uploadFile = new HttpPost("http://localhost:8080/test/test1.aspx");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("username", "admin", ContentType.TEXT_PLAIN);
builder.addTextBody("password", "123456", ContentType.TEXT_PLAIN);
builder.addBinaryBody("file", new File("/Users/yz/Downloads/bd_logo1_31bdc765.png"), ContentType.APPLICATION_OCTET_STREAM, "pic");
HttpEntity multipart = builder.build();
uploadFile.setEntity(multipart);
try {
CloseableHttpResponse response = httpClient.execute(uploadFile);
HttpEntity responseEntity = response.getEntity();
System.err.println(IOUtils.toString(responseEntity.getContent()));
} catch (IOException e) {
e.printStackTrace();
}
}
}

pom 依赖的jar:

<dependency> 
      <groupId>org.apache.httpcomponents</groupId> 
      <artifactId>httpcore</artifactId> 
      <version>4.4.4</version> 
    </dependency> 
`&lt;dependency&gt; 
  &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt; 
  &lt;artifactId&gt;httpclient&lt;/artifactId&gt; 
  &lt;version&gt;4.5.1&lt;/version&gt; 
&lt;/dependency&gt; 

&lt;dependency&gt; &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt; &lt;artifactId&gt;httpasyncclient&lt;/artifactId&gt; &lt;version&gt;4.1.1&lt;/version&gt; &lt;/dependency&gt;

&lt;dependency&gt; &lt;groupId&gt;org.apache.httpcomponents&lt;/groupId&gt; &lt;artifactId&gt;httpmime&lt;/artifactId&gt; &lt;version&gt;4.5.1&lt;/version&gt; &lt;/dependency&gt;

&lt;dependency&gt; &lt;groupId&gt;commons-io&lt;/groupId&gt; &lt;artifactId&gt;commons-io&lt;/artifactId&gt; &lt;version&gt;2.4&lt;/version&gt; &lt;/dependency&gt; `


test1.do是一个最普通的Http请求Mapping,如果当前请求类型是一个multipart请求Spring MVC会将解析好的multipart放到request里面(其实是Spring MVC包装了一个HTTP请求,类名是:org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest)。于是我们在控制层就拿到了multipart里的username参数。

毫无疑问,使用MultipartTest的测试代码去请求test1.aspx会输出multipart内的username的值:admin.
2.png
2.png


Struts2实现方式和SpringMVC大同小异,同样的也自动的利用commons-fileupload做了HTTP解析。
3.png
3.png


import javax.servlet.http.HttpServletRequest; 
import org.apache.struts2.ServletActionContext; 
import com.opensymphony.xwork2.ActionSupport; 
public class Test extends ActionSupport {
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
System.out.println(request.getParameter("username"));
return "input";
}
}


那么为什么一个看似很简单的表单数据请求解析的功能会让很多的WAF蒙了呢?究其原因主要还是因为HTTP请求解析的复杂性和来自客户端的数据不确定性。因为上传一个几十M甚至更大的文件需求再平常不过了,如果WAF完整的去解析这个InputStream会消耗大量的服务器性能有点得不偿失。
另一个原因是由于实现HTTP请求的RFC的差异性导致次类请求解析得不一致或者解析错误的情况。因为multipart解析出问题的还不少。去年PHP和Apache Commons FileUpload 就出过DOS漏洞。
Apache Commons FileUpload 和 Apache Tomcat 拒绝服务

Multipart boundary 边界检查问题(分割线长度大于4091的)导致拒绝服务(死循环)

PHP曝DOS漏洞可致CPU灌满 涉及多个PHP版本

那么问题来了,各位同学的SQL注入和Struts2的命令执行漏洞真的修好了吗?看看loopx9牛的这个漏洞就知道了 WooYun: 百度某站st2命令执行(独特执行姿势)
赶紧回家修补丁吧。

赞(1)
未经允许不得转载:工具盒子 » multipart/form-data PHP和Java通用的WAF绕过方法