[CpawCTF] 第二夜: Q6 古典的暗号
Q6.[Crypto] Classical Cipher
CpawCTF Level1 Q6 (通し番号は 6 だけど 第二問目)
以下、問題文
Q6.[Crypto] Classical Cipher
10pt
暗号には大きく分けて、古典暗号と現代暗号の2種類があります。
特に古典暗号では、古代ローマの軍事的指導者ガイウス・ユリウス・カエサル(英語読みでシーザー)が初めて使ったことから、名称がついたシーザー暗号が有名です。これは3文字分アルファベットをずらすという単一換字式暗号の一つです。次の暗号文は、このシーザー暗号を用いて暗号化しました。暗号文を解読してフラグを手にいれましょう。暗号文: fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu}
答案
問題文を通読すると、以下の情報が手に入りました。
- 暗号文:
fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu}
- この暗号文は、「シーザー暗号」を用いて暗号化しているということ。
- 「シーザー暗号」は「3文字分アルファベットをずらす」という単一換字式暗号により暗号化を行なっているということ。
さて、暗号文を手作業で (頭の中でカウントして) 3文字分だけずらしても良いと思いますが、一応プログラムを書く技能を持っているので、ウォーミング・アップがてら、プログラムを組んでみましょう。
復号プログラム
#include <stdio.h> #include <string.h> int main() { char cip[] = "fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu}"; int l; l = strlen(cip); printf("Cipher: %s (length: %d)\n", cip, l); for (int i=0; i<l; i++) { if ( ('A' <= cip[i] && cip[i] <= 'Z') || ('a' <= cip[i] && cip[i] <= 'z') ) { //cip[i] += 3; cip[i] -= 3; } } l = strlen(cip); printf("Decryption: %s (length: %d)\n", cip, l); return 0; }
一応、解説がてら、以下にメモを残しておきます。
標準入出力を扱うため stdio.h
を、文字列を扱いやすくするため string.h
を include
しています。
cip[] = "暗号文"
の部分は文字列リテラルを用いて、暗号文を変数として定義しています。
数えるのが面倒なので、配列のサイズは省略 (自動決定)
文字列リテラルの扱いに関しては こちら を参考にさせていただきました。
そしてデバッグのために、保険で文字数を strlen()
関数を使ってカウントしておきます。( 参考 )
for 文
により、cip
の各文字に対して処理を行います。
その最中、if 文
により「アルファベットに対してのみ」3文字ズラす対象とするようにし、最後に復号した暗号を出力します。
さて、アルファベットの部分を捉えることができたら、これを 3文字だけずらします。
最初、3文字後ろ ( cip[i] += 3
)に動かしていたのですが、意味がわからない文が出てきまして、試しに3文字前 ( cip[i] -= 3
) に動かしたら、以下の意味を持つような文字列が得られました。
後から気軽にやり直せるのは、プログラムのいいところだと思います。
Cipher: fsdz{Fdhvdu_flskhu_lv_fodvvlfdo_flskhu} (length: 39) Decryption: cpaw{Caesar_cipher_is_classical_cipher} (length: 39)
cpaw{Caesar_cipher_is_classical_cipher}
がどうやら Flag
のようです。
「シーザー暗号は古典暗号」の意
ちなみに Cipher
は「暗号」, Decryption
は「復号」です。
submit
Flag
をゲットしたので、cpawctf のサイトに提出します。
無事、クリアできたようです。
どうでも良いことなのですが
前回の Test problem は 7000番台でしたよね。 今回は 5900 近くということで、1000人くらいは登録して、テストだけして、って感じなのでしょうか。
私もよく三日坊主するので、お気持ちはよくわかります。(早すぎる共感)
まとめ
久しぶりに C言語でプログラムを (しかも文字列を扱う) 書いたので、思ったよりコストがかかってしまいました。
いい勉強になったと思います。
研究の分野柄 python
を書くことが多いのですが、C
を書くと型の硬さや、ビルトインの関数, ヘッダの include
などにおっとっととなりますね。
ところで私は、暗号といえば、サマーウォーズを思い出します。
「骨折り損のくたびれもうけ」でしたっけ。暗号を考える人はとてもユーモアに飛んでいると思います。
次の問題は、拡張子の特定のようです。それでは、また。
Links
別解
暗号文を格納した変数 cip
のうち、アルファベットを捉える if 文
に関して、以下のような書き方をしても動作することを確認しています。
あらかじめ文字列の中に出てくるアルファベット以外を、自分の目でみて、除外する方針です。
for (int i=0; i<l; i++) { if ( (cip[i] == '{') || (cip[i] == '}') || (cip[i] == '_')){ continue; } else { //cip[i] += 3; cip[i] -= 3; }