【工具-DVWA】DVWA渗透系列一:Brute Force

前言

DVWA安装使用介绍,见:【工具-DVWA】DVWA的安装和使用

本渗透系列包含最新DVWA的14个渗透测试样例:

1.Brute Force(暴力破解)                    
2.Command Injection(命令注入)
3.CSRF(跨站请求伪造)                        
4.File Inclusion(文件包含)
5.File Upload(文件上传)                    
6.Insecure CAPTCHA(不安全的验证码)
7.SQL Injection(SQL注入)                    
8.SQL Injection(Blind)(SQL盲注)
9.Weak Session IDs(有问题的会话ID)                
10.XSS(DOM)(DOM型xss)
11.XSS(ref)(反射型xss)                    
12.XSS(Stored)(存储型xss)
13.CSP Bypass(Content Security Policy内容安全策略,旁路/绕过)    
14.JavaScript​​​​​​​

安全级别分低、中、高、安全四个级别来分析Brute Force的渗透测试过程。

1 Low

1.1 渗透测试

  • 使用SQL注入方式

使用账号【admin' OR '1'='1】+正确密码【password】,发现登陆失败

使用账号【admin' OR '1'='1】+空密码【】或者错误密码 ,发现登陆成功

使用账号【admin' OR '1'='2】+任何密码,都能登陆成功

咦?这是为何呢?找源码看看。

1.2 源码分析

网站路径:DVWA-master\vulnerabilities\brute下面有源码

index.php:根据安全级别,来选择使用的php代码。

switch( $_COOKIE[ 'security' ] ) {
	case 'low':
		$vulnerabilityFile = 'low.php';
		break;
	case 'medium':
		$vulnerabilityFile = 'medium.php';
		break;
	case 'high':
		$vulnerabilityFile = 'high.php';
		break;
	default:
		$vulnerabilityFile = 'impossible.php';
		$method = 'POST';
		break;
}

low.php:发现SQL执行使用的拼接,且对参数username、password没有做任何过滤,存在SQL注入漏洞和Brute Force漏洞。

//用来检测变量是否设置
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 = 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>' );

        //当查询结果为1个,则认为登陆成功
	if( $result && mysqli_num_rows( $result ) == 1 ) {
		// Login successful
		$html .= "<p>Welcome to the password protected area {$user}</p>";
		$html .= "<img src=\"{$avatar}\" />";
	}
	else {
		// Login failed
		......
	}
    ......
}

回到之前的问题,为何使用账号【admin' OR '1'='1】+正确密码【password】,会登陆失败。

执行SQL,发现使用password的密码的用户有2个,当查询结果不为1个时都认为登陆失败:

PS:所以,一般SQL注入时,会使用空密码,来避免因密码重复而导致认证失败。

当用户名为【admin' OR '1'='2】时,由于AND优先级高于OR,所以,OR之后的条件相当于不存在,所以只能查询出admin一个用户出来,这个和password不正确的情况是一样的。

2 Medium

2.1 渗透测试

  • 使用Burpsuite来测试

发现:依然可以成功,但是,慢了很多。

  • 使用SQL注入

发现:无效!!

2.2 源码分析

medium.php:与low相比,添加了mysql_real_escape_string函数,来对特殊字符进行转义;同时登陆失败会休眠2秒

if( isset( $_GET[ 'Login' ] ) ) {
	// Sanitise username input
	$user = $_GET[ 'username' ];
	//对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义
	$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' ];
	//对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义
	$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
		$html .= "<p>Welcome to the password protected area {$user}</p>";
		$html .= "<img src=\"{$avatar}\" />";
	}
	else {
		// Login failed 登陆失败休眠2秒
		sleep( 2 );
		$html .= "<pre><br />Username and/or password incorrect.</pre>";
	}

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

PS:mysql_real_escape_string函数可以绕过哟,sleep(2)休眠时间真是短,贴心......

3 High

3.1 渗透测试

  • burpsuite测试

发现:请求中多了一个参数user_token,按原来的暴力请求,会返回CSRF token不正确的异常。

  • 查看页面代码

发现:页面中多了一个隐藏字段user_token,它会随着请求提交,且每次登陆后,该值会变化

  • 再次尝试

1、抓取请求,发送到Intruder,选择Password和user_token,使用Pitchfork(一一映射方式),即每次登陆密码都对应新的user_token

2、线程数修改为1,因为一个user_token只能支持一次登陆测试,只要测试过一次,那么token就会失效。

3、从页面中抓取user_token

点击Refetch response,然后双击你需要抓取的内容,burp会自动帮你填充start和end的内容:

4、Payload1设置照旧,payload2,选择Recursive grep,payload内容会自动加载刚刚配置的截取规则,first request需要填写最新的user_token,去页面里看代码或者使用刚刚response里的value(在这之后没有使用过这个token)。

5、测试成功,user_token会自动截取页面的值,进行请求:

3.2 源码分析

high.php:与medium相比主要就多了一个CSRF-token的效验和生成,还有就是stripslashes去掉反斜线,避免转义绕过

if( isset( $_GET[ 'Login' ] ) ) {
	// Check Anti-CSRF token,验证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 ) {

		// Login successful
		$html .= "<p>Welcome to the password protected area {$user}</p>";
		$html .= "<img src=\"{$avatar}\" />";
	}
	else {
		// Login failed,登陆失败休眠0-3秒
		sleep( rand( 0, 3 ) );
		$html .= "<pre><br />Username and/or password incorrect.</pre>";
	}

}

// Generate Anti-CSRF token,产生CSRF-token
generateSessionToken();

4 impossible

4.1 渗透测试

发现没效果......

4.2 源码分析

核心代码:限制登陆失败次数,以及账号锁定机制,同时使用了更安全的PDO机制,来限制传参执行数据库操作。

// Default values
$total_failed_login = 3;
$lockout_time       = 15;
$account_locked     = false;

// Check the database (Check user information),不能使用PDO扩展本身执行任何数据库操作
$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.当登陆失败次数超过3次,将锁定账号15分钟
if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {
	// User locked out.  Note, using this method would allow for user enumeration!
	//$html .= "<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 = strtotime( $row[ 'last_login' ] );
	$timeout    = $last_login + ($lockout_time * 60);
	$timenow    = time();

	/*
	print "The last login was: " . date ("h:i:s", $last_login) . "<br />";
	print "The timenow is: " . date ("h:i:s", $timenow) . "<br />";
	print "The timeout is: " . date ("h:i:s", $timeout) . "<br />";
	*/

	// Check to see if enough time has passed, if it hasn't locked the account,
	if( $timenow < $timeout ) {
		$account_locked = true;
		// print "The account is locked<br />";
	}
}

5 总结

暴力破解可应用场景:

  • 没有验证码
  • 没有账号登陆失败+锁定机制

登陆渗透过程:

  1. 尝试SQL注入
  2. 尝试常规暴力破解
  3. 通过分析页面源代码和请求参数来解决CSRF类验证

爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!

发布了96 篇原创文章 · 获赞 237 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qqchaozai/article/details/102610483