ksnctf #3 Crawling Chaos
前回に引き続きCTFの問題を解いていきます。
問題
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の整数から文字を求める
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode
実装
問題のソースを参考に以下のように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