JSP安全开发之XSS漏洞详解
前言
大家好,好男人就是我,我就是好男人,我就是-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文件是不是也阔以加载呢?
js?js?那么是不是可以来改变跳转后地址?
既然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>
既然连本地文件都可以打开那么远程文件木马?来个电脑恶搞?这个自己慢慢象限。我可没说啊。。。。。
文件都可以打开,那么写一些文件呢?
通过以上实验我们可以看出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~#
我们在留言板留言:
然后访问: 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>
结束语
技术无黑白,专研甚好。
上一篇: 【常用命令】linux
下一篇: 一个简易的Java多页面队列爬虫程序