日期:2019-08-01 14:49:47
更新:
作者:Bay0net
介绍:一直以为爆破很简单,直到学习了 Burp 的宏录制和匹配关键词,才发现 burp 能这么玩。。。
0x01、 漏洞介绍
利用大量的用户名和字典,去对账号进行暴力破解,人的问题(弱密码)可能是永远都无法防御的一个点吧。
DVWA 没模拟验证码缺陷的相关漏洞,在实战中,会遇到很多验证码无效的栗子。
0x02、Low Security Level
查看源码
<?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();
}
?>
分析源码
接受 username
和 password
的值,然后传入数据库做比较,直接使用爆破即可。
万能密码
此处也可以使用万能密码进行登录。
admin'or '1'='1
admin' -- -
admin' #
0x03、Medium Security Level
查看源码
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
$user = mysql_real_escape_string( $user );
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = mysql_real_escape_string( $pass );
$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
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
mysql_close();
}
?>
分析源码
1、对传过来的值,使用了 mysql_real_escape_string()
函数,转义了以下字符:
x00
'
"
x1a
2、使用了 sleep(2)
函数,如果密码错误,则延时两秒响应。
如果爆破的话,还是可以直接进行爆破的,只不过速度慢了些而已。。
万能密码
因为过滤了单引号,我们无法闭合掉前面的单引号,所以此处无法再使用万能密码了。
0x04、High Security Level
查看源码
<?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 = mysql_real_escape_string( $user );
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = mysql_real_escape_string( $pass );
$pass = md5( $pass );
// Check 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
sleep( rand( 0, 3 ) );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
mysql_close();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
分析源码
可以发现,每次发送的时候,都带上了一个 user_token
,每次发送请求的时候 user_token
都是会变的,而 user_token
是来自于上一个数据包的。

使用 burp 爆破
依次选择【Project options】->【Sessions】->【add】-> 【add a Rule Description】-> 【Run a macro(宏)】
选择新建宏

添加的时候,选择配置

爆破成功。。挺费劲的,不如直接写 python 脚本。。

发现了一个问题,密码为 b 的 user_token
,并不是密码 a 响应报文里面的 user_token
,推测原理如下:
每次 burp 都会去访问之前宏里面的页面,然后获取 user_token
,再给爆破数据包里面的 user_token
替换上,所以流程不是 a -> b -> c
,而是 get_user_token -> a -> get_user_token -> b .....
0x05、Impossible Security Level
查看源码
<?php
if( isset( $_POST[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_POST[ 'username' ];
$user = stripslashes( $user );
$user = mysql_real_escape_string( $user );
// Sanitise password input
$pass = $_POST[ 'password' ];
$pass = stripslashes( $pass );
$pass = mysql_real_escape_string( $pass );
$pass = md5( $pass );
// Default values
$total_failed_login = 3;
$lockout_time = 15;
$account_locked = false;
// Check the database (Check user information)
$data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// Check to see if the user has been locked out.
if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) ) {
// User locked out. Note, using this method would allow for user enumeration!
//echo "<pre><br />This account has been locked due to too many incorrect logins.</pre>";
// Calculate when the user would be allowed to login again
$last_login = $row[ 'last_login' ];
$last_login = strtotime( $last_login );
$timeout = strtotime( "{$last_login} +{$lockout_time} minutes" );
$timenow = strtotime( "now" );
// Check to see if enough time has passed, if it hasn't locked the account
if( $timenow > $timeout )
$account_locked = true;
}
// Check the database (if username matches the password)
$data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR);
$data->bindParam( ':password', $pass, PDO::PARAM_STR );
$data->execute();
$row = $data->fetch();
// If its a valid login...
if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) {
// Get users details
$avatar = $row[ 'avatar' ];
$failed_login = $row[ 'failed_login' ];
$last_login = $row[ 'last_login' ];
// Login successful
echo "<p>Welcome to the password protected area <em>{$user}</em></p>";
echo "<img src="{$avatar}" />";
// Had the account been locked out since last login?
if( $failed_login >= $total_failed_login ) {
echo "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
echo "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>";
}
// Reset bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
else {
// Login failed
sleep( rand( 2, 4 ) );
// Give the user some feedback
echo "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";
// Update bad login count
$data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
// Set the last login time
$data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' );
$data->bindParam( ':user', $user, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
重点在于:
$total_failed_login = 3;
$lockout_time = 15;
$account_locked = false;
错误 3 次的话,就锁定 15 分钟,如果在实战中遇到这种情况,可以采用爆破用户名的方法来做。
0x06、burp 爆破匹配关键词
设置如下:

爆破成功会这样显示: