示例 - 存在SQL注入漏洞的代码示例(Multipart传参方式):

    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. <%@ page import="org.apache.commons.fileupload.FileUploadException" %>
    9. <%@ page import="java.util.Map" %>
    10. <%@ page import="java.util.HashMap" %>
    11. <%@ page import="java.sql.DriverManager" %>
    12. <%@ page import="com.alibaba.fastjson.JSON" %>
    13. <%@ page import="java.sql.Connection" %>
    14. <%@ page import="java.sql.ResultSet" %>
    15. <%@ page import="java.io.IOException" %>
    16. <%
    17. // MYSQL sys_user示例表,测试时请先创建对应的数据库和表
    18. //
    19. // CREATE TABLE `sys_user` (
    20. // `id` int(9) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
    21. // `username` varchar(16) NOT NULL COMMENT '用户名',
    22. // `password` varchar(32) NOT NULL COMMENT '用户密码',
    23. // `user_avatar` varchar(255) DEFAULT NULL COMMENT '用户头像',
    24. // `register_time` datetime DEFAULT NULL COMMENT '注册时间',
    25. // PRIMARY KEY (`id`),
    26. // UNIQUE KEY `idx_sys_user_username` (`username`) USING BTREE
    27. // ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='系统用户表'
    28. //
    29. // INSERT INTO `sys_user` VALUES ('1', 'admin', '123456', '/res/images/avatar/default.png', '2020-05-05 17:21:27'), ('2', 'test', '123456', '/res/images/avatar/default.png', '2020-05-06 18:27:10'), ('3', 'root', '123456', '/res/images/avatar/default.png', '2020-05-06 18:28:27'), ('4', 'user', '123456', '/res/images/avatar/default.png', '2020-05-06 18:31:34'), ('5', 'rasp', '123456', '/res/images/avatar/default.png', '2020-05-06 18:32:08');
    30. %>
    31. <%!
    32. /**
    33. * 解析Multipart请求中的参数
    34. * @param request
    35. * @return
    36. */
    37. Map<String, String> parseMultipartContent(HttpServletRequest request) {
    38. Map<String, String> dataMap = new HashMap<String, String>();
    39. try {
    40. ServletFileUpload fileUpload = new ServletFileUpload();
    41. FileItemIterator fileItemIterator = fileUpload.getItemIterator(request);
    42. while (fileItemIterator.hasNext()) {
    43. FileItemStream fileItemStream = fileItemIterator.next();
    44. if (fileItemStream.isFormField()) {
    45. // 字段名称
    46. String fieldName = fileItemStream.getFieldName();
    47. // 字段值
    48. String fieldValue = Streams.asString(fileItemStream.openStream());
    49. dataMap.put(fieldName, fieldValue);
    50. }
    51. }
    52. } catch (Exception e) {
    53. e.printStackTrace();
    54. }
    55. return dataMap;
    56. }
    57. %>
    58. <%
    59. if (ServletFileUpload.isMultipartContent(request)) {
    60. Map<String, String> dataMap = parseMultipartContent(request);
    61. String username = dataMap.get("username");
    62. String password = dataMap.get("password");
    63. // 处理用户登陆逻辑
    64. if (username != null && password != null) {
    65. ResultSet rs = null;
    66. Connection connection = null;
    67. Map<String, Object> userInfo = new HashMap<String, Object>();
    68. try {
    69. Class.forName("com.mysql.jdbc.Driver");
    70. connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javaweb-bbs", "root", "root");
    71. String sql = "select * from sys_user where username = '" + username + "' and password = '" + password + "'";
    72. System.out.println(sql);
    73. rs = connection.prepareStatement(sql).executeQuery();
    74. while (rs.next()) {
    75. userInfo.put("id", rs.getString("id"));
    76. userInfo.put("username", rs.getString("username"));
    77. userInfo.put("password", rs.getString("password"));
    78. userInfo.put("user_avatar", rs.getString("user_avatar"));
    79. userInfo.put("register_time", rs.getDate("register_time"));
    80. }
    81. // 检查是否登陆成功
    82. if (userInfo.size() > 0) {
    83. // 设置用户登陆信息
    84. out.println(JSON.toJSONString(userInfo));
    85. } else {
    86. out.println("<script>alert('登陆失败,账号或密码错误!');history.back(-1)</script>");
    87. }
    88. } catch (Exception e) {
    89. e.printStackTrace();
    90. out.println("<script>alert('登陆失败,服务器异常!');history.back(-1)</script>");
    91. } finally {
    92. // 关闭数据库连接
    93. if (rs != null)
    94. rs.close();
    95. if (connection != null)
    96. connection.close();
    97. }
    98. }
    99. } else {
    100. %>
    101. <!DOCTYPE html>
    102. <html lang="en">
    103. <head>
    104. <meta charset="UTF-8">
    105. <title>File upload</title>
    106. </head>
    107. <body>
    108. <form action="" enctype="multipart/form-data" method="post">
    109. <p>
    110. Username: <input name="username" type="text" value="admin" /><br/>
    111. Password: <input name="password" type="text" value="'=0#" />
    112. </p>
    113. <input name="submit" type="submit" value="Submit"/>
    114. </form>
    115. </body>
    116. </html>
    117. <%
    118. }
    119. %>

    访问示例中的后台登陆地址:http://localhost:8000/modules/jdbc/multipart.jsp,如下图:
    5. 4. SQL注入 - Multipart传参测试 - 图1
    提交万能密码'=0#即可绕过登陆验证获取到admin用户信息:
    5. 4. SQL注入 - Multipart传参测试 - 图2
    Spring MVC Multipart请求解析示例

    1. import org.springframework.jdbc.core.JdbcTemplate;
    2. import org.springframework.stereotype.Controller;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. import javax.annotation.Resource;
    5. import javax.servlet.http.HttpServletRequest;
    6. import javax.servlet.http.HttpServletResponse;
    7. import javax.servlet.http.HttpSession;
    8. import java.io.IOException;
    9. import java.io.PrintWriter;
    10. import java.util.HashMap;
    11. import java.util.Map;
    12. import static org.javaweb.utils.HttpServletResponseUtils.responseHTML;
    13. /**
    14. * Creator: yz
    15. * Date: 2020-05-04
    16. */
    17. @Controller
    18. @RequestMapping("/SQLInjection/")
    19. public class SQLInjectionController {
    20. @Resource
    21. private JdbcTemplate jdbcTemplate;
    22. @RequestMapping("/Login.php")
    23. public void login(String username, String password, String action, String formType,
    24. HttpServletRequest request, HttpServletResponse response,
    25. HttpSession session) throws IOException {
    26. String contentType = request.getContentType();
    27. String sessionKey = "USER_INFO";
    28. Object sessionUser = session.getAttribute(sessionKey);
    29. PrintWriter out = response.getWriter();
    30. // 退出登陆
    31. if (sessionUser != null && "exit".equals(action)) {
    32. session.removeAttribute(sessionKey);
    33. response.sendRedirect(request.getServletPath() + (formType != null ? "?formType=" + formType : ""));
    34. return;
    35. }
    36. Map<String, Object> userInfo = null;
    37. // 检查用户是否已经登陆成功
    38. if (sessionUser instanceof Map) {
    39. userInfo = (Map<String, Object>) sessionUser;
    40. responseHTML(response,
    41. "<p>欢迎回来:" + userInfo.get("username") + ",ID:" +
    42. userInfo.get("id") + " \r<a href='?action=exit" + (formType != null ? "&formType=" + formType : "") + "'>退出登陆</a></p>"
    43. );
    44. return;
    45. }
    46. // 处理用户登陆逻辑
    47. if (username != null && password != null) {
    48. userInfo = new HashMap<String, Object>();
    49. try {
    50. String sql = "select id,username,password from sys_user where username = '" +
    51. username + "' and password = '" + password + "'";
    52. System.out.println(sql);
    53. userInfo = jdbcTemplate.queryForMap(sql);
    54. // 检查是否登陆成功
    55. if (userInfo.size() > 0) {
    56. // 设置用户登陆信息
    57. session.setAttribute(sessionKey, userInfo);
    58. String q = request.getQueryString();
    59. // 跳转到登陆成功页面
    60. response.sendRedirect(request.getServletPath() + (q != null ? "?" + q : ""));
    61. } else {
    62. responseHTML(response, "<script>alert('登陆失败,账号或密码错误!');history.back(-1)</script>");
    63. }
    64. } catch (Exception e) {
    65. responseHTML(response, "<script>alert('登陆失败,服务器异常!');history.back(-1)</script>");
    66. }
    67. return;
    68. }
    69. String multipartReq = "";
    70. // 如果传入formType=multipart参数就输出multipart表单,否则输出普通的表单
    71. if ("multipart".equals(formType)) {
    72. multipartReq = " enctype=\"multipart/form-data\" ";
    73. }
    74. responseHTML(response, "<html>\n" +
    75. "<head>\n" +
    76. " <title>Login Test</title>\n" +
    77. "</head>\n" +
    78. "<body>\n" +
    79. "<div style=\"margin: 30px;\">\n" +
    80. " <form action=\"#\" " + multipartReq + " method=\"POST\">\n" +
    81. " Username:<input type=\"text\" name=\"username\" value=\"admin\"/><br/>\n" +
    82. " Password:<input type=\"text\" name=\"password\" value=\"'=0#\"/><br/>\n" +
    83. " <input type=\"submit\" value=\"登陆\"/>\n" +
    84. " </form>\n" +
    85. "</div>\n" +
    86. "</body>\n" +
    87. "</html>");
    88. out.flush();
    89. out.close();
    90. }
    91. }

    访问示例中的后台登陆地址:http://localhost:8000/SQLInjection/Login.php?formType=multipart,如下图:
    5. 4. SQL注入 - Multipart传参测试 - 图3
    发送Multipart请求,登陆测试Spring MVC:
    5. 4. SQL注入 - Multipart传参测试 - 图4
    使用万能密码登陆成功:
    5. 4. SQL注入 - Multipart传参测试 - 图5
    Back