3人でチームを組んで、睡眠も取って15時間以上は参加できたと思います。
世界から455チームが挑戦して(最低でも244チームは1問以上解いていました)、私達のチームは65ポイントで58位でした。
問題はCrypt, Web, Rev, Recon, Misc, Exploitから各5,6問程度ずつ出ました。今回は、私が解いた問題の内、覚えているwhitebox, bad_appleについてwriteupを書きます。
whitebox crypt - rev - 20[pt]
問題文:
Do not panic, it's only XTEA! I wonder what the key was... ctf.link/assets/downloads/rev/xtea
問題ファイルをobjdumpで見ると、まずstrlenでargv[1]が16文字かをチェックしています。
そこで適当な16文字を渡していき、ブラックボックス的にテストすると、後半の8文字は出力に影響が無いことが分かりました。
さらに、問題文のXTEAで調べると、wikipediaに辿り着き、これを参考に解析を開始します。
wikiによると、暗号化の途中で sum + key[sum & 3] を行うのですが、keyがバレないように、この値は全て予め書き込まれています。こんな感じ:
4005a7: 89 ca mov edx,ecx
4005a9: 89 c8 mov eax,ecx
4005ab: c1 ea 05 shr edx,0x5
4005ae: c1 e0 04 shl eax,0x4
4005b1: 31 d0 xor eax,edx
4005b3: 01 c8 add eax,ecx
4005b5: 35 68 78 70 7b xor eax,0x7b707868
4005ba: 03 07 add eax,DWORD PTR [rdi]
4005bc: 41 89 c2 mov r10d,eax
4005bf: 89 c2 mov edx,eax
4005c1: c1 e0 04 shl eax,0x4
4005c4: 41 c1 ea 05 shr r10d,0x5
4005c8: 41 31 c2 xor r10d,eax
4005cb: 41 01 d2 add r10d,edx
4005ce: 41 81 f2 2e ea 58 1b xor r10d,0x1b58ea2e
4005d5: 45 8d 1c 0a lea r11d,[r10+rcx*1]
ということで、この sum + key[sum & 3] にあたる値を収集しました。
そのあと、なぜかこのencipherをC言語で書いたのですが、sumやkeyは入力に影響しないと気付いて、すぐにkeyの計算に取りかかりました。
wikiのdecipherを参考に、
・delta = 0x9E3779B9 で固定。
・sum = delta * num_round でnum_roundは今回の場合32個分あった。
・sum -= delta を繰り返す。
・keyは4要素ある32bit整数の配列
と分かりました。
すぐに解読コードを書きました:
#include <stdio.h>
#include <stdint.h>
int main()
{
int num_rounds = 32, i;
uint32_t delta = 0x9E3779B9, sum = delta * num_rounds;
uint32_t l[64] = {0x7b707868, 0x1b58ea2e, 0xba9ae30, 0x9bd661db, 0x9bd661db, 0x4818a1a2, 0x57c7dda0, 0xf44e5f4c, 0xf44e5f4c, 0x9285d905, 0x84879514, 0x326e4acb, 0x14b448bf, 0xb2ebc278, 0xd0a5c484, 0x5f2e023f, 0x6d2c4630, 0xb63bfe9, 0xfd657bf8, 0xa99b39a2, 0x8d922fa3, 0x4983ab68, 0x4983ab68, 0xca012315, 0xe60a2d14, 0x764362dc, 0x764362dc, 0x147adc95, 0x6701687, 0xc0b09a3f, 0xc261924c, 0x60990c05, 0x5ee813f8, 0xe11683b2, 0xef2149c0, 0x8d58c379, 0x7f4dfd6b, 0x2b903d32, 0x3b3f7930, 0xd7c5fadc, 0xd7c5fadc, 0x77ae6ca, 0x67ff30a4, 0xf82be44f, 0xf82be44f, 0xa46e2416, 0xb41d6014, 0x42a59dcf, 0x50a3e1c0, 0xeedb5b79, 0xe0dd1788, 0x8ec3cd3f, 0x7109cb33, 0xf4144ec, 0x2cfb46f8, 0xad78bea5, 0xc981c8a4, 0x59bafe6c, 0x59bafe6c, 0x5f0bc16, 0xe9e7b217, 0xa5d92ddc, 0xa5d92ddc, 0x2656a589};
uint32_t key[4] = {0,0,0,0};
for(i = 0; i < num_rounds; i++) {
key[(sum >> 11) & 3] = l[63 - i*2] - sum;
sum -= delta;
key[sum & 3] = l[62 - i*2] - sum;
}
printf("%08x %08x %08x %08x\n", key[3], key[2], key[1], key[0]);
return 0;
}
出力結果をasciiにし、エンディアンを直したら、}!pu_gnimr4w{pxhとなりました。
これを逆にして、hxp{w4rming_up!}
bad_apple - Crypt - 15[pt]
問題文:
Baby's 1st ctf.link/assets/downloads/cry/bad_apple.tar.xz try: ncat 1.ctf.link 1027 < good.bin expect: "hello"
xzにはgood.binとbad_apple.pyが含まれていました。
以下がbad_apple.pyです:
#!/usr/bin/env python3
import sys, binascii
from Crypto.Hash import SHA256
key = open('key.bin', 'rb').read()
message = sys.stdin.buffer.read(0x100)
if len(message) < SHA256.digest_size:
print('len')
exit(0)
tag, message = message[:SHA256.digest_size], message[SHA256.digest_size:]
if SHA256.new(key + message).digest() != tag:
print('bad')
exit(0)
if b'hello pls' in message:
print('hello')
elif b'flag pls' in message:
print(open('flag.txt', 'r').read())
送信されたファイルをハッシュ(tag)と文字(message)に分け、sha256(key + message)とtagの値が等しいことを確認します。
さらにmessageが"flag pls"ならフラグが取得できます。
不明な文字を足してハッシュを取る、というプログラムの時点で頭の中はhashpumpだけだったので、hashpumpを使う方向で解きました。
さて、good.binの中の前半のハッシュは「2628455f6617ecea024895a48578ebce00fa9204983d09b6d1757d05dc430567」で、文字は「hello pls」でした。
これを元に、flag plsを付け足したハッシュを作ります。
keyの長さが分からなかったのでpythonでループしました:
import commands import binascii i = 32 for i in range(1, 1024): ret = commands.getoutput("hashpump -s 2628455f6617ecea024895a48578ebce00fa9204983d09b6d1757d05dc430567 -d \"hello pls\" -a \"flag pls\" -k " + str(i)) get = ret.split("\n") _hash_ = binascii.unhexlify(get[0]) data = get[1].decode('string_escape') buf = _hash_ + data f = open("data", "wb") f.write(buf) f.close() ret = commands.getoutput("nc 1.ctf.link 1027 < data") print(str(i) + " : " + ret) if ret == "len" : continue if ret == "bad" : continue if ret == "hello" : continue break32回目でフラグが返ってきましたとさ...。
hxp{M3rkL3_D4mg4rd_h4s_s0m3_Pr0bl3mZ}
全体を通して難しかったです。Web30とRev10とExploit10が解けなかったのが非常に悔しいです。あとは上級者向けが多く、1人〜3人程度しか解いていない問題ばかりで、難易度がすごい分かれていました。
もしかしたら暗号が向いているのかなぁ...
CTFで思ったのは、まず、Reconを予告しておきながら、最後の最後で0[pt]の答え無しのページにした運営に驚きました。あと、web20の問題の答えが後半でIRCに書き込まれたせいか、20[pt]にも関わらず145人もの人が後半でこのポイントを獲得しています。
ちょっと難しかったですが、解けたときは楽しかったです。