1. 网络不是法外之地,学习网络安全只是为了更好地保护网络安全
2. 文中内容仅为学习交流,涉及到的工具和方法均不能用于破坏网络安全
该笔记仅在CSDN中发表,如转载请大家注明出处。
CSDN:http://blog.csdn.net/elang6962?viewmode=contents
-
PhpStudy=0.4公测版
-
OS:Kali Linux 2019.4
-
DVWA:1.9
上一篇中记录了在Kali 2019.4中利用phpstudy搭建dvwa的过程,这一篇是完整的DVWA练习记录,内容长,很肝!
1 Brute Force 暴力破解
1.1 使用工具
- 抓包/破解:OWASP ZAP
- 代理插件:火狐FoxyProxy
1.2 Low
思路:
- 输入正确的用户名和密码,提交观察。这里自动填写了用户名和密码,点提交就行。看到出现一句欢迎语和一张图片,同时F12看到请求方法是GET,当然看URL里带了用户名和密码也就知道了。
- 输入错误的用户名和密码,提交观察。直接删除密码点提交,提示“用户名或密码错误”。
- 准备抓包破解。FoxyProxy添加一个8081端口的本地代理,ZAP在选项-local proxies里设置了8081端口,因为有时会把ZAP和Burp同时开,避免端口冲突。
- 抓包。Foxy开启代理,为了做戏,密码处填个错的,点击提交。这里我出现了浏览器不信任127.0.0.1,看了下URL变成了https,点高级-继续访问。然后ZAP就抓到了包。
- 暴力破解。点击抓到的包,上图右上角请求处查看URL,找到那个URL有用户名和密码的包,点右键-选择FUZZ。这里FUZZ可能会带有一个Fuzz Locations,一般都是错的,删掉后选择用户名的payloads也就是
admin
,点击右边的Add按键,在弹出的对话框随便输入几个用户名,有admin
就行;密码同理,有password
就行,点击Start Fuzzer
。
- 观察结果。ZAP做不到成功后自动停止,只能从响应中观察哪个是成功的。这里因为自己玩,总共没几个组合,直接从结果里看了。响应中有一个4424字节,其他都是4381,这就可以猜测4424就是成功了,点击在ZAP中查看果然能看到那句欢迎语。
结束后分析DVWA的PHP源代码,Low等级拿到用户输入的用户名和密码后,对密码进行了md5,然后就直接到数据库里查询,没有任何保护。
1.3 Medium
依据Low的思路,依旧使用ZAP执行暴力破解,还是可以成功,但明显FUZZ的时间变久了。从截图看,每个请求的RTT时间是秒级的,看了源代码,在请求失败时会sleep2秒,这里不懂为什么RTT中记录的时间会比2秒多那么多。
1.4 High
思路:
- 先从最简单的ZAP暴力开始,非常快的结束了。仔细看ZAP可以发现,所有包的响应都是0字节,且状态码是302。这时候想到看看请求包和响应包有什么特殊的地方,很明显的看到请求包的url中多了个
user_token
,先瞧瞧是不是它的原因。
- 这时候继续用zap抓包,回到页面分别测试2次正确输入和2次错误输入,再来看包的内容。点开1号正确包,可以看到url和referer处都有
user_token
,但二者不同;再点第二个包,2个位置依旧不同,且url中的user_token
与第一个包不同,到这里就可以判断这个user_token
是每次请求都会改变的,不论输入的用户名密码组合是否正确。错误的2个包已经没必要看了。这里第一个包的referer
就有token是因为直接在第一次抓包后的页面中进行这一步。
- 已经确定是
user_token
的问题就好办了,先从响应中找到它。查看页面源代码,可以直接搜索user_token
。
- 现在可以大致知道规则,即每次点击提交,响应中会返回一个
user_token
,在下一次提交时带上。
- 那么第0个
user_token
是怎么来的?是第一次点击进入Brute Force模块时的响应中返回的。很明显,ZAP这类工具没法用了,必须手动从响应中拿出user_token
并放入下一个请求中,也就是要自己动手。这里有两个思路:
-
- 一是按照上面那个图一直进行下去,从第一次进入模块之后就不断重复“提交-获取token-提交”,直到发现成功flag或者payloads消耗光。
request Brute Force
get response
while 1:
user_token = user_token in response
send request(username,password,user_token)
get response
# 思路一的代码
import urllib.request as urllib2
import re
from urllib import request
IP = '127.0.0.1'
USERFILE = '' # username的字典
PASSFILE = '' # password的字典
COOKIE = '' # 手动查看后加入
# 由字典生成列表
usernames = [x[:-1] for x in open(USERFILE,'r')]
passwords = [x[:-1] for x in open(PASSFILE,'r')]
# 访问首页
response = request.urlopen(request.Request('http://' + IP + '/dvwa/vulnerabilities/brute/',headers={'Cookie':COOKIE}))
content = response.read().decode('utf-8')
for username in usernames:
for password in passwords:
# 获取user_token
user_token = re.findall("(?<=<input type='hidden' name='user_token' value=').+?(?=' />)",content)[0]
# 发送request
url = 'http://' + IP + '/dvwa/vulnerabilities/brute/?username='+username+'&password='+password+'&Login=Login&user_token='+user_token
response = request.urlopen(request.Request(url,headers={'Cookie':COOKIE}))
content = response.read().decode('utf-8')
# 确认结果
print('用户名:'+username)
print('密码:'+password)
if 'Username and/or password incorrect.' in content:
print('结果:失败')
else:
print('结果:成功')
break
else:
continue
break
-
- 二是将“进入Brute Force-提交”这个过程不断循环,成功后自动退出。从代码看和思路一区别不大,反而看起来对服务器的负担更大些,因为每次循环都会多请求一次首页。
while 1:
request Brute Force
get response
user_token = user_token in response
send request(username,password,user_token)
# 思路二代码
# 和思路一的区别是把访问首页挪到循环里
for username in usernames:
for password in passwords:
# 访问首页
response = request.urlopen(request.Request('http://' + IP + '/dvwa/vulnerabilities/brute/',headers={'Cookie':COOKIE}))
content = response.read().decode('utf-8')
# 获取user_token
user_token = re.findall("(?<=<input type='hidden' name='user_token' value=').+?(?=' />)",content)[0]
# 发送request
url = 'http://' + IP + '/dvwa/vulnerabilities/brute/?username='+username+'&password='+password+'&Login=Login&user_token='+user_token
response = request.urlopen(request.Request(url,headers={'Cookie':COOKIE}))
content = response.read().decode('utf-8')
# 确认结果
print('用户名:'+username)
print('密码:'+password)
if 'Username and/or password incorrect.' in content:
print('结果:失败')
else:
print('结果:成功')
break
else:
continue
break
1.5 防御
- 验证token还是要有的,反正还得防CSRF
- 设置一个失败阈值,达到上限禁止尝试
- 验证码,也可以设置一个失败阈值,达到后开始使用验证码
- 参数化SQL语句,使用PDO技术
2 命令注入
2.1 使用工具
命令注入的底层是执行系统命令,如windows的ping、ipconfig等,漏洞产生的原因主要是系统没有处理或过滤用户输入,将输入作为系统命令的参数直接与命令进行拼接
工具上强行安排hackbar插件,其实啥也不需要
2.2 Low
- 很明显这里执行的是
ping
,用户的输入作为参数传入。正常输入127.0.0.1
,可以看到会输出常见的ping响应。 - 可以直接输入
payload
测试是否存在漏洞。这里使用字母少且输出快的dir
命令,payload
为127.0.0.1|dir
- 这里可以把常见的命令连接符都测试一遍。
-
|
,命令1|命令2
,将命令1的输出作为命令2的输入,payload
为127.0.0.1|dir
可输出dir
-
||
,命令1||命令2
,只有命令1执行失败时才会执行命令2,payload
为127.0.0||dir
可输出dir
-
&
,命令1;命令2
,同时执行命令1和命令2,payload
为127.0.0.1&dir
可输出dir,此时dir先执行完,所以输出在ping的上面
-
&&
,命令1&&命令2
,只有命令1执行成功时才会执行命令2,payload
为127.0.0.1&&dir
可输出dir
-
;
,命令1;命令2
,按顺序执行命令1和命令2,2个命令间不存在任何逻辑关系,payload
为127.0.0.1;dir
基本可以判断,这里没有对输入做任何安全处理。
2.3 Medium
直接测试:
-
可行:
|
、&
、||
-
不可行:
&&
、;
看一下php源码,果然只过滤了不可行的2个符号,没有对输入做其他处理
2.4 High
直接测试:
- 可行:
|
- 不可行:
||
、&&
、&
、;
看看代码,一开始就给ip用了个trim函数,默认去除输入参数两侧的空白字符。且符号过滤的更多了,里面是包含|
的,仔细看可以发现,代码里过滤的|
后面有个空格,简直就是特地给我这样的萌新留个窗户。
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
trim()函数移除字符串两侧的空白字符或预定义字符。空白字符如回车、制表符、换行符、NULL、空格等
2.5 防御
白名单模式,DVWA这里impossible模式中检测输入是否是合法的ipv4地址;同时检测token防止CSRF
3 CSRF
3.1 使用工具
ZAP和Burpsuite
上一篇博客安装phpstudy时设置了只允许127.0.0.1访问,为了实现测试中的2个IP,将此设置暂时去掉了。方法是在网页面板-网站-配置中,按照提示点击恢复
,然后重启服务。
3.2 Low
- 在正常输入的测试中就打开zap和本地代理,查看请求和响应。注意到URL中带有刚才填的参数。
- 似乎没什么特别的,按照URL中对应参数的名称直接构造测试页面。这里把正常页面的html代码直接复制后修改,改动了填写密码的部分。构造完成拷贝到网站根目录下,这个页面的路径为
127.0.0.1/test.html
,这里最后使用按钮是为了多次测试,实际中应该是自动执行的。
<form action="http://192.168.1.2/dvwa/vulnerabilities/csrf/" method="GET">
<input type="hidden" name="password_new" value="123456"><br />
<input type="hidden" name="password_conf" value="123456"><br />
<input type="hidden" value="Change" name="Change">
<input type="submit" value="Click Me">
</form>
- 在同一个浏览器中输入
127.0.0.1/test.html
,进入后效果如下图,没有css效果是因为网页代码中引用的样式都是一些相对目录,如<link rel="stylesheet" type="text/css" href="../../dvwa/css/main.css" />
。
- 点击图里的Click Me按钮,密码成功修改。
3.2 Medium
- 这里为了更像真的,我改了hosts,所以测试时的url都不再是ip了。另外ZAP在抓包上真的不如Burp用着舒服,这把用Burp了。
- 按照low的思路进行,在浏览输入
127.0.0.1/test.html
,虽然还是到了CSRF的页面,但是提示由修改成功变为了That request didn't look correct.
- 开Burpsuite,找找看原因。首先在low和medium下,正常输入,点击提交,请求包的内容是一样的;然后在medium下正常输入一次,尝试CSRF一次,通过对比请求包,发现CSRF请求包只有referer一项和正常包是有区别的。那么就试着改改,先进入test.html,打开抓包,点击Click Me,Burp里抓到的包右键发送到Repeater模块,先把referer修改为和正常包一样,果然成功了。
- 确定问题所在,思考如何绕过。注意到请求包host和referer有同样的域名(用Burp抓到的包GET请求网址是不包含域名的,用F12看到的才有),猜测防护规则来自host,所以回到第二步,把host的域名加到referer的各个位置,包括:只有域名;域名在
127.0.0.1/test.html
前面;在后面;在中间。测试结果是只要referer里有那个域名,就可以修改成功。如图,甚至放在数字中间都可以。
- 由此,Medium级别的防护可以肯定为,识别referer中是否包含host的域名。绕过方法就简单了,一种是把
test.html
改成exercise.hun.html
,另一种是新建一个和域名同名的文件夹,把test.html
丢进去。
3.4 防御
High等级正常输入,可以看到URL中多了user_token,而且每次响应都会改变。
而CSRF只不过让用户进入了我这边的一个html,不管是点击Click Me还是进入html就自动提交,都没办法在Form中添加上正确的user_token.
要绕过的话,这里的逻辑似乎是:
换句话说,用户只访问一次test.html
,而该页面要自己完成后面所有的步骤。
impossible中,更需要输入用户的原密码才可以修改,这就更不可能绕过了。
4 文件包含
4.1 前置
设置php为允许包含和打开文件
4.2 Low
- 点击几个文件,观察url,
page=
后面就是参数 - 修改参数为一个不存在的文件,先按顺序尝试
file4.php
,忘记它是存在了,如图,正好说明漏洞是存在的,这里可以证明漏洞起码是可以读取服务器本地文件的
- 尝试不存在的文件名,报错且信息中包含本地文件的目录
- 当前目录确定可以利用漏洞,尝试服务器的其他目录。在文件上传漏洞中上传
phpinfo.php
,完整的url改为http://exercise.hun/dvwa/vulnerabilities/fi/?page=../upload/phpinfo.php
,可以成功输出php信息 - 尝试包含远程文件,这里测试本机中DVWA服务器之外的目录,在DVWA同级目录拷贝
phpinfo.php
。完整url为http://exercise.hun/dvwa/vulnerabilities/fi/?page=http://127.0.0.1/phpinfo.php
,可显示php信息
核心:文件包含漏洞的本质就是通过漏洞执行攻击者本地的脚本。否则,需要利用文件上传或是通过信息收集找到其服务器上已有的脚本
4.3 Medium
file4.php
还是可以执行,也就是至少服务器上该目录是可以利用的- 尝试不存在的文件依旧会报出文件目录
http://exercise.hun/dvwa/vulnerabilities/fi/?page=../upload/phpinfo.php
失败,从报错信息看Warning: include(upload/phpinfo.php): failed to open stream: No such file or directory in /www/admin/localhost_80/wwwroot/dvwa/vulnerabilities/fi/index.php on line 36
服务器只解析了upload/phpinfo.php
而不是../upload/phpinfo.php
,应该是../
被过滤了,那么就可以用....//
尝试绕过,果然成功。所以这里是把../
作为一个整体过滤了http://exercise.hun/dvwa/vulnerabilities/fi/?page=http://127.0.0.1/phpinfo.php
失败,同理也是过滤,尝试httphttp://://
,这么写如果成功就可以肯定是把http://
作为整体过滤
4.4 High
file4.php
依旧可行- 尝试服务器其他目录和远程文件均失败,提示如图,错误信息太少
- 继续尝试,把
phpinfo.php
拷贝到file4.php
同目录中,失败,提示相同。 - 考虑文件名,将
phpinfo
不断修改,直至修改为filephpinfo
后可执行,也就是page=
的参数必须以file
开头,那么首先远程文件不可能执行了 - 能执行本地文件也是好滴,使用
file://
加上完整路径就成。exercise.hun/dvwa/vulnerabilities/fi/?page=file:///www/admin/localhost_80/wwwroot/dvwa/vulnerabilities/upload/phpinfo.php
成功,www前面的第三个/
是根目录/
,使用file://
需要使用文件的本地完整路径 - 想要执行自己的脚本就得把它上传到目标服务器
4.5 防御
黑名单:Medium级别就是,太容易有遗漏
白名单:High就是
通过代码,High级别只需要file*
或include.php
,impossible级别最绝,只允许file1、file2、file3
5 文件上传
5.1 工具
burpsuite
5.2 Low
看到是个上传文件的模块,直接尝试上传一个php文件,提示成功,并且会报出文件所在路径。
5.3 Medium
- 直接上传php文件是失败的,提示“只接受jpeg或png文件”
- 现在用Burpsuite抓包,上传正常图片和php文件各抓一个包,对比他们的不同。
- 分析上面2个图,二者的不同有三处:文件名、content-type、文件内容
- 尝试找到漏洞。文件内容肯定是放在最后考虑的,那么可以测试把文件名或content-type修改后重发
- 经过测试,修改2处、修改content-type都可以上传,只修改文件名依旧上传失败,可以确定只要修改掉content-type即可
5.4 High
- 来到高级,用前面的方法都会失败。而我们能修改的只剩下文件内容,也就是让php文件的内容变成图片内容。
- 新建一个
1.php
,Kali终端使用cat 1.jpeg 1.php > hello.jpeg
将两个文件合并成新的图片,新文件的大小等于两个文件之和。 - 现在直接上传就好了。为了验证,可以切回Low等级到文件包含下,随便点一个正确的文件,然后地址栏改为
http://exercise.hun/dvwa/vulnerabilities/fi/?page=../../hackable/uploads/hello.jpeg
,回车后可以看到上面是图片内容的乱码,下面是php信息。
5.5 防御
修复方法有两个方向:
- 阻止非法文件上传,文件后缀、类型、内容必须控制,等于high等级,但不能有文件包含漏洞、服务器使用apache(因为nginx有一个畸形解析漏洞,可以用在文件包含)
- 阻止非法文件执行,存储目录执行权限关闭、存储文件和服务器分离、文件重压缩生成、文件重命名
6 SQL
6.1 思路和工具
对于固定目标的SQL注入,其实就是个找注入点,剩下的就是sqlmap
寻找注入点就是判断后台是否将用户输入直接进行SQL语句拼接,导致用户的输入被执行:
- 1 and 1=2
- 1’ and ‘1’='2
- 1" and “1”="2
上面都是真 and 假 = 假
,如果假
被执行则存在SQL注入点;同时可以确定是字符型还是数字型
工具:
- hackbar插件,sqlmap
6.2 Low
输入1
提交,hackbar读取url后把payload改为4' and '1'='2
提交,输出是空的,说明系统执行了注入的语句,注入点确定。
sqlmap:sqlmap -u "<url>" --cookie="<cookie>"
6.3 Medium
提交页变成序号选择,且通过F12网络查看是post方法,url中没有修改payload的地方。
用hackbar读取url,勾选post data,修改payload即可
sqlmap:sqlmap -u "<url>" --data="<post data>" --cookie="<cookie>" -level 2
level2可检测data中的注入点
6.4 High
有个链接,点击后弹出新的页面,然而这个新的页面直接按照Low的方法就可以注入
用1'
错误返回不包含有价值信息,用1"
会过滤掉
sqlmap:sqlmap -u "exercise.hun/dvwa/vulnerabilities/sqli/session-input.php#" --cookie="security=high; PHPSESSID=6qo1h0q2k95bit6j6ki1u5e8o5" --second-url="http://exercise.hun/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit"id=1&Submit=Submit
6.5 防御
- 参数化SQL语句,预编译
- 白名单
7 SQL盲注
7.1 目标和工具
目标与回显注入是一样的,只要找到注入点就行
工具:hackbar、sqlmap
7.2 Low
- 输入1,只显示
exist
1' and '1'='1
存在,1' and '1'='2
不存在,说明输入全部被执行,注入点存在
手工:
- 手工的话,首先得试出来SQL语句有几列内容。
1' order by 1
,二分法依次改为10,5,3,2,最终1' order by 2
存在,3则不存在,确定select后面要跟2个参数 - 可以验证一下,
' union select sleep(3)#
是马上刷新页面,而' union select 1,sleep(3)--
基本是等待3秒后刷新 - 先猜数据库长度,
1' union select 1,if(length(database())>100,sleep(3),False)--
- 然后就是体力活猜字母了
1' union select 1,if(ascii(substr(database(),1,1))>100,sleep(3),False)--
7.3 Meidum和High
其实和回显注入都一样的,只不过换成了盲注,手工注入的方法完全改变,用sqlmap的话没差别了
8 XSS-DOM
8.1 原理
目前理解的是,通过url传入payload,从而触发漏洞。和反射性、存储型对比,也可以理解成CS交互,不同是的把JS当成是client,html是server,html解析url时页面JS执行了藏在url中的JS代码
本节基本都是看了代码和help的
8.2 Low
<script>alert(1)</script>
确定注入点存在
8.3 Medium
不能使用包含<script
的任何payload,即要在不使用script的前提下找到执行JS语句的方法
/option></select><img src='x' onerror='alert(1)'>
option可以没有,看html代码可知需要先闭合<select>
,然后正常添加一个<img>
8.4 High
搞不定,看了php代码,只有4个白名单是合法的。
加个#
号就可以,#/option></select><img src='x' onerror='alert(1)'>
在url中#
后面的内容不会发送到server,因而无法阻止运行
8.5 防御
客户端保护,JS使用decodeURI(),payload直接被解码,和正常输入拼接后输出,无法运行
9 XSS-Reflected
9.1 思路和工具
反射型是输入传输到服务器,由服务器进行解析后再返回
9.2 Low
看到输入框,直接试<script>alert(1)</script>
抓包可以看到响应的html代码中直接输出了用户的输入
9.3 Medium
- 直接输入
<script>alert(1)</script>
不成功,抓包看到<script>
标签被过滤了
- 尝试修改标签大小写,成功,抓包看到输入被完全输出
9.4 High
- 这次
<script>alert(1)</script>
不管怎么变,输出的只有一个>
。说明script
标签被彻底过滤了,也就是不能直接执行脚本 - 绕过使用
script
,尝试另一种较常见的<img>
标签,<img src='x' onerror="alert(1)">
,成功 - 阅读php源码,过滤所有
<script
的变形
9.5 防御
使用htmlspecialchars函数将<
和>
转换为实体字符,而不是html元素
10 XSS-Stored
10.1
存储型是利用漏洞将恶意代码保存在服务器端,每次客户端请求包含而已代码的页面时就执行该代码
10.2 Low
评论区输入<script>alert(1)</script>
成功
10.3 Medium
- 照样输入,发现
<script>
被完整过滤掉了 - 尝试大小写
script
,依旧过滤 - 尝试
<img src="x" onerror="alert(1)">
,整个标签都过滤掉了,怀疑是会过滤<
到>
的所有内容 - 前面都只看到了message,现在注意到name也是个文本输入框,尝试输入发现是有长度限制的
- 开Burp,抓包发送到repeater,在txtName后输入
<sCRIPt>alert(1)</ScriPt>
,发送后查看respond里对应位置代码是完整的,在浏览器里刷新页面成功弹窗。
10.4 High
- 前面的方法都试过,
script
被过滤干净了 - 在name中使用
<img>
标签成功
10.5 防御
- 过滤
<
、>
等脚本常用符号,使用php的htmlspecialchars()函数可过滤<
、>
、&
、"
、'
z转换为实体,而不是符号。同时'
也可以用mysql_real_escape_string()转义 - 用户输入:在服务端设置白名单过滤
- 服务端输出:输出至html中就进行编码,输出至JS中就进行转义
htmlspecialchars()中过滤引号时要设置flag参数
- ENT_COMPAT - 默认。仅编码双引号。
- ENT_QUOTES - 编码双引号和单引号。
- ENT_NOQUOTES - 不编码任何引号
11 Insecure CAPTCHA
不安全的验证
要求注册获取key,该模块没办法学习
12 CSP Bypass
12.1 思路
内容安全策略,支持CSP的浏览器会根据用户设置的内容来源、格式甚至值进行过滤。因为需要用户自行设置,存在被绕过的风险。
12.2 Low
提示检查CSP的设置,然后输入带有script的外部资源。F12到网络选项卡,然后刷新页面。
可以看到CSP允许同源脚本以及几个URL:script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;
这里我已经知道https://pastebin.com
是个剪贴板网站,可以为输入的内容生成链接,这里可以直接在网站输入alert(1)
,生成链接粘贴到dvwa中就可以看到效果
12.3 Medium
题目变成了"不管输入什么都会直接显示在页面中",输入TEST
效果如下:
照例先检查下CSP设置
允许同源脚本,同时允许unsafe-inline
,但要求有个随机数属性。script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';
这里我是不懂的,但看了提示。可以多试几次,瞧瞧这个随机数是个什么情况,结果当然它是一个固定值。
Payload:<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert('1')</script>
输入后点include
就OK了
也可以hackbar修改post
12.4 High
这里题目变了,只要点一个键就会显示加法的计算结果,但是它提示了可以去读../..//vulnerabilities/csp/source/jsonp.php
的代码。目前代码阅读不知道对不对。
- 先看看CSP配置,只剩下
script-src 'self';
也就是说只能执行同源脚本 - 反正提示了路径,直接去文件夹打开代码了。第一次学习的时候记得应该可以利用文件包含的,但是第二次发现不行了,不知是不是我打开的方式不对。总的来讲,
jsonp.php
的逻辑就是获得callback
传来的东西再输出到页面。outp是answer对应15,最后一句的结果的是solveSum(json_encode(“answer”:“15”)),也就是说在此处才是调用了solveSum函数 High.js
:这个可以直接看,右键网页源代码,漏洞代码区就能看到high.js
点击即可。一旦监听到点击按键,就生成一个script,把solveSum函数传给callback。solveSum函数的逻辑就是检查answer
在不在传来的参数里,如果在,就把answer
对应的东西赋值给网页中id是answer
的元素,也就是<span>
OK,阅读完毕,现在看来能让我下手的地方就是传给callback的参数,只要修改它就成。在这里我又一次打开Burpsuite,为了看到效果,直接在proxy模块操作,简单修改callback参数后点intercept is on
关掉代理就可以看到效果了。
另外一种方法,在中级时通过hackbar其实是看到post data里有个include,这里如果忘记了尝试也没关系,因为我们会读php源代码,可以看到这一题还是接受include参数的,只不过直接从hackbar里看不到。题目只能执行同源脚本,因此直接上<script>alert(1)</script>
是不行的,需要在script
中执行src
最终payload:include=<script src="source/jsonp.php?callback=alert(1);"></script>
12. 5 防御
别给用户修改参数的机会!callback不需要传参,直接写死成solveSum
13 JavaScript
13.1 思路和工具
该模块的本意是熟悉JS常见的位置、形式、保护机制以及在浏览器中使用和操作JS,以绕过保护。
较繁琐的办法:看到JS代码后,可通过另存修改后执行,获取到正确的token,然后Burp抓包修改token。
可选的:Burpsuite
最佳实践:F12即可,在控制台执行JS函数;在线反混淆
13.2 Low
- 打开JS模块,看到只要提交
success
就可以win,这太简单了。输入success
,点击提交,成功获得返回消息Invalid token.
,那么第一条tip就是找到这个token
- 右键查看网页html源代码,找到token和phrase位置
<form name="low_js" method="post">
<input type="hidden" name="token" value="" id="token" />
<label for="phrase">Phrase</label> <input type="text" name="phrase" value="ChangeMe" id="phrase" />
<input type="submit" id="send" name="send" value="Submit" />
</form>
- 右键查看元素,找到token和phrase位置。可以看到token有值,为了验证我重载入了dvwa,这里依旧是有值的,可以确定这是
ChangMe
的对应token
- 寻找计算
token
的方法,继续右键查看源代码。看到在html代码中包含了完整的JS脚本,先获取phrase
处的字符,计算其md5赋给token
。 - 利用。保持查看元素,先在
phrase
输入success
,然后到控制台下直接执行generate_token()
,再回到查看器,可以看到token
的值变了,此时点提交就成功了。
13.3 Medium
- 查看元素找到token位置为空。
- 查看源代码,看到JS代码只有一行链接
<script src="/vulnerabilities/javascript/source/medium.js"></script>
,且直接点击会返回404 - 使用完整路径查看
http://exercise.hun/dvwa/vulnerabilities/javascript/source/medium.js
function do_something(e)
{
for(var t="",n=e.length-1;n>=0;n--)
t+=e[n];
return t
}
setTimeout(function(){do_elsesomething("XX")},300);
function do_elsesomething(e)
{
document.getElementById("token").value=do_something(e+document.getElementById("phrase").value+"XX")
}
- setTimeout在300毫秒后执行do_elsesomething(“XX”),token等于do_something(XXsuccessXX),结果是其倒序排列。算到这里抓包修改似乎很简单了,但实际中这么简单的结果应该很少见。
- 老样子移步控制台,执行
do_elsesomething("XX")
提示未定义,仔细检查发现medium.js
的路径是http://exercise.hun/vulnerabilities/javascript/source/medium.js
,实际应该是http://exercise.hun/dvwa/vulnerabilities/javascript/source/medium.js
,少个dvwa
。这是自己的锅,因为在phpstudy中网站的默认路径就没有dvwa。 - 手动在控制台加载
medium.js
:
var script = document.createElement('script');
script.src = "http://exercise.hun/dvwa/vulnerabilities/javascript/source/medium.js";
document.getElementsByTagName('head')[0].appendChild(script);
- 先输入
success
,执行do_elsesomething("XX")
后回到查看器,看到token
已经修改了,提交就好了。
当然一些复杂情况,在弄清逻辑后也可以直接用控制台执行代码,算出token
,然后抓包修改。
function do_something(e){
for(var t="",n=e.length-1;n>=0;n--)t+=e[n];
return t
}
alert(do_something("XX"+'success'+"XX"));
13.4 High
- 什么都不做了,直接查看html源代码,找到JS位置访问。看到乱糟糟的一团,百度后得知需要反混淆,并找到JS脚本反混淆网站:http://deobfuscatejavascript.com/#。完整代码实在太长,前面部分都是HASH,直接看后面就好了。核心代码如下:
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);
- 代码逻辑:300毫秒后执行
token_part_2
,点击后执行token_part_3
,执行token_part_1
,其中do_something()反转phrase。也就是说在正常时会先默认phrase为空,然后执行token_part_1
、token_part_2
,最后提交执行token_part_3
。 - 老办法先载入
high.js
,然后输入success
,依次执行token_part_1("ABCD", 44)
、token_part_2("XX")
,提交成功。
13.5 防御
据impossible等级介绍,根本无需担心
你根本不能信任用户,必须假设任何发送给用户的代码都可以被修改或绕过。根本不存在绝对安全的级别。
DVWA暂时就是这样了,剩下的时间里会针对练习中很多一知半解的知识进行深入,目标是搞透原理和本质。