【(」・ω・)」うー!(/・ω・)/にゃー!】 ksnctfのCrawling Chaosを解いてみた

こんにちは。
私にはセキュリティの知識は全くと言っていいほど無いのですが、

今日突然SECCONに参加する予定の友達に、
「この問題、JavaScriptなんだけど、解ける?」

と聞かれたので、見てみました。
問題は、ksnctfというサービスの、Crawling Chaosという問題です。

問題はここに書いてあります。
ページを開くと、

あるのは入力フォームと送信ボタンだけ。あとは、

<script>(ᒧᆞωᆞ)=(/ᆞωᆞ/),(ᒧᆞωᆞ).ᒧうー=-!!(/ᆞωᆞ/).にゃー,(〳ᆞωᆞ)=(ᒧᆞωᆞ),(〳ᆞωᆞ).〳にゃー=- -!(ᒧᆞωᆞ).ᒧうー,(ᒧᆞωᆞ).ᒧうーー=(〳ᆞωᆞ).〳にゃー…</script>

(」・ω・)」うー!(/・ω・)/にゃー

…は?

この問題を解いてみました。

このサービスの問題の解き方

このサービスは、

主にコンピュータセキュリティに関する問題を出題します。各問題からFLAG_123456xyzという形式の答え(Flag)を探してください。Twitterでログインすると、ランキングに参加できます。

とトップページにあるように、
問題から、FLAG_*****という文字列を何らかの方法で取得し、
解答ページに送信するという形式のようです。

先ほどの文字列から、答えが何なのか全く想像がつきません。笑

なんだこの文字列は

※長いし貼っても意味が分からないので、コードの全文は問題を読んでみて下さい。

まず前提として、このうーにゃーjsは、有効なJavaScriptです。
jsでは、マルチバイト文字を変数名などの識別子として利用できます。

例:

var あいうえお配列 = "あいうえお".split("");

あいうえお配列.join("") === "あいうえお"; // true

とこんな感じで、さも当たり前のように全角の文字も扱うことができます。

とりあえずminifyされてて見難いので、カンマごとに改行してみました。
すると、37行目が、異様に長いです。

見てみると、うーにゃー達を+で連結しています。
ここで何らかの文字列を生成しているんだろうなぁ、と予想したものの、

まだ全く答えの想像がつきません。
そもそも顔文字が多すぎてコードを読む気すら起きません

偶然の発見

こういう問題は初めて挑んだので、気持ち悪いし解けないしでヤケになり
そのコードをjsファイルとして保存して、Nodejsから実行してみました。

jQueryっぽい文字列

? ? ? ? ? ?

なぜでしょう、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コード文字列)()と実行するんだと思われます。 無駄な努力し過ぎだろ…

コードさえ見つけてしまえば、問題が解ける可能性は一気に高まります。
こいつを読んでみます。

デコードする

このコードは、

  1. formタグがsubmitされたら、
  2. pという正解文字列を表す配列と照合して
  3. 正解なら"(」・ω・)」うー!(/・ω・)/にゃー!"
  4. 不正解なら"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);

こいつを問題ページのフォームに貼り付けてみると、

うーにゃー

答えは、ここに書いてしまうと面白くなさそうなので、
この問題に興味を持った方↑のコードを実行してみて下さい。