DVWA 黑客攻防演练(二)暴力破解 Brute Froce
暴力破解,简称”爆破“。不要以为没人会对一些小站爆破。实现上我以前用 wordpress 搭建一个博客开始就有人对我的站点进行爆破。这是装了 WordfenceWAF 插件后的统计的情况。
装了 WordfenceWAF 看到报告就深刻感受到国际友人对我这破站的安全性的深刻关怀了。你不封他们的 ip ,他们的程序就会像中了 “奇淫合欢散” 那些对你的网站锲而不舍地爆破。而下面会从 dvma 中学习如何爆破和如何防爆破。
初级
页面是这样的。
很简单的登录,代码可以点击 view source 可以看到
<?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
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>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
这提交太简单,简直引人犯罪,来看看人类满满的恶意吧。下面介绍在 Kali Linux 中比较常用的两个工具 burp suite 和 hydra
Burp Suite
Burp Suite 是 Kali Linux 中预装的非常强大的渗透工具。在这个部分主要用来抓包和进行吧爆破。(可以在这里下载,win,mac都可以用的
将安全等级设置为 low 后,打开 burp suite,设置火狐浏览器的网络走 burp suite 的代理(即localost:8080,如图所示),让 burp suite 拦截火狐的请求
如果你要修改 burp suite 端口的话,你可以在 proxy-> options 中修改
之后你可以在 DVWA 的 Brute Force 的页面上输入帐号 admin 和 密码,密码随便都可以,提交后,发现页面动不了了,因为 burp suite 拦截了请求了。 点击右键,将求的信息发送到入侵器(intruder)(你点击 Forward 会将请求跑完,如果错过了,可以在 Proxy->Http History 和 target 那里都能找到之前的记录 )
然后在 intruder->position 那里点击 clear ,清理掉所有的变量,然后再添加 password 部分。也就是爆破点只有 password 部分。
然后点击 payloads,添加一份常用密码(Kali Linux 在 /usr/share/john/password.lst 或者从 github 上搜一份),并移除注释。
然后再设置爆破的标准,因为密码错误的时候会提示 Username and/or password incorrect
所以报文如果不能匹配到 incorrect,就说明密码爆破成功。所以在 Options 那里设置成这样就行了
然后再点击上面,右上角的 start attack 就可以了
结果如下
明显 password 就是密码了
Hydra
爆破的原理是一样的,不过 hydra 用的是命令行。而根据上面抓包得出的信息。(文档可以看这里) 可以马上用这条命令进行爆破。
hydra 192.168.0.110 -s 5678 -l admin -IVv -P /usr/share/john/password.lst http-get-form "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:incorrect.:H=Cookie: security=low;PHPSESSID=isk2inn2psu1sh56slicq6oim7"
常用参数
- s :端口
- l:用户名
- L:用户列表文件
- p:密码
- P:密码文件
- I:忽略现有的恢复文件,强制退出就有恢复文件了(不需要等10秒)
- v:输出详细信息
- V:输出每次尝试的用户和密码
而 http-get-form 的东西和上面说的都差不多只是一个是图形化界面一个是命令行参数罢了。应该很容易理解。得到的结果如下。
SQL 注入
如果你看上面的源代码的话,你会发现会有 SQL 注入的问题的。密码因为有 md5 一下所有不存在注入的问题,但是 $user 没有。。。所以往 $user 参数的方向去想。 比如 user 是 admin'# 时
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
//结果会是这样
$query = "SELECT * FROM `users` WHERE user = 'admin'#' AND password = '$pass';"
//mysql 语句中 # 后面都是注释,就变成
$query = "SELECT * FROM `users` WHERE user = 'admin'
从而登录到了 admin 帐号。。。
中级
中级的界面是这样的
而代码与前面相比只是多了要用mysqli_real_escape_string函数进行验证,以及登录失败会 sleep(2)
将 用户名和密码转义,比如说 \n 被转义成 \\n,' 转义成 \',这可以抵御一些 SQL 注入攻击,但是不能抵御爆破。代码如下
<?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
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>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
用 hydra 试试 hydra 192.168.0.110 -s 5678 -l admin -IVv -P /usr/share/john/password.lst http-get-form "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:incorrect.:H=Cookie: PHPSESSID=pfoju9qirkvqmcnpgb49a2bop4; security=medium"
毫无疑问可以爆破的
高级
高级的页面代码
<form action="#" method="GET">
Username:<br>
<input type="text" name="username"><br>
Password:<br>
<input type="password" autocomplete="off" name="password"><br>
<br>
<input type="submit" value="Login" name="Login">
<input type="hidden" name="user_token" value="b258081b421c1ee77b3b1d5a53be58ca">
</form>
服务端的代码
<?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
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>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
高级点的代码的话,会检查 user_token
意思是,用户访问 login.php 的时候就生成一个 token 保存在 session,并让它登录的时候发送给服务器,如果服务器有这个 token 就证明这个请求是确实打开了 login.php 才再提交了。
登录失败或者根本就没 token 就将这个 token 从 session 中移除,生成新的 token 再执行之前的操作。
就如注释所言但这是用来防止 CSRF 攻击的,所谓的 CSRF 就是比如打开恶意网站,里面有张图片,或者伪造一个输入框利用网站 cookies 就可以直接“帮你”做删除数据之类的操作的。
这段代码防不了爆破,每次爆破之前获取页面的 token 不就可以了吗。 而 stripslashes 是用来还原 html 词汇,比如 a\tsay\t\'world\' 之类,就会还原成 a say 'world' 对防御爆破没什么帮助的。
一些文章会用 python 去写,我觉得要写代码去做功能考虑去做,如果能工具去做的事绝不写代码。
(有点奉太郎的味道
所以下面主要是用 brup 实现,而基本操作 在 Kali Linux 中要用 brup v1.3.6 才行。v1.3.5 有录制宏的bug 。 burp suite 也不复杂。
爆破可以配置成这样,在作用域(scope)中设置匹配的 Url 及相关的宏,爆破前就能就获取页面的 token,并将之放到 url 参数中
下面是设置过程
录制请求
之后你可以在 DVWA 的 Brute Force 的页面上输入帐号 admin 和 密码,密码随便都可以,提交后,发现页面动不了了,因为 burp suite 拦截了请求了。
此时,如果你点击 Forward 会将请求会继续跑
配置作用域
在 target-> sitemap 那里配置作用域
设置匹配条件
在 project options -> session 的 Session Handle Rules 上,配置匹配的项目以及匹配后要做的事。 点击 Add 按钮创建匹配 Url 后要做的东西。
这里配置意思是说,这是只有爆破的时候才会启动(intruder),其他功能不会用到这个规则。
录制获取 token 的行为的宏
行为是,在爆破前获取 token ,并将之放到 url 中。
然后点击 Add 按钮,添加宏
选择需要那个需要获取 token 的页面,点击最下面的 ok 按钮
配置选项
创建要添加到 url 的参数
填写要放到参数名,双击 b59619b0e13a9016a524e686f47720e2 后帮你自动填好的。然后点击 ok
配置后如下
然后一直点击 Ok 就行了。
爆破设置
步骤和之前的一样。在 target->site map 中找到要爆破的请求
到 Intruder 页,设置要爆破的参数
配置常用密码字典
匹配出错情况
开始爆破
爆破结果如下
不可能级别
到这个级别,利用工具基本无法爆破。来看看代码吧
<?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!
//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 = 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
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();
?>
这代码就多了很多锁定用户的逻辑了。而且 sql 查询也不用stripslashes( $pass )
和 mysql_real_escape_string($pass )
了,更加简洁和安全了。觉得最好还是加个验证码,比如是错了三次之后就要填写验证码之类的逻辑。
最后
- 任何手段都无法保护弱密码,如果密码就是 123456,password之类的弱密码就不需要爆破也能解决,当你的密码是有大小写的英文,有数字,有特殊符号(@#.)之类的8位数以上就非常难被爆破获得了
- 锁定的用户的逻辑其实是有漏洞的,假如我讨厌一个人,那么我写个程序每隔一段登录失败,他就永远登录不了了。。。
- 登录简单吗?一个简单的功能可以很简单,但也可以很不靠谱,老鸟与菜鸟实现同一种功能可能会考虑很多种情况,为何他会知道这种情况,可能是看书的,可能是看别人写的代码,但更有可能是坑过或者曾被坑过。。。这就是老鸟的价值了。
DVWA 黑客攻防演练(二)暴力破解 Brute Froce的更多相关文章
- DVWA 黑客攻防演练(一) 介绍及安装
原本是像写一篇 SELinux 的文章的.而我写总结文章的时候,总会去想原因是什么,为什么会有这种需求.而我发觉 SELinux 的需求是编程人员的神奇代码或者维护者的脑袋短路而造成系统容易被攻击.就 ...
- DVWA 黑客攻防演练(十二) DOM型 XSS 攻击 DOM Based Cross Site Scripting
反射型攻击那篇提及到,如何是"数据是否保存在服务器端"来区分,DOM 型 XSS 攻击应该算是 反射型XSS 攻击. DOM 型攻击的特殊之处在于它是利用 JS 的 documen ...
- DVWA 黑客攻防演练(六)不安全的验证码 Insecure CAPTCHA
之前在 CSRF 攻击 的那篇文章的最后,我觉得可以用验证码提高攻击的难度. 若有验证码的话,就比较难被攻击者利用 XSS 漏洞进行的 CSRF 攻击了,因为要识别验证码起码要调用api,跨域会被浏览 ...
- DVWA 黑客攻防演练(十四)CSRF 攻击 Cross Site Request Forgery
这么多攻击中,CSRF 攻击,全称是 Cross Site Request Forgery,翻译过来是跨站请求伪造可谓是最防不胜防之一.比如删除一篇文章,添加一笔钱之类,如果开发者是没有考虑到会被 C ...
- DVWA 黑客攻防演练(十三)JS 攻击 JavaScript Attacks
新版本的 DVWA 有新东西,其中一个就是这个 JavaScript 模块了. 玩法也挺特别的,如果你能提交 success 这个词,成功是算你赢了.也看得我有点懵逼. 初级 如果你改成 " ...
- DVWA 黑客攻防演练(十一) 存储型 XSS 攻击 Stored Cross Site Scripting
上一篇文章会介绍了反射型 XSS 攻击.本文主要是通过 dvwa 介绍存储型 XSS 攻击.存储型 XSS 攻击影响范围极大.比如是微博.贴吧之类的,若有注入漏洞,再假如攻击者能用上一篇文章类似的代码 ...
- DVWA 黑客攻防演练(九) SQL 盲注 SQL Injection (Blind)
上一篇文章谈及了 dvwa 中的SQL注入攻击,而这篇和上一篇内容很像,都是关于SQL注入攻击.和上一篇相比,上一篇的注入成功就马上得到所有用户的信息,这部分页面上不会返回一些很明显的信息供你调试,就 ...
- DVWA 黑客攻防演练(八)SQL 注入 SQL Injection
web 程序中离不开数据库,但到今天 SQL注入是一种常见的攻击手段.如今现在一些 orm 框架(Hibernate)或者一些 mapper 框架( iBatis)会对 SQL 有一个更友好的封装,使 ...
- DVWA 黑客攻防演练(五)文件上传漏洞 File Upload
说起文件上传漏洞 ,可谓是印象深刻.有次公司的网站突然访问不到了,同事去服务器看了一下.所有 webroot 文件夹下的所有文件都被重命名成其他文件,比如 jsp 文件变成 jsp.s ,以致于路径映 ...
随机推荐
- [Swift]LeetCode242. 有效的字母异位词 | Valid Anagram
Given two strings s and t , write a function to determine if t is an anagram of s. Example 1: Input: ...
- [Swift]LeetCode265.粉刷房子 II $ Paint House II
There are a row of n houses, each house can be painted with one of the k colors. The cost of paintin ...
- [Swift]LeetCode417. 太平洋大西洋水流问题 | Pacific Atlantic Water Flow
Given an m x n matrix of non-negative integers representing the height of each unit cell in a contin ...
- [Swift]LeetCode669. 修剪二叉搜索树 | Trim a Binary Search Tree
Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so that a ...
- Python 使用图灵机器人实现微信聊天功能
首先需要去图灵官网创建一个属于自己的机器人然后得到apikey. 一.自动与指定好友聊天 # -*- coding: utf-8 -*- """ Created at 2 ...
- 【Storm篇】--Storm基础概念
一.前述 Storm是个实时的.分布式以及具备高容错的计算系统,Storm进程常驻内存 ,Storm数据不经过磁盘,在内存中处理. 二.相关概念 1.异步: 流式处理(异步)客户端提交数据进行结算,并 ...
- Curl 请求数据多’1‘
今天做curl请求时遇到一个问题 数据请求回来,无缘无故多了1 加上这一行代码就就可以了:curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- Android中颜色透明度对应16进制值
透明度百分比对应的十六进制: (说明:百分比计算出来会有小数,按照常规的四舍五入处理,详情请往下查看) 百分比:0% HEX: 00 百分比:1% HEX: 30 百分比:2% HEX: 50 百分比 ...
- 行为驱动:Cucumber + Selenium + Java(三) - 使用标签实现测试分组
在上一篇中,我们写出了Selenium + Cucumber + Java环境下的第一个BDD自动化测试用例,这一篇我们说说怎么用标签对用例进行分组. 3.1 Cucumber标签 实际工作中,我们的 ...
- 客户端缓存机制 - Cookie详解
Cookie 作者:Stanley 罗昊 [转载请注明出处和署名,谢谢!] Cookie不是内置对象,所以用的时候需要new出来,Cookie是由服务端产生的,再发送给客户端保存,它不是内置对象,却是 ...