こんにちは。
私にはセキュリティの知識は全くと言っていいほど無いのですが、
今日突然SECCONに参加する予定の友達に、
「この問題、JavaScriptなんだけど、解ける?」
と聞かれたので、見てみました。
問題は、ksnctfというサービスの、Crawling Chaosという問題です。
問題はここに書いてあります。
ページを開くと、
あるのは入力フォームと送信ボタンだけ。あとは、
<script>(ᒧᆞωᆞ)=(/ᆞωᆞ/),(ᒧᆞωᆞ).ᒧうー=-!!(/ᆞωᆞ/).にゃー,(〳ᆞωᆞ)=(ᒧᆞωᆞ),(〳ᆞωᆞ).〳にゃー=- -!(ᒧᆞωᆞ).ᒧうー,(ᒧᆞωᆞ).ᒧうーー=(〳ᆞωᆞ).〳にゃー…</script>
(」・ω・)」うー!(/・ω・)/にゃー
…は?
この問題を解いてみました。
このサービスは、
主にコンピュータセキュリティに関する問題を出題します。各問題からFLAG_123456xyzという形式の答え(Flag)を探してください。Twitterでログインすると、ランキングに参加できます。
とトップページにあるように、
問題から、FLAG_*****
という文字列を何らかの方法で取得し、
解答ページに送信するという形式のようです。
先ほどの文字列から、答えが何なのか全く想像がつきません。笑
※長いし貼っても意味が分からないので、コードの全文は問題を読んでみて下さい。
まず前提として、このうーにゃーjsは、有効なJavaScriptです。
jsでは、マルチバイト文字を変数名などの識別子として利用できます。
例:
var あいうえお配列 = "あいうえお".split("");
あいうえお配列.join("") === "あいうえお"; // true
とこんな感じで、さも当たり前のように全角の文字も扱うことができます。
とりあえずminifyされてて見難いので、カンマごとに改行してみました。
すると、37行目が、異様に長いです。
見てみると、うーにゃー達を+
で連結しています。
ここで何らかの文字列を生成しているんだろうなぁ、と予想したものの、
まだ全く答えの想像がつきません。
そもそも顔文字が多すぎてコードを読む気すら起きません。
こういう問題は初めて挑んだので、気持ち悪いし解けないしでヤケになり
そのコードをjsファイルとして保存して、Nodejsから実行してみました。
? ? ? ? ? ?
なぜでしょう、jQueryっぽい文字列が出てきています。
ログには$ is not defined
と出ています。
jQueryを読み込んでないので実行が止まったようです。
Nodejsありがとう。ありがとう。
コードの断片が出てきたということは、
おそらくやたら長い37行目は、jsコードを生成しているようです。
うーにゃーjsを下から順にダンプしていったら、見つけました。
カンマ区切りでいうところの、41行目です。
$(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;});});
こんな文字列が出てきます。
おそらく、この文字列をFunction(jsコード文字列)()
と実行するんだと思われます。 無駄な努力し過ぎだろ…
コードさえ見つけてしまえば、問題が解ける可能性は一気に高まります。
こいつを読んでみます。
このコードは、
form
タグがsubmitされたら、p
という正解文字列を表す配列と照合して"(」・ω・)」うー!(/・ω・)/にゃー!"
"No"
とアラートで出すという動作をしています。
正解の文字列を得るには、p
が何を表しているのかさえわかれば良さそうなので、
配列pとその扱い方の部分だけを抜き出してみます。
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); for(var i=0; i<p.length; i++) if(t.charCodeAt(i) * (i+1) != p[i]) f = false;
tはフォームのinputタグに入力した文字列です。
tを1文字ずつASCIIコード化して、i+1を乗算した文字列が、答えなようです。
なら逆に、pをi+1で除算し、ASCIIコードから文字列化すれば答えになるはずです。
for分の中身を変えて確認してみます。
var str = ""; for(var i=0; i<p.length; i++) str += String.fromCharCode(p[i] / (i+1)); console.log(str);
こいつを問題ページのフォームに貼り付けてみると、
答えは、ここに書いてしまうと面白くなさそうなので、
この問題に興味を持った方↑のコードを実行してみて下さい。