何もできなかったSECCONオンライン予選
解けなさすぎてZガンダムの腕が完成した pic.twitter.com/hiJJfB260F
— 田中ァ (@tanakaxa) 2016年12月11日
つまり力不足・努力不足でろくに解けなかったってこと.
※この記事は岩手県立大学AdventCalender12日目の記事です.
qiita.com
SECCONとは
ハック力を競うCTFと呼ばれる日本のセキュリティコンテストの一つ.
暗号分野やネットワーク、バイナリ等様々なジャンルの問題が出題される.
昨年も説明したので今年は簡単に.CTFについては以下のスライドに詳しくある.
Vigenere(Crypto 100)
問題文
k: ???????????? p: SECCON{???????????????????????????????????} c: LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ k=key, p=plain, c=cipher, md5(p)=f528a6ab914c1ecf856a1d93103948fe |ABCDEFGHIJKLMNOPQRSTUVWXYZ{} -+---------------------------- A|ABCDEFGHIJKLMNOPQRSTUVWXYZ{} B|BCDEFGHIJKLMNOPQRSTUVWXYZ{}A C|CDEFGHIJKLMNOPQRSTUVWXYZ{}AB D|DEFGHIJKLMNOPQRSTUVWXYZ{}ABC E|EFGHIJKLMNOPQRSTUVWXYZ{}ABCD F|FGHIJKLMNOPQRSTUVWXYZ{}ABCDE G|GHIJKLMNOPQRSTUVWXYZ{}ABCDEF H|HIJKLMNOPQRSTUVWXYZ{}ABCDEFG I|IJKLMNOPQRSTUVWXYZ{}ABCDEFGH J|JKLMNOPQRSTUVWXYZ{}ABCDEFGHI K|KLMNOPQRSTUVWXYZ{}ABCDEFGHIJ L|LMNOPQRSTUVWXYZ{}ABCDEFGHIJK M|MNOPQRSTUVWXYZ{}ABCDEFGHIJKL N|NOPQRSTUVWXYZ{}ABCDEFGHIJKLM O|OPQRSTUVWXYZ{}ABCDEFGHIJKLMN P|PQRSTUVWXYZ{}ABCDEFGHIJKLMNO Q|QRSTUVWXYZ{}ABCDEFGHIJKLMNOP R|RSTUVWXYZ{}ABCDEFGHIJKLMNOPQ S|STUVWXYZ{}ABCDEFGHIJKLMNOPQR T|TUVWXYZ{}ABCDEFGHIJKLMNOPQRS U|UVWXYZ{}ABCDEFGHIJKLMNOPQRST V|VWXYZ{}ABCDEFGHIJKLMNOPQRSTU W|WXYZ{}ABCDEFGHIJKLMNOPQRSTUV X|XYZ{}ABCDEFGHIJKLMNOPQRSTUVW Y|YZ{}ABCDEFGHIJKLMNOPQRSTUVWX Z|Z{}ABCDEFGHIJKLMNOPQRSTUVWXY {|{}ABCDEFGHIJKLMNOPQRSTUVWXYZ }|}ABCDEFGHIJKLMNOPQRSTUVWXYZ{ Vigenere cipher https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher
概要
ヴィジュネル暗号 - Wikipedia
近世のヨーロッパでフランスの外交官が考えた暗号.
問題文下部の表から,平文と鍵が交差するところにある文字を暗号とするというもの.
暗号の詳細はWikipediaで.
この問題は,暗号文と平文の一部と,各文の文字数が与えられており,そこからmd5ハッシュが一致する平文を求めるというものだと考えられる.
7桁目までの鍵を求める
平文が先頭7桁まで公開されているため,ここから鍵を7桁まで求めることができる.
7桁なのでコードを書くまでもないんだけど目grepがつらかったので書いた.
間違いなく書くより目で追った方が早かった.
コード
#include<stdio.h> #include<string.h> char str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{}"; // 平文と暗号文から鍵を探す関数 char keysearch(char plain, char ciper) { char key; // 平文行のリストを生成 int plain_cou; for (plain_cou = 0; plain != str[plain_cou]; plain_cou++) { } char plain_list [strlen(str + 1)]; int plain_list_cou; for (plain_list_cou = 0; plain_list_cou < strlen(str); plain_list_cou++) { if(plain_list_cou + plain_cou < strlen(str)) { plain_list[plain_list_cou] = str[plain_list_cou + plain_cou]; } else { plain_list[plain_list_cou] = str[plain_list_cou + plain_cou - strlen(str)]; } } plain_list[plain_list_cou] = '\0'; // 鍵を見つける int ciper_cou; for (ciper_cou = 0; ciper != plain_list[ciper_cou]; ciper_cou++) { } key = str[ciper_cou]; return key; } int main() { char plain_text[] ="SECCON{"; char ciper_text[] ="LMIG}RP"; // 平文と暗号文から1文字ずつ鍵を抽出する for (int i = 0; i < strlen(plain_text); i++) { printf("%c", keysearch(plain_text[i], ciper_text[i])); } printf("\n"); return 0; }
実行結果
# gcc keysearch.c # ./a.out VIGENER
総当たりする
残りの5桁が分からないが
28文字^5 = 17,210,368通り
しかないので総当たりする.
考え方は鍵検索と同じで鍵配列を作り暗号文から平文を探していき,該当のmd5ハッシュが見つかるまで順番に5桁を変えながらループ.
コード
#include<stdio.h> #include<string.h> #include<openssl/md5.h> char str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{}"; // 鍵と暗号文でデコード char decode(char key, char ciper) { char plain; // 鍵行のリストを生成 int key_cou; for (key_cou = 0; key != str[key_cou]; key_cou++) { } char key_list [strlen(str + 1)]; int key_list_cou; for (key_list_cou = 0; key_list_cou < strlen(str); key_list_cou++) { if(key_list_cou + key_cou < strlen(str)) { key_list[key_list_cou] = str[key_list_cou + key_cou]; } else { key_list[key_list_cou] = str[key_list_cou + key_cou - strlen(str)]; } } key_list[key_list_cou] = '\0'; // 平文を見つける int ciper_cou; for (ciper_cou = 0; ciper != key_list[ciper_cou]; ciper_cou++) { } plain = str[ciper_cou]; return plain; } // md5を計算して該当のFLAGか確認する int md5check(char *prain) { char answer_md5[] = "f528a6ab914c1ecf856a1d93103948fe"; MD5_CTX c; unsigned char md[MD5_DIGEST_LENGTH]; char md5_str[33]; int i; MD5_Init(&c); MD5_Update(&c, prain, strlen(prain)); MD5_Final(md, &c); for(i = 0; i < 16; i++) { sprintf(&md5_str[i * 2], "%02x", (unsigned int)md[i]); } if(strcmp(md5_str, answer_md5) == 0) { return 0; } return 1; } int main() { char ciper_text[] = "LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ"; char key_text[] = "VIGENERxxxxx"; char prain_text[44] = ""; int i; int key_num7; int key_num8; int key_num9; int key_num10; int key_num11; // ブルートフォース for (key_num7 = 0; key_num7 < strlen(str); key_num7++) { printf("8桁目\t%c\n", str[key_num7]); key_text[7] = str[key_num7]; for (key_num8 = 0; key_num8 < strlen(str); key_num8++) { key_text[8] = str[key_num8]; for (key_num9 = 0; key_num9 < strlen(str); key_num9++) { key_text[9] = str[key_num9]; for (key_num10 = 0; key_num10 < strlen(str); key_num10++) { key_text[10] = str[key_num10]; for (key_num11 = 0; key_num11 < strlen(str); key_num11++) { key_text[11] = str[key_num11]; // 復号 for(i = 0; i < strlen(ciper_text); i++) { prain_text[i] = decode(key_text[i%12], ciper_text[i]); } prain_text[i] = '\0'; // md5を確認 if (md5check(prain_text) == 0) { printf("prain\t:%s\n", prain_text); printf("key\t:%s\n", key_text); goto OUT; } } } } } } OUT: return 0; }
VoIP(Forensics 100) - 解けなかった
問題文
Extract a voice. The flag format is SECCON{[A-Z0-9]}. [添付]voip.pcap
概要
VoIP(Voice Over IP)の音声のやり取りをキャプチャしたpcap.ここからFLAGを探し出す.
おわりに
昨年から何も成長していない.むしろ退化している.
最近はネットワークとかOSの仕事とか趣味ばかりをしていただけだけか、プログラミングも忘れてしまい、ggらないとコードが書けないインフラエンジニアになってしまった.
また、英語力はやはり必要だと強く感じた.
私は「翻訳技術が進歩してるし英語いらないでしょ」派だったが、聞き取れずにFLAGを取れなかったのは大変悔しかった.
来年はインフラエンジニアとしてCCNA取得を目標に勉強していこうと思っていたが、プログラミングと英語も勉強しなくてはと感じた年末だった.
おまけ 岩手県立大学Advent Calenderに思うこと
明日は○○さんよろしくって書こうと思ったら誰もいなかった.
ソフトウェア情報学部が各学年100名以上いるのに空きがあるのは寂しい…
Qiitaとはいえ他社ブログに書けばプログラミングとは関係ない話でも全然OKだったはずなので、現役の皆さんは後輩・知人に「なんか書けばいいよ」と声をかけて広めていこう.