什么是XSS?
XSS全称是Cross Site Scripting(为了和CSS进行区分,就叫XSS)即跨站脚本,当目标网站目标用户浏览器渲染HTML文档的过程中,出现了不被预期的脚本指令并执行时,XSS就发生了
XSS分类
XSS有三类:反射型XSS(非持久型)、存储型XSS(持久型)和DOM XSS
反射型XSS
发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,所以称反射型XSS。
一个简单的例子:
<?php
echo $_GET['x'];
?>
如果输入x的值没有经过任何过滤直接输出,假设提交链接为:
http://www.foo.com/xss/reflect.php?x=<script>alert(1)</script>
则alert()函数会在浏览器访问时触发
存储型XSS
存储型XSS和反射型XSS的差别仅在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求目标页面时不用再提交XSS代码。最典型的例子就是留言板XSS,用户提交一条包含XSS代码的留言存储到数据库,目标用户查看留言板时,那些留言就会从数据库中加载出来并显示,于是出发了XSS攻击
DOM XSS
DOM XSS和反射型XSS、存储型XSS的区别在于DOM XSS代码并不需要服务器参与,出发XSS靠的是浏览器的DOM解析,完全是客户端的事情
www.xss.com/domxss.html 代码如下:
<script>
eval(localtion.hash.substr(1));
</script>
触发方式为:www.xss.com/domxss.html#alert(1) 这个URL#后的内容是不会发送到服务器端的,仅仅在客户端被接收并执行,常见的输入点有:
document.URL
document.URLUnencoded
document.localtion
document.referrer
window.location
window.name
实验一
1.构造代码
<%@ 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 'xss_test.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>
<%
String str = request.getParameter("name");
out.print(str);
%>
</body>
</html>
2.漏洞演练
首先访问:http://localhost:8080/jsp/user/xss_test.jsp?name=小明
接着我们试着加载一个js脚本看看,访问:http://localhost:8080/jsp/user/xss_test.jsp?name=<script>alert("XSS")</script>
那么是不是可以改变跳转后的地址?访问:http://localhost:8080/jsp/user/xss_test.jsp?name=<script>location.href='http://www.baidu.com'</script>
既然可以加载JS,那么我们是不是可以通过js来打开本地的某些东西?我提前在D盘根目录下放了一个1.txt文件,访问:http://localhost:8080/jsp/user/xss_test.jsp?name=<script> var objShell = new ActiveXObject("wscript.shell");objShell.Run("D:/1.txt");</script>
上面我都是用的Firefox或者chrome,但是这个只能用IE,因为只有IE才有ActiveXObject这个对象,而且访问之前要去IE的Internet选项里把安全级别调成很低,具体来说就是各种设置全部启用
既然连本地文件都可以打开,那么远程木马,写入文件?自己想象......
实验二
大部分网站都会和数据库打交道,XSS漏洞如果出现在这些网站会怎么样?
1.构造代码
user_insert.jsp
<%@ 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 'user_insert.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">
<jsp:include page="../res.jsp" />
</head>
<body class="container">
<div class="container">
<h3 class="page-header">输入用户信息</h3>
<form action="<%=basePath%>user/user_insert_action.jsp" method="get"
class="form-horizontal">
<div class="form-group">
<label class="col-md-3 control-label">userName:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="username">
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">password:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="password">
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">name:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="name">
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">sex:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="sex">
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">classes:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="classes">
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">phone:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="phone">
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">email:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="email">
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label">qq:</label>
<div class="col-md-5">
<input type="text" class="form-control" name="qq">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-3 col-md-5">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</form>
</div>
</body>
</html>
user_insert_action.jsp
<%@page import="java.sql.*"%>
<%@ 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>user_insert_action.jsp</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>
<%
//1.获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String name = request.getParameter("name");
String sex = request.getParameter("sex");
String classes = request.getParameter("classes");
String phone = request.getParameter("phone");
String email = request.getParameter("email");
String qq = request.getParameter("qq");
%>
<%
//2.向数据库插入数据
Connection conn = null;
PreparedStatement pstmt = null;
try {
//2.1 加载驱动类
Class.forName("com.mysql.jdbc.Driver");
//2.2 创建数据库连接
String url = "jdbc:mysql://localhost:3306/jsp?useUnicode = true & characterEncoding = UTF-8";
String userName = "root";
String pwd = "root";
conn = DriverManager.getConnection(url, userName, pwd);
//2.3 创建预处理声明
String sql = "insert into user" + "(username,password,name,sex,classes,phone,email,qq)"
+ "values(?,?,?,?,?,?,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
pstmt.setString(3, name);
pstmt.setString(4, sex);
pstmt.setString(5, classes);
pstmt.setString(6, phone);
pstmt.setString(7, email);
pstmt.setString(8, qq);
//2.4 向数据库发送sql语句
int res = pstmt.executeUpdate();
out.println(res);
} catch (Exception e) {
e.printStackTrace();
} finally {
pstmt.close();
conn.close();
}
%>
</body>
</html>
user.jsp
<%@page import="cn.edu.wic.jsp.bean.User"%>
<%@page import="java.sql.DriverManager"%>
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.PreparedStatement"%>
<%@page import="java.sql.Connection"%>
<%@ 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>JDBC</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>
<%
List<User> users = new ArrayList<User>();
Connection conn = null;
PreparedStatement pstmt = null;//发送声明的对象
ResultSet rs = null;//返货结果集的对象
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jsp?useUnicode=true&characterEncoding=UTF-8";
String username = "root";
String password = "root";
conn = DriverManager.getConnection(url, username, password);
pstmt = conn.prepareStatement("select * from user");
rs = pstmt.executeQuery();
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setName(rs.getString("name"));
%>
<%= user.getId() + ","%>
<%= user.getUsername() + "," %>
<%= user.getPassword() + "," %>
<%= user.getName() %>
<br>
<%
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (Exception e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
}
}
}
%>
</body>
</html>
用户从user_insert.jsp输入信息提交,然后跳转到user_insert_action.jsp,页面会显示数据库更改的条数信息,然后访问user.jsp查询数据库中的用户信息
2.漏洞实验
首先在user_insert.jsp输入信息
提交后跳转到user_insert_action.jsp,页面显示1,说明用户信息成功插入数据库
最后,访问user.jsp,XSS注入成功
上面实验一进行的实验都可以在这上面利用,比方说写入文件,或者实现页面跳转
信息提交成功
然后访问user.jsp,首先弹框1,没问题,点击确定后,就直接跳转到baidu的网站去了
如果我把弹框的信息删掉,只保留跳转,产生的效果就是只要有用户访问到user.jsp页面,就立马跳转到百度去了,类似DNS劫持
还可以利用XSS获取用户Cookie
都行,只要有漏洞