前言
大家好,好男人就是我,我就是好男人,我就是-0nise。在各大漏洞舉報平台,我們時常會看到XSS漏洞。那麼問題來了,為何會出現這種漏洞?出現這種漏洞應該怎麼修複?
本文
1.XSS?XSS?XSS是什麼鬼?
XSS又叫跨站指令碼攻擊(Cross Site Scripting),我不會告訴他原本是叫CSS的,但是為了不和我們所用的層疊樣式表(Cascading Style Sheets)CSS搞混。CSS(跨站指令碼攻擊),CSS(層疊樣式表)傻傻分不清。所以就叫XSS咯。
2.XSS的危害是什嗎?
實驗一:
0x00構造代碼
<%@ 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>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <div style="margin: 0 auto"> <%//設定編碼 request.setCharacterEncoding("UTF-8");//接收使用者傳入值 String tmp = request.getParameter("opr"); //減速傳入值是否為空白 if(tmp == null){ out.print("111"); }else{ //轉碼 String opr = new String(tmp.getBytes("ISO-8859-1"),"utf-8"); out.print(opr); } %> 我是內容 </div> </body></html>
0x01環境布局
0x02漏洞演練
我們訪問:http://localhost:8080/XSS/index.jsp?opr=i%E6%98%A5%E7%A7%8B
然後訪問:http://localhost:8080/XSS/index.jsp?opr=0nise
最後我們發現了一個“偉大的規律”:
opr參數等於什麼頁面就列印什麼。(好像是廢話)
我們接著來載入一個圖片看看
訪問:http://localhost:8080/XSS/index.jsp?opr=%3Cimg%20src=%221.png%22%3E%3C/img%3E
既然圖片都可以載入,那麼我們JS檔案是不是也闊以載入呢?
訪問:http://localhost:8080/XSS/index.jsp?opr=%3Cscript%3Ealert(/i%E6%98%A5%E7%A7%8B%E7%A4%BE%E5%8C%BA%E6%AC%A2%E8%BF%8E%E5%A4%A7%E5%AE%B6/)%3C/script%3E
Js?Js?那麼是不是可以來改變跳轉後地址?
訪問:http://localhost:8080/XSS/index.jsp?opr=%3Cscript%3Elocation.href=%27http://bbs.ichunqiu.com%27%3C/script%3E
既然xss都可以載入js,那麼,我們是不是通過js來開啟本地的某些東西?
提前放了一個MD5.exe檔案
訪問:http://localhost:8080/XSS/index.jsp?opr=
既然連本地檔案都可以開啟那麼遠程檔案木馬?來個電腦惡搞?這個自己慢慢象限。我可沒說啊。。。。。
檔案都可以開啟,那麼寫一些檔案呢?
訪問:http://localhost:8080/XSS/index.jsp?opr=%3Cscript%3Evar%20fso,tf;fso%20=%20new%20ActiveXObject(%22Scripting.FileSystemObject%22);tf%20=%20fso.CreateTextFile(%22d:\\test.txt%22,true);tf.WriteLine(%22i%E6%98%A5%E7%A7%8B%E7%A4%BE%E5%8C%BA%E6%AC%A2%E8%BF%8E%E6%82%A8%22);tf.Close();alert(%22%E6%96%87%E4%BB%B6%E5%86%99%E5%85%A5%E6%88%90%E5%8A%9F%EF%BC%81%22);%3C/script%3E
通過以上實驗我們可以看出opr參數賦值操作。如果opr參數沒有值的話,就無法執行執行,被攻擊者必須訪問攻擊者提前設計好的才能攻擊。這種XSS攻擊方式叫做:儲存型XSS
如果你想看到更給力的實驗,請接著往下看。
實驗二:
前言:
大部分網站都會和資料打交道那麼,XSS漏洞出現這些網站是什麼樣子的?
0x00構造代碼
資料庫部分
BaseDao.java
import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class BaseDAO { //開啟串連 public Connection getConn(){ Connection conn = null; try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); conn = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=SQLTMP","sa","sa"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; } //關閉連結的方法 public void closeAll(Connection conn,Statement stat,ResultSet rs){ try { if(rs != null) rs.close(); if(stat != null) stat.close(); if(conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } //重載關閉方法 public void closeAll(Connection conn,PreparedStatement pstat,ResultSet rs){ try { if(rs != null) rs.close(); if(pstat != null) pstat.close(); if(conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } //繼續重載 public void closeAll(Connection conn,PreparedStatement pstat){ try { if(pstat != null) pstat.close(); if(conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } //增刪改的公用方法 public int upDate(String sql,Object[] pram){ PreparedStatement pstat = null; Connection conn = null; int a = 0; try { conn = getConn(); pstat =conn.prepareStatement(sql); //遍曆參數集合,將集合中的參數對應添加到sql語句中 for (int i = 1; i <= pram.length; i++) { pstat.setObject(i, pram[i-1]); } //調用方法 a = pstat.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); }finally{ closeAll(conn, pstat); } return a; }}
CommentDao.java
import java.sql.*;import java.util.*;import entity.*;public class CommentDao extends BaseDAO { /** * 擷取所有留言 * */ public List<comm> GetComment(){ //SQL語句 String sql = "SELECT CID,CName,CContext FROM Comments"; List<comm> list = new ArrayList<comm>(); //資料庫連接對象 Connection conn = null; //SQL執行對象 PreparedStatement pstmt = null; //資料庫執行傳回值 ResultSet rs = null; try { //建立資料庫連結 conn = this.getConn(); //建立SQL執行對象 pstmt = conn.prepareStatement(sql); //執行SQL語句 傳回值 rs = pstmt.executeQuery(); //讀取 while (rs.next()) { comm comment = new comm(); comment.setCID(rs.getInt("CID")); comment.setCName(rs.getString("CName")); comment.setCContext(rs.getString("CContext")); list.add(comment); } } catch (Exception e) { e.printStackTrace(); }finally{ //關閉 this.closeAll(conn, pstmt, rs); } return list; } public int AddComment(comm comment){ String sql = "INSERT INTO Comments VALUES(?,?)"; //受影響行數 int result = 0; //資料庫連接對象 Connection conn = null; //SQL執行對象 PreparedStatement pstmt = null; try { //建立資料庫連結 conn = this.getConn(); //建立SQL執行對象 pstmt = conn.prepareStatement(sql); //設定參數 pstmt.setString(1, comment.getCName()); pstmt.setString(2, comment.getCContext()); //執行SQL語句 result = pstmt.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally{ this.closeAll(conn, pstmt); } return result; }}
CommentServlvet
import java.io.*;import javax.servlet.*;import javax.servlet.http.*;import entity.*;public class CommentServlvet extends HttpServlet { /** * doGet() */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String opr = request.getParameter("opr"); CommentDao commentDao = new CommentDao(); //檢索參數是否為空白 if(opr == null || opr.equals("all")){ request.setAttribute("all", commentDao.GetComment()); //轉寄 request.getRequestDispatcher("comment.jsp").forward(request, response); }else if (opr.equals("add")){ comm comment = new comm(); comment.setCName(request.getParameter("UName")); comment.setCContext(request.getParameter("context")); if(commentDao.AddComment(comment) > 0){ out.print("<script>alert('留言成功');location.href='CommentServlvet?opr=all';</script>"); }else{ out.print("<script>alert('留言失敗');location.href='CommentServlvet?opr=all';</script>"); } }else{ request.setAttribute("all", commentDao.GetComment()); //轉寄 request.getRequestDispatcher("comment.jsp").forward(request, response); } out.flush(); out.close(); } /** * doPost() */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
Comment.jsp
<%@ page language="java" import="java.util.*,entity.*" 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>My JSP 'comment.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <% request.setCharacterEncoding("UTF-8"); if(request.getAttribute("all") == null){ request.getRequestDispatcher("CommentServlvet?opr=all").forward(request, response); } %> <table> <% List<entity.comm> list = (List<entity.comm>)request.getAttribute("all"); for(int i = 0; i < list.size(); i++ ){ %> <tr> <td><%=list.get(i).getCName() %></td> <td><%=list.get(i).getCContext() %></td> </tr> <% } %> </table> <form action="CommentServlvet?opr=add" method="post"> <textarea rows="5" cols="30" name="context"></textarea> 暱稱:<input type="text" name="UName" /> <input type="submit" value="提交" /> </form> </body></html>
0x01漏洞實驗
root@1~#
我們在留言板留言:
<script> var objShell = new ActiveXObject("wscript.shell");objShell.Run("G:/work/XSS/WebRoot/Md5.exe");</script>
然後訪問:http://localhost:8080/XSS/comment.jsp
這樣只要訪問這個頁面,軟體就自動開啟了,來個遠程檔案?慢慢領悟。
root@2~#
我們在留言板留言:
複製代碼 代碼如下:
<script>var fso,tf;fso = new ActiveXObject("Scripting.FileSystemObject");tf = fso.CreateTextFile("d:\\test.txt",true);tf.WriteLine("i春秋社區歡迎您");tf.Close();alert("檔案寫入成功!");</script>
然後訪問: http://localhost:8080/XSS/comment.jsp
檔案寫入成功。
root@3~#
留言內容:
[code]<script>location.href='http://bbs.ichunqiu.com'</script>[code]
訪問頁面:http://localhost:8080/XSS/comment.jsp
訪問留言頁面自動跳轉到攻擊者特定的網站。難道這就是傳說中的劫持嗎?
3.XSS防禦方案
正所謂哪裡有攻擊,哪裡就有防禦。XSS一樣,有攻擊的方式,也有防禦的方案。
EL運算式+JSTL標籤庫
EL(Expression Language):[size=12.0000pt]為了使JSP寫起來更簡單。運算式語言的靈感來自於ECMAScript和XPath運算式語語言,他提供了JSP中簡化運算式的方法,讓jsp代碼更簡單。
JSTL(JSP Standard Tag Library):開放原始碼的JSP標籤庫。
實驗一防禦代碼:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%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>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <div style="margin: 0 auto"> <% request.setCharacterEncoding("UTF-8"); String tmp = request.getParameter("opr"); //減速傳入值是否為空白 if(tmp == null){ out.print("111"); }else{ //轉碼 String opr = new String(tmp.getBytes("ISO-8859-1"),"utf-8"); request.setAttribute("name", opr); %> <c:out value="${requestScope.name }"></c:out> <% } %> 我是內容 </div> </body></html>
實驗二防禦代碼:
<%@ page language="java" import="java.util.*,entity.*" pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><%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>My JSP 'comment.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> <% request.setCharacterEncoding("UTF-8"); if(request.getAttribute("all") == null){ request.getRequestDispatcher("CommentServlvet?opr=all").forward(request, response); } %> <table><!-- 防禦XSS方案 --> <c:forEach var="x" items="${requestScope.all }"> <tr> <td> <c:out value="${x.getCName() }"></c:out> </td> <td> <c:out value="${x.getCContext() }"></c:out> </td> </tr> </c:forEach> </table> <form action="CommentServlvet?opr=add" method="post"> <textarea rows="5" cols="30" name="context"></textarea> 暱稱:<input type="text" name="UName" /> <input type="submit" value="提交" /> </form> </body></html>
結束語
技術無黑白,專研甚好。