DVWA靶场——JavaScript Attacks(前端攻击)
JavaScript Attacks表面上是一个修改"短语(phrase)
你输入一段文字(比如ChangeMe)——点击 Submit——页面显示"你的短语已更新"
但其真正目的在于告诉我们"把所有安全逻辑都放在前端 JavaScript 里"是多么危险的事情
一个正常的 Web 应用,修改用户数据时,应该由服务器端完成验证,浏览器只负责提交和展示
low
源码分析
<?php $page['body'] .= <<<EOF <script> /* MD5 code from here https://github.com/blueimp/JavaScript-MD5 */ !function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function r(n,t){return n<<t|n>>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<<r%32,n[14+(r+64>>>9<<4)]=r;var e,i,a,d,h,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e<n.length;e+=16)i=l,a=g,d=v,h=m,g=f(g=f(g=f(g=f(g=c(g=c(g=c(g=c(g=u(g=u(g=u(g=u(g=o(g=o(g=o(g=o(g,v=o(v,m=o(m,l=o(l,g,v,m,n[e],7,-680876936),g,v,n[e+1],12,-389564586),l,g,n[e+2],17,606105819),m,l,n[e+3],22,-1044525330),v=o(v,m=o(m,l=o(l,g,v,m,n[e+4],7,-176418897),g,v,n[e+5],12,1200080426),l,g,n[e+6],17,-1473231341),m,l,n[e+7],22,-45705983),v=o(v,m=o(m,l=o(l,g,v,m,n[e+8],7,1770035416),g,v,n[e+9],12,-1958414417),l,g,n[e+10],17,-42063),m,l,n[e+11],22,-1990404162),v=o(v,m=o(m,l=o(l,g,v,m,n[e+12],7,1804603682),g,v,n[e+13],12,-40341101),l,g,n[e+14],17,-1502002290),m,l,n[e+15],22,1236535329),v=u(v,m=u(m,l=u(l,g,v,m,n[e+1],5,-165796510),g,v,n[e+6],9,-1069501632),l,g,n[e+11],14,643717713),m,l,n[e],20,-373897302),v=u(v,m=u(m,l=u(l,g,v,m,n[e+5],5,-701558691),g,v,n[e+10],9,38016083),l,g,n[e+15],14,-660478335),m,l,n[e+4],20,-405537848),v=u(v,m=u(m,l=u(l,g,v,m,n[e+9],5,568446438),g,v,n[e+14],9,-1019803690),l,g,n[e+3],14,-187363961),m,l,n[e+8],20,1163531501),v=u(v,m=u(m,l=u(l,g,v,m,n[e+13],5,-1444681467),g,v,n[e+2],9,-51403784),l,g,n[e+7],14,1735328473),m,l,n[e+12],20,-1926607734),v=c(v,m=c(m,l=c(l,g,v,m,n[e+5],4,-378558),g,v,n[e+8],11,-2022574463),l,g,n[e+11],16,1839030562),m,l,n[e+14],23,-35309556),v=c(v,m=c(m,l=c(l,g,v,m,n[e+1],4,-1530992060),g,v,n[e+4],11,1272893353),l,g,n[e+7],16,-155497632),m,l,n[e+10],23,-1094730640),v=c(v,m=c(m,l=c(l,g,v,m,n[e+13],4,681279174),g,v,n[e],11,-358537222),l,g,n[e+3],16,-722521979),m,l,n[e+6],23,76029189),v=c(v,m=c(m,l=c(l,g,v,m,n[e+9],4,-640364487),g,v,n[e+12],11,-421815835),l,g,n[e+15],16,530742520),m,l,n[e+2],23,-995338651),v=f(v,m=f(m,l=f(l,g,v,m,n[e],6,-198630844),g,v,n[e+7],10,1126891415),l,g,n[e+14],15,-1416354905),m,l,n[e+5],21,-57434055),v=f(v,m=f(m,l=f(l,g,v,m,n[e+12],6,1700485571),g,v,n[e+3],10,-1894986606),l,g,n[e+10],15,-1051523),m,l,n[e+1],21,-2054922799),v=f(v,m=f(m,l=f(l,g,v,m,n[e+8],6,1873313359),g,v,n[e+15],10,-30611744),l,g,n[e+6],15,-1560198380),m,l,n[e+13],21,1309151649),v=f(v,m=f(m,l=f(l,g,v,m,n[e+4],6,-145523070),g,v,n[e+11],10,-1120210379),l,g,n[e+2],15,718787259),m,l,n[e+9],21,-343485551),l=t(l,i),g=t(g,a),v=t(v,d),m=t(m,h);return[l,g,v,m]}function a(n){var t,r="",e=32*n.length;for(t=0;t<e;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function d(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;var e=8*n.length;for(t=0;t<e;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function h(n){return a(i(d(n),8*n.length))}function l(n,t){var r,e,o=d(n),u=[],c=[];for(u[15]=c[15]=void 0,o.length>16&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(d(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="";for(r=0;r<n.length;r+=1)t=n.charCodeAt(r),e+="0123456789abcdef".charAt(t>>>4&15)+"0123456789abcdef".charAt(15&t);return e}function v(n){return unescape(encodeURIComponent(n))}function m(n){return h(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this); function rot13(inp) { return inp.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);}); } function generate_token() { var phrase = document.getElementById("phrase").value; document.getElementById("token").value = md5(rot13(phrase)); } generate_token(); </script> EOF;- 从页面中获取
id="phrase"的输入框的值(用户输入的短语) - 对该值进行 ROT13 编码
- 再对 ROT13 的结果进行 MD5 哈希
- 将最终结果赋值给
id="token"的隐藏字段
关键点
generate_token()在页面加载时立即执行(最后一行),这意味着Token的值完全由用户输入的phrase决定
其核心问题就在于
Token的生成算法完全暴露在客户端token === MD5(ROT13(phrase)),且依赖于用户输入的phrase,因此任何用户都可以为自己想要的任何phrase生成一个"合法"的Token
提示我们提交单词 "success" 即可获胜
根据我们刚刚的分析
- Token 是由
md5(rot13(phrase))计算得出的 - 服务器验证的就是这个公式
- 需要提交
phrase=success和对应的正确Token
第一步:计算 ROT13
"success" 经过 ROT13 编码: s → f u → h c → p c → p e → r s → f s → f 结果: "fhpprff"第二步:计算 MD5
对"fhpprff"进行 MD5 哈希。
使用浏览器开发者工具(F12 → Console):
md5("fhpprff")
第三步:构造请求
开发者工具里,改变token和phrase的值分别为 value="38581812b435834ebf84ebcc2c6424d6" ,value="success" 点击submit
提示我们成功了
Medium
源码分析
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") }1.do_something()函数 - 字符串反转
- 输入:
"hello" - 输出:
"olleh"
2.setTimeout()- 延迟执行,等待 300毫秒 后,调用do_elsesomething("XX")
3.do_elsesomething()- Token生成核心,
- 参数
e的值是"XX" - 获取输入框
phrase的值 - 拼接字符串:
"XX" + phrase + "XX" - 调用
do_something()进行反转 - 将反转后的结果赋给
token字段
开发者可能认为做了以下"加强":
- 增加了盐值(Salt):
"XX"+ phrase +"XX"
- 看起来像是加了密钥,让Token不那么容易被猜中
- 增加了延迟执行:
setTimeout()让代码不那么直观
- 可能认为这样能阻止自动化脚本
- 改变了算法:从MD5改为字符串反转
- 认为换算法能迷惑攻击者
实际情况是
- 算法仍然完全暴露在客户端
- 所谓的"盐值"是硬编码的
- 反转算法是可逆的
- 延迟执行可以用调试器跳过
漏洞利用
第一步:计算正确的Token
对于 phrase = "success": token = reverse("XX" + "success" + "XX") = reverse("XXsuccessXX") = "XXsseccusXX"第二步:构造攻击请求
我们依旧直接改变表单元素
没毛,注入成功了
high
源码分析
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);跟前两个等级差不多,依次执行
第1步:token_part_1()
// 反转 phrase token = do_something("success") = "sseccus"第2步:token_part_2("XX")
// 拼接 + SHA256 拼接 = "XX" + "sseccus" = "XXsseccus" token = sha256("XXsseccus")第3步:用户点击Submit →token_part_3()
// 获取当前token,拼接"ZZ",再SHA256 拼接 = token + "ZZ" = sha256("XXsseccus") + "ZZ" token = sha256(拼接)最终Token:
text
最终Token =sha256( sha256("XX" + reverse(phrase)) + "ZZ" )
其问题:
算法仍然完全在客户端,盐值是硬编码的,可以手动调用函数,可以绕过延迟
所以利用漏洞也是同样的流程
第一步:计算token
第二步:构造攻击请求
impossible
你永远不能信任任何来自用户的东西,也无法阻止用户篡改它,因此不存在"不可能"级别
DVWA靶场的最后一个关卡在屏幕上弹出"Success"提示的那一刻,我知道这段短暂却充实的Web安全探索之旅迎来了尾声。回首这段学习的日日夜夜,从最初面对漏洞时的茫然无措,到如今能够从容分析代码、构造攻击载荷并提出防御方案,其中的每一次突破都离不开许多人的帮助与支持
首先,我要由衷感谢DVWA(Damn Vulnerable Web Application)项目及其开发者团队。这个由Digininja发起并持续维护的开源项目,以其精心设计的漏洞环境和循序渐进的四级难度体系,为我这样的Web安全初学者搭建了一座从理论通往实践的金色桥梁。从Low级别将安全逻辑赤裸裸地暴露在客户端,到Impossible级别将验证逻辑深植服务器端,DVWA不仅是漏洞的陈列馆,更是一部活生生的安全思想演进史。正是这个"在危险中学习"的平台,让我深刻理解了"永远不要信任用户输入"这条Web安全的金科玉律
同时,我要特别感谢互联网上无数开源的网络安全社区与知识分享者。Burp Suite的官方文档、各大安全论坛中前辈们的实战经验与踩坑记录,以及GitHub上数不清的Payload集合与绕过技巧——这些散落在数字世界各处的智慧碎片,在我最需要指引的时刻点亮了前路。感谢那些素未谋面却毫不吝啬分享知识的博主与安全从业者,是你们的无私筑就了后来者学习的阶梯。
我也要感谢这段学习过程中遇到的每一个"漏洞"本身。SQL注入教会我,最危险的往往不是复杂的攻击手法,而是一个未被转义的单引号;XSS向我证明,安全防御不能止步于输入过滤,输出编码同样不可或缺;而JavaScript Attacks模块则用最简单的方式展示了那个最深刻的道理——所有摆在客户端的安全措施,都不过是防君子不防小人的装饰品。感谢这些漏洞以最生动的方式为我上了一堂堂安全之课。
我要感谢未来的自己和所有正在学习安全的朋友们。DVWA只是万里长征的第一步,真正安全的系统需要在实战中千锤百炼,真正的安全工程师需要在攻防对抗中持续成长。我将带着这段学习经历沉淀下的技术、思维与敬畏之心,继续在这个充满挑战与魅力的领域前行。
愿我们都能用技术让这个世界变得更加安全
peace and love
