速さこそ正義

調べたことを書いていきます。

ksnctf #3 Crawling Chaos

前回に引き続きCTFの問題を解いていきます。
www.cyamax.com

問題

ksnctf.sweetduet.info 100pt f:id:cyamax:20170820195840p:plain

適当に入力すると以下のようにでます。 f:id:cyamax:20170820191632p:plain

解き方

ソースコードを見てみる。 f:id:cyamax:20170820191819p:plain

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がゲットできます。

f:id:cyamax:20170820205822p:plain

その他

記号だけでJavascriptが動く理由。
http://perl-users.jp/articles/advent-calendar/2010/sym/3

エンコードの説明。
http://d.hatena.ne.jp/kusano_k/touch/20120421/1335006525