functionh(s) { for (i = 0; i != s.length; i++) { a = ((typeof a == 'undefined' ? 1 : a) + ord(str(s[i]))) % 65521; b = ((typeof b == 'undefined' ? 0 : b) + a) % 65521 } return chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF) }
functionc(a, b, c) { for (i = 0; i != a.length; i++) c = (c || '') + chr(ord(str(a[i])) ^ ord(str(b[i % b.length]))); return c } for (a = 0; a != 1000; a++) debugger; x = h(str(x)); source = /Ӈ#7ùª9¨M¤À.áÔ¥6¦¨¹.ÿÓÂ.Ö£JºÓ¹WþÊmãÖÚG¤ ¢dÈ9&òªћ#³1᧨/; source.toString = function() { return c(source, x) }; try { console.log('debug', source); with(source) returneval('eval(c(source,x))') } catch (e) {} }
首先是创建了ord,chr,str这三个函数,分别对应charCodeAt,fromCharCode,String函数.接着又创建了h(),c()函数,其中h()函数可以大致判断为hash一类的函数,c()函数作用类似,接着是一个for循环包裹的debugger反调试,在浏览器中进行调试时,只需要在console里输入a=999即可进行绕过。接着是对x的处理,这里我观察到x并未声明,但是使用Vscode可以分析出x为 function x(x:any):any ,即处理任意输入的x函数。
english dictionary: { "lang": "en", "translate": "Translate", "not_found": "I don't know that word, sorry.", "in_lang_query_is_spelled": "In french, {{userQuery}} is spelled .", "input_query": "What French word do you want to translate into English? A few examples: informatique en nuage, téléverser "," title ":" Translation utility for technical terms in French and English "," subtitle ":" In the current pluri - polarized great powers context, internationalization is the key for good trans - border understanding between tech workers. "," informatique en nuage ":" cloud computing "," mot - dièse ":" hashtag "," courriel ":" email "," téléverser ":" upload "," ordiphone ":" smartphone "," original_word ":" Word to translate "," a ":" a "}
french数据格式化为:
1 2 3 4 5 6 7 8 9 10 11 12
french dictionary: { "lang": "fr", "translate": "Traduire", "not_found": "Je ne connais pas ce mot, désolé.", "in_lang_query_is_spelled": "En francais, {{userQuery}} s'écrit .", "input_query": "Quel mot anglais voulez-vous traduire en français ? Quelques exemples: cloud computing, to upload "," title ":" Application de traduction de termes techniques entre français et anglais "," subtitle ":" Dans notre contexte actuel de multipolarisation des puissances,internationalisation est critique au bon entendement transfrontalier des travailleurs des TIC.","upload":"téléverser","cloud computing":"informatique en nuage","hashtag":"mot-dièse","email":"courriel","original_word":"Mot à traduire","{{1+1}}test":"asx"}
在switch分支上的else if (msg[0] != '/')判断会将用户的输入向所有用户广播。
而catchat.js中有一句非常关键:
1 2 3 4
if (msg.match(/dog/i)) { send("/ban ${name}"); send("As I said, d*g talk will not be tolerated."); }
可以看到admin用户会在ban这个操作中将用户名直接代入执行,因此这里可能就是攻击点。
以这个为基础,构造的攻击思路为:
用户A邀请用户B到当前房间
用户A发表dog言论
用户B举报A
Admin进入房间并执行ban操作
观察CSP规则,基本是无法适用script标签进行绕过了。
但是对于CSS是没有太多限制的,ban对应的代码为:
1 2 3 4 5 6 7 8 9
ban(data) { if (data.name == localStorage.name) { document.cookie = 'banned=1; Path=/'; sse.close(); display('You have been banned and from now on won't be able to receive and send messages.'); } else { display("${esc(data.name)} was banned.<style>span[data-name^=${esc(data.name)}] { color: red; }</style>"); } },
functionhandle(data) { ({ undefined(data) {}, error(data) { display("Something went wrong :/ Check the console for error message."); console.error(data); }, name(data) { display("${esc(data.old)} is now known as ${esc(data.name)}"); }, rename(data) { localStorage.name = data.name; }, secret(data) { display("Successfully changed secret to <span data-secret="${esc(cookie('flag'))}">*****</span>"); }, msg(data) { let you = (data.name == localStorage.name) ? ' (you)' : ''; if (!you && data.msg == 'Hi all') send('Hi'); display("<span data-name="${esc(data.name)}">${esc(data.name)}${you}</span>: <span>${esc(data.msg)}</span>"); }, ban(data) { if (data.name == localStorage.name) { document.cookie = 'banned=1; Path=/'; sse.close(); display("You have been banned and from now on won't be able to receive and send messages."); } else { display("${esc(data.name)} was banned.<style>span[data-name^=${esc(data.name)}] { color: red; }</style>"); } }, })[data.type](data); }
可以看到这一句:
1
secret(data) { display("Successfully changed secret to <span data-secret="${esc(cookie('flag'))}">*****</span>"); }
同时,根据Computer too slow? Try it on our i386 beowulf cluster.可以推测出我们的计算式在被发送后会被解析,这也就意味着vars.pi这一类的变量会被解析。
那么下一步的思路就是:通过构造vars,使得服务端解析计算式时触发xss
我们可以在页面中发现计算器的源码,其中涉及到vars以及expr构造的代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
functionp(a, b) { a = String(a).toLowerCase(); b = String(b); if (!/^(?:[\(\)\*\/\+%\-0-9 ]|\bvars\b|[.]\w+)*$/.test(a)) throwError(a); b = JSON.parse(b, function(a, b) { if (b && "object" === typeof b && !Array.isArray(b)) returnObject.assign(Object.create(null), b); if ("number" === typeof b) return b }); return (newFunction("vars", "return " + a))(b) }