スキルアップのためにCTF(キャッチ・ザ・フラグ)にチャレンジしてみました。

問題

50pt

EBG K VVV vf n fvzcyr yrggre fhofgvghgvba pvcure gung ercynprf n yrggre jvgu gur yrggre KVVV yrggref nsgre vg va gur nycunorg. EBG KVVV vf na rknzcyr bs gur Pnrfne pvcure, qrirybcrq va napvrag Ebzr. Synt vf SYNTFjmtkOWFNZdjkkNH. Vafreg na haqrefpber vzzrqvngryl nsgre SYNT.

ただ意味のわからないアルファベットが並んでいますが、
これを各々推測して中に含まれているフラグ(FLAG_******)をゲットするのが問題の意図です。

CTFの問題はバイナリ解析やプログラムの脆弱性を突くものなどもあり、様々なスキルが求められます。

解き方

仮説を立てる

Easy Cipher = 「簡単な暗号」

問題文を見ると文字の区切り方が英文に似ています。
アルファベットを別文字に入れ替えたような暗号と仮定して一旦解いてみます。

vf → is ?
n → a ?
SYNT → FLAG ?

上記の仮定でそれぞれ文字を置換してみます。

実装

置換はjavascriptで実装してみます。

// easy_cipher.js
var i = "EBG KVVV vf n fvzcyr yrggre fhofgvghgvba pvcure gung ercynprf n yrggre jvgu gur yrggre KVVV yrggref nsgre vg va gur nycunorg. EBG KVVV vf na rknzcyr bs gur Pnrfne pvcure, qrirybcrq va napvrag Ebzr. Synt vf SYNTFjmtkOWFNZdjkkNH. Vafreg na haqrefpber vzzrqvngryl nsgre SYNT.";
// まずはSYNT→FLAGにしてみる
i = i.replace(/F/ig,"1");//Fを1に変換。igのiは大文字小文字の区別しない、gは全文字に適応の意味(gがないと一文字置換して終わってしまう)
i = i.replace(/S/ig,"F");
i = i.replace(/L/ig,"2");
i = i.replace(/Y/ig,"L");
i = i.replace(/A/ig,"3");
i = i.replace(/N/ig,"A");
i = i.replace(/G/ig,"4");
i = i.replace(/T/ig, "G");
//S(暗号)→F(復号)など変換をかけるときに、もともとのF(暗号)とF(復号)が文中に混ざってしまうため、一旦数字に置換させています

console.log(i);

これをnode.jsで実行。

$ node easy_cipher.js
EB4 KVVV v1 A 1vzcLr Lr44re 1ho14v4h4vb3 pvcure 4uA4 ercLApr1 A Lr44re jv4u 4ur Lr44re KVVV Lr44re1 AF4re v4 v3 4ur ALcuAor4. EB4 KVVV v1 A3 rkAzcLr bF 4ur PAr1Ae pvcure, qrirLbcrq v3 A3pvr34 Ebzr. FLAG v1 FLAG1jmGkOW1AZdjkkAH. V31re4 A3 h3qre1pber vzzrqvA4rL2 AF4re FLAG.

だんだん英文っぽくなってきました。 このあとも最初の予想通りvf→isn→aでうまく行きそうです。
ただ、このまま一つずつすべてのアルファベットでreplaceを入力していくのは面倒。

もしかして問題はもっと単純でないかと。 アルファベットがそれぞれバラバラに何かの単語に入れ替わったのではなく、 何かの規則性をもって置換されていると推測してみます。

ここでGoogle先生に「アルファベット 暗号化」で聞いてみます。
すると「シーザー暗号」という暗号化方法が上位に出てきました。

SYNT→FLAGの仮定が正しいとするならば、アルファベットを13シフトしたものが復号化となります。

自分でコードを書くのはしんどいので、Google先生に聞いてみると良い感じのサイトがありました。

http://anti.rosx.net/etc/tools/enc_caesar.php

ここで13シフトで復号すると問題の答えが出てきました。
※ただしちゃんと英語を読まないと正解にならない

コードで解く

せっかくなのでJSでシーザー暗号を解けないかと検索したらすでにありました。

上記のサイトを参考に書き直すと

var i = "EBG KVVV vf n fvzcyr yrggre fhofgvghgvba pvcure gung ercynprf n yrggre jvgu gur yrggre KVVV yrggref nsgre vg va gur nycunorg. EBG KVVV vf na rknzcyr bs gur Pnrfne pvcure, qrirybcrq va napvrag Ebzr. Synt vf SYNTFjmtkOWFNZdjkkNH. Vafreg na haqrefpber vzzrqvngryl nsgre SYNT.";
function rot13(str) {
  return str.replace(/[A-Z]/g, (L) => String.fromCharCode(65 + (L.charCodeAt(0) - 65 + 13) % 26));
};
console.log(rot13(i));

こんなに綺麗に書けるんですね。
またシーザー暗号で13文字ずらすのは「ROT13」

https://ja.wikipedia.org/wiki/ROT13

という暗号化のようです。いろいろ勉強になりました。