ksnctf #3 Crawling Chaos
前回に引き続きCTFの問題を解いていきます。
www.cyamax.com
問題
ksnctf.sweetduet.info 100pt
適当に入力すると以下のようにでます。
解き方
ソースコードを見てみる。
Chromeのデベロッパーモードでソースコードを見てみると、headのscript内に謎のコードを発見。
console.logを見てもエラーが発生していないので、正しく認識されている。
これを解析する必要がある。
node.jsで開いてみる。
デベロッパーツールのままでは調査しづらいので、
「(ᒧᆞωᆞ).ᒧうーー=(〳ᆞωᆞ).〳にゃーー」のスクリプトを別ファイルに切り出してnode unya.js
として実行してみた。
すると
undefined:2 $(function(){$("form").submit(function(){var t=$('input[type="text"]').val();var p=Array(70,152,195,284,475,612,791,896,810,850,737,1332,1469,1120,1470,832,1785,2196,1520,1480,1449);var f=false;if(p.length==t.length){f=true;for(var i=0;i<p.length;i++)if(t.charCodeAt(i)*(i+1)!=p[i])f=false;if(f)alert("(」・ω・)」うー!(/・ω・)/にゃー!");}if(!f)alert("No");return false;});}); ^ ReferenceError: $ is not defined at eval (eval at <anonymous> (/Users/takanori/Desktop/jquerry/u.js:1:17299), <anonymous>:2:1) at Object.<anonymous> (/Users/takanori/Desktop/jquerry/u.js:1:17333) at Module._compile (module.js:569:30) at Object.Module._extensions..js (module.js:580:10) at Module.load (module.js:503:32) at tryModuleLoad (module.js:466:12) at Function.Module._load (module.js:458:3) at Function.Module.runMain (module.js:605:10) at startup (bootstrap_node.js:158:16) at bootstrap_node.js:575:3
と表示された。
$ is not defined
についてはJQueryを読み込んでいないのでエラーが出たが、
それ以外の部分はソースがデコードされた内容で表示された。
ソースを解析する
上記でデコードされた内容を見やすく改行すると以下のようになった。
$(function () { $("form").submit(function () { var t = $('input[type="text"]').val(); var p = Array(70, 152, 195, 284, 475, 612, 791, 896, 810, 850, 737, 1332, 1469, 1120, 1470, 832, 1785, 2196, 1520, 1480, 1449); var f = false; if (p.length == t.length) { f = true; for (var i = 0; i < p.length; i++) if (t.charCodeAt(i) * (i + 1) != p[i]) f = false; if (f) alert("(」・ω・)」うー!(/・ω・)/にゃー!"); } if (!f) alert("No"); return false; }); });
そして一番大事なのは
if (t.charCodeAt(i) * (i + 1) != p[i]) f = false;
の部分。
入力された文字(t)の左からi番目の文字をUTF-16の整数に変換(charCodeAt)して、
その変換した数字に*(i + 1)した値がp[i]と一致しなかった場合にfalseを返すというコードになっていました。
正しい値(FLAG)をフォーム(t)に入れれば
if (f) alert("(」・ω・)」うー!(/・ω・)/にゃー!");
が実行されます。
つまりtの値を求めれば、それすなわちFLAGになるので、
pの値からtを求めます。
t.charCodeAt(i) * (i + 1) != p[i]
より
t[i] = String.fromCharCode( p[i] / ( i +1 ) )
で求めることができそうです。
※String.fromCharCodeはcharCodeAtの逆。UTF-16の整数から文字を求める String.fromCharCode() - JavaScript | MDN
実装
問題のソースを参考に以下のようにJavascriptを書きました。
// javascript flag.js var flag = ""; var o = ''; var p = Array(70, 152, 195, 284, 475, 612, 791, 896, 810, 850, 737, 1332, 1469, 1120, 1470, 832, 1785, 2196, 1520, 1480, 1449); for (var i = 0; i < p.length; i++) { //pの値から本来入力するべき値を逆算する var o = p[i] / (i + 1); //Strings.fromCode(o)でoの数値をUnicodeの文字に戻す var flag = flag + String.fromCharCode(o); }; console.log(flag);
これをnode flag.js
で実行するとFLAGがゲットできます。
その他
記号だけでJavascriptが動く理由。
http://perl-users.jp/articles/advent-calendar/2010/sym/3
エンコードの説明。
http://d.hatena.ne.jp/kusano_k/touch/20120421/1335006525