【Javascript】サイトのファビコンURLを取得する

f:id:cyamax:20171203112012p:plain

概要

ファビコン(favicon)とは

www.webword.jp

やり方

想定環境

  • javascript
  • favicon画像のurlは<HEAD>配下に<link rel="shortcut icon" href="アイコンのURI" >として書かれている
  • 以下コードを取得したいサイトで実行するとファビコンを取得できる

    コード

//ファビコンURL取得
function favi() {
  //favicon画像のurlはhtml内のhead->linkタグの属性relがshortcut iconのhref値に記載されている
  for (var i = 0;i < document.head.getElementsByTagName('link').length ;i++){
    if (document.head.getElementsByTagName('link')[i].getAttribute('rel') === "shortcut icon"){
      var favicon_url = document.head.getElementsByTagName('link')[i].getAttribute('href');
      break;
    } else {
      var favicon_url = '';
      console.log("取得NG");
    }
  };
console.log(favicon_url);
};

その他

URLが相対パスの場合は少し工夫する必要あり。
chrome extensionなどjavascriptを後から挿入する場合に使える。
chrome apiでもっと簡単に取れる方法があるなら教えて欲しい。

ログインシェルで遊ぶ

FTPの設定をしたときに、
/usr/sbin/nologinについて調べたら色々面白かったので簡単に実験してみた。

/usr/sbin/nologinは/etc/nologin.txtを読み込む

らしい。読み込んで表示した後はexitされる。
しかし/etc/nologin.txtなどというファイルは存在しなかった。

vogel.at.webry.info

なので試しに、自分でファイルを作成してみた。

f:id:cyamax:20171117000030p:plain

そしてログインしてみる。

f:id:cyamax:20171117000056p:plain

期待した通りのことができた。
これでサーバに不正にログインしようとした人を「ドキッ!」とさせることができる。

ログインシェルを別のコマンドに変える

ログインシェルは/etc/passwdに書かれたものが実行される特性を利用して、
別のコマンドを起動してみた。

ログインシェルを /usr/bin/wに変更(ログインユーザをみるコマンド)。

f:id:cyamax:20171117000713p:plain

/usr/bin/wが実行され、ログインユーザが表示された。

ログインシェルを空白にする

本来は記載されているログインシェルを空白にしてみた。
するとshが起動した。これは意外。
なのでログインシェルを起動させたくない場合は、/usr/sbin/nologin/usr/bin/falseをちゃんと明記した方が良いということがわかった。

感想

ログインシェル面白い。
自分でコンパイルした実行ファイルなどを指定したらもっと面白いことができそう。

ksnctf #35 Simple Auth II

問題

ksnctf.sweetduet.info

f:id:cyamax:20170917180043p:plain

解き方

ソースコードが見れるので確認する。

PHPのソースコードはよくわからないが、以下のような記載があった。

$db = new PDO('sqlite:database.db');

DBの場所が記載されているので、URLから直接ダウンロードしてみる。

ttp://ctfq.sweetduet.info:10080/~q35/database.db

DBの中身を確認

SQLiteのDBのようなので接続してみる。

# Macの場合
~ ❯❯❯ sqlite3 Downloads/database.db                                                                ⏎
SQLite version 3.16.0 2016-11-04 19:09:39
Enter ".help" for usage hints.
sqlite> .tables
user   user2
sqlite> select * from user;
root|FLAG_*****************←ここにFLAG
sqlite> select * from user2;
root|GLDmNFJimveAAxyg_wSNp
sqlite>

その他

SQLインジェクションの問題だと思ったら違った。

参考

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

  • 作者: 碓井利宣,竹迫良範,廣田一貴,保要隆明,前田優人,美濃圭佑,三村聡志,八木橋優,SECCON実行委員会
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2015/09/30
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (4件) を見る

セキュリティコンテストのためのCTF問題集

セキュリティコンテストのためのCTF問題集

  • 作者: 清水祐太郎,竹迫良範,新穂隼人,長谷川千広,廣田一貴,保要隆明,美濃圭佑,三村聡志,森田浩平,八木橋優,渡部裕,SECCON実行委員会
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2017/07/28
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (1件) を見る

闇のゲームを攻略した

序章

帰宅すると嫁がテレビに向かって無表情でリモコンの"決定"ボタンを連打してた。

何事かと思ったら、期間限定でABC朝日放送※のデータ放送(dボタン)を見ると、ABCeeiSLOTとというミニゲームができるようだった。
※関西のテレビ局

ミニゲームでコインを1000枚集めるとQUOカード一年分ゲットに応募できるチャンスがもらえるらしい。

嫁はスロットが当たるたびに、「やった」と小さくつぶやくと、また無表情でボタンを連打した。

嫁はテレビ番組を見ていなかった。 テレビ局の狙いはゲームをやりながら番組を見てもらい、視聴率やファンを増やすことだったと思うが、私や嫁はもともとABC朝日放送は好きなのでミニゲームがなくても見ている。今の嫁は番組を見ていないのでマイナス効果である。

妻は長時間ボタンを連打したため少し疲れていた。

私は妻の精神的な負担と、全国のリモコンの決定ボタンの塗装が心配になった。

おそらく他のご家庭でも同じ問題が発生しているだろう。

1000コインという頑張れば到達できそうな数字は実は最低でも3時間以上は連打しないといけないゲームだなんて…

これは闇のゲームだ…。

私はエンジニアの端くれとして、この闇のゲームを攻略し、関西の家庭の平和を守るべく立ち上がった。

技術章

リモコンから出ている赤外線を別の装置から連続して送信し続ける装置
=全自動「決定ボタン」連打機を作ることにした。

キャプチャ編

テレビの「決定ボタン」の赤外線信号がどんなものを送っているかわからないため、赤外線受光器を使ってキャプチャし、その信号を送信に使う。

赤外線受光器はこんなこともあろうかと半年前に秋月で20円で購入したものを使った。

電気回路の制御にはArduinoを使った。

【永久保証付き】Arduino Uno

【永久保証付き】Arduino Uno

Arduinoをはじめよう 第3版 (Make:PROJECTS)

Arduinoをはじめよう 第3版 (Make:PROJECTS)

これも5年前に自由研究用に買ったもの。 いまだにたまに使うので重宝する。

赤外線の受信方法は以下サイトを参考にした。

deviceplus.jp

しかし以下のようなコンパイルエラーが出る。

receve:49: error: 'class decode_results' has no member named 'panasonicAddress'
 Serial.print(results->panasonicAddress,HEX);
                       ^
exit status 1
'class decode_results' has no member named 'panasonicAddress'

panasonicAddress関数が見つからないと怒られるので、 以下のように書き換えてその場を凌ぐ。

//IRremoteライブラリのサンプルプログラム
//// ~~~~ 47行目から
else if (results->decode_type == PANASONIC) { 
Serial.print("Decoded PANASONIC - Address: ");
//Serial.print(results->panasonicAddress,HEX); ←エラーがでるのでコメントアウト
Serial.print(HEX); //←受信結果を16進数でとりあえず表示
Serial.print(" Value: ");
////~~~~

送信編

赤外線は赤外線用LEDから送る。 電気回路の制御もArduinoを使う。

詰まったところ

①回路がわからない

送信サンプルコードを見ると、Arduinoのどのピンから電流を流すのか指定していなかった。
上部で指定しているライブラリ(IRremote.h)の中に記載されていると思い、 ライブラリコードの中を30分は迷走したがよくわからない。
こんな簡単なこともできないのかと自分に絶望していると、サンプルコードに

* An IR LED must be connected to Arduino PWM pin 3.

と書いてあった。あほすぎる。。。

②IRsend関数がわからない

Arduinoには赤外線用のライブラリ(IRremote.h)があり、それを使うと簡単に送信できるようだが、
中で定義されてる関数の意味がわからずエラー連発だった。

最終的には Sonyのリモコンでの電源オフ信号を送信するサンプルを参考にPanasonic用の関数(irsend.sendPanasonic)を見つける
Panasonic用の関数が含まれるサンプルコードを漁る。
サンプルコードからPanasonic共通のPanasonicAddress値を見つける。
上記値とサンプリングした受信信号(HEX)を関数に入れる。 これでようやく動いた。

完成品

コード

#include <IRremote.h>

IRsend irsend;

void setup()
{  
}

void loop() {
  irsend.sendPanasonic(0x4004, 0x1009293); //決定ボタン
   delay(2000);
}

今回はわざわざ赤外線を物理的にキャプチャしたけれども、
ボタンの情報はここに載っていた。

memento mori: Arduinoと赤外線LEDでPanasonicのテレビをリモコン操作できた

回路

やっていることはLチカとかわらない。
回路はすごくシンプル。

終章

仕事から帰って作業に取りかかり3時間かけて完成した。 時計は深夜の1時を回っていた。

装置を使う直前は293コインであり、3時間ほど妻が連打した成果がそこにあった。

1000コインまでどんだけ道のりは長いんだ、、、。
もしかしたらこの装置を使っても時間内に1000コイン貯まらないかもしれない。
そう思いながら完成した回路をテレビの前にセットし、私は疲れて寝てしまった。




そして次の日テレビを見ると…





























やりすぎた。

余談

どんだけポイントをゲットしても応募できるチャンスは各賞一人一回まででだった。

しかしこれで関西の家庭からボタンの連打音をなくし、笑顔と会話を取り戻せたのならよしとする。

ksnctf #12 Hypertext Preprocessor

問題

ksnctf.sweetduet.info

f:id:cyamax:20170904234250p:plain
ページを開いただけではよくわからない。

解き方

とりあえずグーグル先生に出てきた数字を聞いてみる。
すると徳丸先生の脆弱性情報が出てきた。

blog.tokumaru.org

なるほど。数字の先頭の2012:1823はCVE(脆弱性番号)で、 phpの脆弱性らしい。

試しに、URLのクエリ文字列に「?-s」をつけるとソースコードが出てきた。 http://ctfq.sweetduet.info:10080/~q12/?-s

ソースコードを見ると丁寧に「// Flag is in this directory.」と書かれているので、 次はOSインジェクションを試してみる。

「-d」をつけると任意のPHPコードが実行できるようなので、 参考サイトみながらちょろっとコードを書いて実行してみたらフラグをゲットできた。

# bash
echo "<?php system('ls -al'); exit(); ?>" | POST "http://ctfq.sweetduet.info:10080/~q12/?-d+allow_url_include%3d1+-d+auto_prepend_file%3dphp://input"

その他

初めはBurpを使ってクエリを書き換えていたが、bashから実行する方法がネットに載っていたので最終的にbashで答えを見つけた。
burpの使い方は慣れていないので練習が必要。

PHPの知識がなくても簡単にOSインジェクションができてしまったが、 PHPの勉強もしたい。

参考

CGI版PHPにリモートからスクリプト実行を許す脆弱性(CVE-2012-1823) | 徳丸浩の日記
CGI版PHPに対する魔法少女アパッチマギカ攻撃を観測しました | 徳丸浩の日記
https://pentesterlab.com/exercises/cve-2012-1823/course

ksnctf #17 Math II

問題

ksnctf.sweetduet.info xを満たすyがFLAGらしい。

解き方

問題を見たときはモジュロ演算なんて言葉は知らなかったけど、取りあえず累乗根を単純に計算するだけだと思った。

ささっと計算してみたらオーバーフローしてうまく動かない。
多分これは私の書き方とかそもそも論で何か間違っているのだろう。

    y = pow (x,(1.00/101.00))
OverflowError: long int too large to convert to float

どうすればいいのか調べていたら二分探索で解を得るのがベターらしい。

二分探索をやってみる

二分探索はここを参考にした。 codezine.jp

二分探索と聞いたときは「基本情報でなんか見たかも」ぐらいな感じで、使ったことは一回もなかった。

# python3
import math

x = ここに問題文の値を入れる

y_min = 0
y_max = 10 ** 300 #ここは適当
y_jou = 101

for num in range(0,2000): # 2000も適当
  mid = ( y_min + y_max ) // 2
  dy = pow (mid , y_jou)
  if dy == x:
    print (mid)
    break
  elif dy > x:
    y_max = mid
    print ("大きすぎ")
  elif dy < x:
    y_min = mid
    print ("小さすぎ")
  keta = int(math.log10(mid)+1)
  print (str(num) + ": " + str(keta) + "桁")

なんとなくやっていることを理解して、雰囲気で実装してみたけど一応答えはでた。

その他

問題に書かれていたモジュロ演算はRSA暗号で使われているらしい。
※RSA暗号は公開鍵暗号の一つ

参考

はやわかり RSA
法とモジュロ – まいとう情報通信研究会
RSA暗号

Pythonスタートブック

Pythonスタートブック

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

アセンブラ・アセンブリ・アセンブルの違い

f:id:cyamax:20170813122622p:plain コンピュータの世界における アセンブラ・アセンブリ・アセンブルがややこしいのでまとめました。

まとめ

荒っぽくまとめると以下の通り。
アセンブラ・・・変換するソフト
アセンブリ・・・言語
アセンブル・・・バイナリに変換すること

アセンブラ(assembler)

アセンブリ言語で書いたプログラムを
機械語に変換するプログラム(ソフト)のこと。

英語ではassemblerなので、
assemble(変換)+er(者)で変換をしてくれる者のこと。

アセンブリ(assembly)

機械語と一対一の関係にある低水準言語のこと。
アセンブリ言語(アセンブラ言語)と呼ばれる。 ※反対に、高水準言語でC言語やPythonなどがある

言語自体は MOV AX , FFなどと書かれている。
このままでは実行できない。

アセンブル(assemble)

アセンブリをバイナリファイルに変換(コンパイルとも言う)すること。

アセンブリ言語MOV AX , FF ー変換(アセンブル)→ B8FF00バイナリ

となる。

※引用 アセンブリとは

参考

アセンブルとは?アセンブリは言語名でアセンブラは機械語に変換する作業のことらし... - Yahoo!知恵袋

アセンブリとは

stringsでなぜprintfが見えるのか

Linuxへの理解を深めるため、最近CTFの問題を解いています。

CTFの回答を見ていると実行ファイル(バイナリ)に対してstringsコマンドを実行して、その中にpritnfがあるから書式文字列攻撃を試して・・・と書いてありました。
全く意味がわからなかったので、調べたことのメモになります。

そもそもstringsとは

Linuxのコマンド 。 指定したファイル中の表示可能な文字列を表示します。 バイナリファイルやデータファイルの内容を判断するために利用します。

strings実行例

実行するとHello world!と表示されるファイルを用意します。

$ ./main
Hello world!

この実行ファイルをstirngsで見てみます。
※stringsはデフォルトではファイルの先頭しか見ないので、オプションの-をつけることでファイル全体から文字を検索します。

$ strings - main
__PAGEZERO
__TEXT
__text
__TEXT
__stubs
__TEXT
__stub_helper
__TEXT
__cstring
__TEXT
__unwind_info
__TEXT
__DATA
__nl_symbol_ptr
__DATA
__la_symbol_ptr
__DATA
__LINKEDIT
/usr/lib/dyld
/usr/lib/libSystem.B.dylib
Hello world!
@dyld_stub_binder
@_printf
_mh_execute_header
!main
__mh_execute_header
_main
_printf
dyld_stub_binder

stringsは実行ファイル中に存在する文字だけを抽出してくれるのがメリットです。
※lessやcatで実行ファイルを見てもノイズが多くて見にくい

実行ファイル(バイナリ)って変数名とか見れないんじゃないの?

私の認識では高級言語のソースコードをコンパイルしたら変数名や関数は機械語に変換されるため、stringsコマンドでバイナリを見てももちろんソースコードは出てこないし、関数名・変数も機械の都合の良い名前に変換されていると思っていました。
なので例えばint hoge=1111なんてコードを書いても、コンパイル後はintはアセンブリ言語のMOVかなにかに変換されて、変数名hogeも別の何かに変換されて跡形もなくなると思っていました。

なのでstringsコマンドで実行ファイルを覗いても、ソースコードの関数は見えないはず(=printfは出てこない)では?と思い混乱しました。

実際にコンパイルしてみた

やってみないとわからないので、
試しに以下のコードを書いてコンパイルしてみました。

C言語でテスト

/* test.c */
#include <stdio.h>
int test(){
  int hensu = 1111;
  printf("変数の中身は%dです\n",hensu);
  return 0;
} 

int main(){
  printf("Hello world!\n");
  test();
  return 0;
}

上記コードを保存(test.cなど)して以下コマンドでオブジェクトファイルの作成をします。
gcc -o test.c

するとtest.oが生成されるので、今回はこれをstringsで覗きます。

strings結果

MacBook-Pro:Desktop takanori$ strings - test2.o
__text
__TEXT
__cstring
__TEXT
__compact_unwind__LD
__eh_frame
__TEXT
Hello world!
_test
_main
_printf

たしかにprintfが拾えてる!

まとめ

上記の検証から 変数名hensuはコンパイル後stringsで見えない。
関数名main,test,printfは見えることがわかった。

どうやら変数名(hensu)や既存の関数(intなど)はコンパイルされると跡形もなくなりますが、自分で作った関数main,testや外部の関数printfはそのまま変換されずに残るようです。
ただし残るといっても関数名が残っているだけで、関数内部の処理はコンパイル済みであり、あくまでも関数の名前の部分だけがそのまま残るみたいです。

ここまで調べるのに結構時間がかかりましたが、最後まとめているときに逆コンパイルについてまとめているサイトを見つけました。これを初めから見ていえればこんなに時間がかからなかったのに・・・ とはいえ、今までわかっていなかったコンパイルの流れや実行ファイルの仕組みを理解できたのは非常に有益でした。
特にコンパイルには広義・狭義の意味があったり、アセンブラ・アセンブリ・アセンブルの違いなども調べていくうちに理解できたので、興味がある人は調べてみてはいかがでしょうか。

参考書籍

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

  • 作者: 碓井利宣,竹迫良範,廣田一貴,保要隆明,前田優人,美濃圭佑,三村聡志,八木橋優,SECCON実行委員会
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2015/09/30
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (4件) を見る

セキュリティコンテストのためのCTF問題集

セキュリティコンテストのためのCTF問題集

  • 作者: 清水祐太郎,竹迫良範,新穂隼人,長谷川千広,廣田一貴,保要隆明,美濃圭佑,三村聡志,森田浩平,八木橋優,渡部裕,SECCON実行委員会
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2017/07/28
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログ (1件) を見る

参考サイト

C言語がコンパイルされて実行可能になるまでの流れ - にょきにょきブログ

Cの逆コンパイラはどこまで実現可能か,Javaはなぜ逆コンパイルされやすいのか?

ksnctf #2 Easy Cipher

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

問題

ksnctf.sweetduet.info 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先生に「アルファベット 暗号化」で聞いてみます。
すると「シーザー暗号」という暗号化方法が上位に出てきました。

f:id:cyamax:20170803231253p:plain

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

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

多言語シーザー暗号(Universal Caesar cipher)

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

コードで解く

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

qiita.com

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

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」

ROT13 - Wikipedia

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