何もできなかったSECCONオンライン予選

つまり力不足・努力不足でろくに解けなかったってこと.

※この記事は岩手県立大学AdventCalender12日目の記事です.
qiita.com


SECCONとは

ハック力を競うCTFと呼ばれる日本のセキュリティコンテストの一つ.
暗号分野やネットワーク、バイナリ等様々なジャンルの問題が出題される.
昨年も説明したので今年は簡単に.CTFについては以下のスライドに詳しくある.

www.slideshare.net

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桁まで求めることができる.

f:id:tanakaxa:20161211173614p:plain

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;
}
実行結果
root@kali:~/SECCON# gcc decode.c -lcrypto
root@kali:~/SECCON# time ./a.out
8桁目   A
8桁目   B
8桁目   C
8桁目   D
8桁目   E
prain   :SECCON{ABABABCDEDEFGHIJJKLMNOPQRSTTUVWXYYZ}
key     :VIGENERECODE

real    0m55.584s
user    0m55.576s
sys     0m0.000s

5年前のThinkPadなvSphereサーバ上のVMでも,1分かからずに平文(FLAG)と鍵が見つかった.

VoIP(Forensics 100) - 解けなかった

問題文

Extract a voice.
The flag format is SECCON{[A-Z0-9]}.
[添付]voip.pcap

概要

VoIP(Voice Over IP)の音声のやり取りをキャプチャしたpcap.ここからFLAGを探し出す.

Wiresharkで開いて音声を聞く

WiresharkにはキャプチャしたパケットからVoIP通話を再生する機能がある.
f:id:tanakaxa:20161211185950p:plain

f:id:tanakaxa:20161211190005p:plain

あとは再生すると、「S・E・C・C・O・N・{」とFLAGが流れ始めるので聞き取るだけ…



聞き取るだけだけど聞き取れなかった.
答えは SECCON{9001IVR} らしい.
IとRとか、VとBの聞きわけができず、あれやこれやと試していたが…
9の部分はIかRだと思い込んでいたために解くことができなかった.

英語ダメなエンジニア絶対殺す問題だったので、私の英語力のなさを棚に上げてDislikeした.
f:id:tanakaxa:20161211191435p:plain

おわりに

昨年から何も成長していない.むしろ退化している.
最近はネットワークとかOSの仕事とか趣味ばかりをしていただけだけか、プログラミングも忘れてしまい、ggらないとコードが書けないインフラエンジニアになってしまった.

また、英語力はやはり必要だと強く感じた.
私は「翻訳技術が進歩してるし英語いらないでしょ」派だったが、聞き取れずにFLAGを取れなかったのは大変悔しかった.

来年はインフラエンジニアとしてCCNA取得を目標に勉強していこうと思っていたが、プログラミングと英語も勉強しなくてはと感じた年末だった.

おまけ 岩手県立大学Advent Calenderに思うこと

f:id:tanakaxa:20161211204445p:plain
明日は○○さんよろしくって書こうと思ったら誰もいなかった.
ソフトウェア情報学部が各学年100名以上いるのに空きがあるのは寂しい…
Qiitaとはいえ他社ブログに書けばプログラミングとは関係ない話でも全然OKだったはずなので、現役の皆さんは後輩・知人に「なんか書けばいいよ」と声をかけて広めていこう.