SECCON 2016 オンライン予選
軽い感じで参加してみました。400 点で提出 930 チーム中 286 位という結果でした。
Vigenere
問題文を読んでヴィジュネル暗号についてググってプログラミングするだけ。
まずSECCON{
に対応するキーの先頭7文字を求めておきます。
するとキーは少なくともVIGENER?????
であることが分かります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env perl
use strict;
use warnings;
use Digest::MD5;
my $md5 = Digest::MD5->new;
my @cipher = split(//,"LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ");
my %dic = (
'A' => 0,
'B' => 1,
'C' => 2,
'D' => 3,
'E' => 4,
'F' => 5,
'G' => 6,
'H' => 7,
'I' => 8,
'J' => 9,
'K' => 10,
'L' => 11,
'M' => 12,
'N' => 13,
'O' => 14,
'P' => 15,
'Q' => 16,
'R' => 17,
'S' => 18,
'T' => 19,
'U' => 20,
'V' => 21,
'W' => 22,
'X' => 23,
'Y' => 24,
'Z' => 25,
'{' => 26,
'}' => 27,
);
my @key = split(//,'VIGENEREAAAA');
my @next = split(//,'BCDEFGHIJKLMNOPQRSTUVWXYZ{}A');
my %table;
my @A = split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZ{}');
$table{'A'} = \@A;
my @B = split(//,'BCDEFGHIJKLMNOPQRSTUVWXYZ{}A');
$table{'B'} = \@B;
my @C = split(//,'CDEFGHIJKLMNOPQRSTUVWXYZ{}AB');
$table{'C'} = \@C;
my @D = split(//,'DEFGHIJKLMNOPQRSTUVWXYZ{}ABC');
$table{'D'} = \@D;
my @E = split(//,'EFGHIJKLMNOPQRSTUVWXYZ{}ABCD');
$table{'E'} = \@E;
my @F = split(//,'FGHIJKLMNOPQRSTUVWXYZ{}ABCDE');
$table{'F'} = \@F;
my @G = split(//,'GHIJKLMNOPQRSTUVWXYZ{}ABCDEF');
$table{'G'} = \@G;
my @H = split(//,'HIJKLMNOPQRSTUVWXYZ{}ABCDEFG');
$table{'H'} = \@H;
my @I = split(//,'IJKLMNOPQRSTUVWXYZ{}ABCDEFGH');
$table{'I'} = \@I;
my @J = split(//,'JKLMNOPQRSTUVWXYZ{}ABCDEFGHI');
$table{'J'} = \@J;
my @K = split(//,'KLMNOPQRSTUVWXYZ{}ABCDEFGHIJ');
$table{'K'} = \@K;
my @L = split(//,'LMNOPQRSTUVWXYZ{}ABCDEFGHIJK');
$table{'L'} = \@L;
my @M = split(//,'MNOPQRSTUVWXYZ{}ABCDEFGHIJKL');
$table{'M'} = \@M;
my @N = split(//,'NOPQRSTUVWXYZ{}ABCDEFGHIJKLM');
$table{'N'} = \@N;
my @O = split(//,'OPQRSTUVWXYZ{}ABCDEFGHIJKLMN');
$table{'O'} = \@O;
my @P = split(//,'PQRSTUVWXYZ{}ABCDEFGHIJKLMNO');
$table{'P'} = \@P;
my @Q = split(//,'QRSTUVWXYZ{}ABCDEFGHIJKLMNOP');
$table{'Q'} = \@Q;
my @R = split(//,'RSTUVWXYZ{}ABCDEFGHIJKLMNOPQ');
$table{'R'} = \@R;
my @S = split(//,'STUVWXYZ{}ABCDEFGHIJKLMNOPQR');
$table{'S'} = \@S;
my @T = split(//,'TUVWXYZ{}ABCDEFGHIJKLMNOPQRS');
$table{'T'} = \@T;
my @U = split(//,'UVWXYZ{}ABCDEFGHIJKLMNOPQRST');
$table{'U'} = \@U;
my @V = split(//,'VWXYZ{}ABCDEFGHIJKLMNOPQRSTU');
$table{'V'} = \@V;
my @W = split(//,'WXYZ{}ABCDEFGHIJKLMNOPQRSTUV');
$table{'W'} = \@W;
my @X = split(//,'XYZ{}ABCDEFGHIJKLMNOPQRSTUVW');
$table{'X'} = \@X;
my @Y = split(//,'YZ{}ABCDEFGHIJKLMNOPQRSTUVWX');
$table{'Y'} = \@Y;
my @Z = split(//,'Z{}ABCDEFGHIJKLMNOPQRSTUVWXY');
$table{'Z'} = \@Z;
my @bra1 = split(//,'{}ABCDEFGHIJKLMNOPQRSTUVWXYZ');
$table{'{'} = \@bra1;
my @bra2 = split(//,'}ABCDEFGHIJKLMNOPQRSTUVWXYZ{');
$table{'}'} = \@bra2;
while(1) {
print join("", @key),"\n";
my $buf = "";
for (my $i = 0; $i < @cipher; $i++) {
my $ni = $i % 12;
my @t = @{$table{$key[$ni]}};
for (my $j = 0; $j < @t; $j++) {
if ($t[$j] eq $cipher[$i]) {
foreach (keys %dic) {
if ($dic{$_} == $j) {
$buf .= $_;
last;
}
}
last;
}
}
}
my $h = $md5->add($buf)->hexdigest;
if ($h eq 'f528a6ab914c1ecf856a1d93103948fe') {
print $buf,"\n";
exit;
}
$key[11] = $next[$dic{$key[11]}];
if ($key[11] eq 'A') {
$key[10] = $next[$dic{$key[10]}];
if ($key[10] eq 'A') {
$key[9] = $next[$dic{$key[9]}];
if ($key[9] eq 'A') {
$key[8] = $next[$dic{$key[8]}];
if ($key[8] eq 'A') {
$key[7] = $next[$dic{$key[7]}];
}
}
}
}
}
我ながら酷いコードですね。
while
文の中がメインの探索部で、最初のfor
で復号、MD5 を取って正解チェック、違ったら次のキーへ、という処理を回しています。単純なブルートフォースです。
最初は完全に確定しているVIGENER
までを固定しておいて回したんですが、終わりそうになかったので推測でVIGENERE
と埋めて回したら1分くらいですぐ見つかりました。
VoIP
とりあえず Wireshark で開いて、全パケットを結合しないといけないの?と思いながらググったところ、Wireshark に VoIP パケットを再生できる機能があることが分かりました。
電話(y)
→VoIP通話(V)
→ストリームを再生
で再生できます。
これで再生はできたんですがシークする機能がないようで、自分の英語力では一発で聞き取れなくてかなり厳しかったので、Soundflower と Audacity でさくっと録音して繰り返し聴いてなんとかなりました。
DTM やっててよかった!あと英語力大事!
Memory Analysis
説明に書いてある通り、ダウンロードしたファイルを Volatility Framework に入れてごちゃごちゃします。
「Volatility Frameworkを使ったメモリフォレンジック」と言うハンズオンに参加させて頂きました。 | Developers.IO
このサイトがめちゃくちゃ参考になりました。
まずimageinfo
でOSを特定し、pstree
で起動しているプロセス一覧を取得、connections
とconnscan
で接続先を取得するところまでは上のサイト通りにやりました。
いまいち正確な解答がわかっていないのですが、
結局hostsを抽出し、IEの履歴を見てそのURLにhostsが適用されているものとしてアクセスする、すなわちhttp://153.127.200.178/entry/Data-Science-import-pandas-as-pd
にアクセスするとフラグが取れます。
説明にWebサイトにアクセスすればフラグが取れますと追記されたのが逆に悩む元になって、crattack.tistory.com の中をくまなく探したりしてしまいました。
crattack.tistory.com のアクセスカウンタがどんどん回っているのがとにかく気になって仕方がなかったです。
Anti-Debugging
まず手元に CPU が Atom のへっぽこ Windows マシンしかなくて解こうか迷ったんですが、他に解けそうな問題が見当たらなかったので挑戦してみました。
問題名からデバッガが必要なんだろうなと思い、とりあえず Mac だけでなんとかならないか足掻いてみようと IDA というデバッガを Wine で実行してみたんですが、なんかうまく動きそうになかったので Mac でやるのは早々に断念。
次に Windows に移動して、Immunity Debugger というデバッガで実行。
まず正しいパスワードがわからなくて困ったんですが、Mac でstrings
コマンドに通してみたところ、出力されているメッセージのあたりにI have a pen.
という文字列があったのでこれを入力してみたところYour password is correct.
となったのでパスワードを発見。
そのついでに、あきらかに何かしらの暗号化が施されている文字列;aj&@:JQ7HBOt[h?U8aCBk]OaI38"
が見え、これをどうにかしたら復号できるんじゃないかとも思いましたが、まじめにアンチデバッギングを一つ一つ処理しました。
しかし、CheckRemoteDebuggerPresent
をスキップさせるために試行錯誤しているあたりで、アンチデバッギング対策なんてチートの必須技術なんだから何かツールがあるんじゃないかと思いググったところ ScyllaHide というツールを発見。
ScyllaHide は、OllyDbg などで動作するプラグインで各種アンチデバッギングに自動で対応してくれる優れものです。
OllyDbg に移ってこれを使ってみたところ一通りアンチデバッギングを回避はできているようなんですが、なぜか途中で変なところにジャンプしてしまい、ここで結構悩みました。
ふと、最初のIsDebuggerPresent
のところにブレークポイントを置きステップ実行していったところ、途中で無条件に CALL し飛んでいったっきり帰ってこないようになっていることに気づきました。
つまり、0x00401590
のCALL
命令が諸悪の根源であるということに気づき、なのでとりあえずその命令をNOP
命令に置き換えてみたところ、少し下の別のところで引っかかるので、JMP
で怪しい文字列の直前である0x00401663
にジャンプするように置き換え直して、メッセージダイアログが表示されフラグを取ることができました。
OllyDbg なら該当の命令を右クリック→Assemble
→JMP 00401663
と入力→Assemble
とすればできます。
感想
とりあえず多くの人が解けている問題を全部解けたので満足です。
cheer msgとか200点の問題を解けるともうちょっと楽しめそうなのでいろいろやろうと思います。