9 月 7 日から 9 月 9 日にかけて開催された Affinity CTF 2019 - Quals に、チーム zer0pts として参加しました。最終的にチームで 4061 点を獲得し、順位は得点 450 チーム中 11 位でした。うち、私は 12 問を解いて 2361 点を入れました。
以下、私が解いた問題の write-up です。
添付ファイル: spiral.png
テキストを変形させたような画像が与えられました。GIMP の渦巻きと吸い込みフィルターを、Whirl を -1020 ぐらいに設定して適用すると読めるようになりました。
AFFCTF{h3arly_He4rly_th1s_WaS_sw1rly_!!!}
添付ファイル: stegoego.png
立体視で何か読めそうな画像ファイルが与えられました。…が、私には頑張っても読めなかったので、stegsolve.jar に搭載されている Stereogram Solver 機能を使って、横方向にずらした同じ画像との差分を見ていくと以下のようにフラグが読めました。
AFFCTF{Defence123}
Welcome!
添付ファイル: alphinity.png
stegsolve.jar でこの画像を開いてみると、透明度の LSB のみを抽出したときに左端のピクセルで縦方向に何かデータ埋め込まれていそうな様子が確認できました。これを抽出するスクリプトを書きましょう。
import binascii
from PIL import Image
im = Image.open('alphinity.png')
y = 0
res = ''
while True:
r, g, b, a = im.getpixel((0, y))
if not a & 2:
break
res += str(a & 1 ^ 1)
y += 1
print(binascii.unhexlify(hex(int(res, 2))[2:]))
実行するとフラグが得られました。
$ python solve.py
b'AFFCTF{h3ll0_4ff1n17y}'
AFFCTF{h3ll0_4ff1n17y}
Sam Sepiol pwned one of the Evol Corp office computers. But stuck in get into one their mainframes. To avoid detection he download only one file and connection dropped.
添付ファイル: EVOLCORPB.BIN
以下のようなファイルが与えられました。
$ xxd EVOLCORPB.BIN | head
0000000: a31e b6e3 72a1 a001 0000 00f0 3000 3000 ....r.......0.0.
0000010: c5f6 c340 f8f2 f0f5 4040 4040 4040 4040 ...@....@@@@@@@@
0000020: 4040 4040 4040 4040 4040 4040 4040 4040 @@@@@@@@@@@@@@@@
0000030: 0000 4500 0000 1000 0000 0000 0000 00f0 ..E.............
0000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................
EBCDIC として読み、AFFCTF
という文字列を探すとフラグが得られました。
$ dd if=EVOLCORPB.BIN of=res.bin conv=ascii
247+1 records in
247+1 records out
126720 bytes (127 kB, 124 KiB) copied, 0.0475761 s, 2.7 MB/s
$ strings -a res.bin | grep AFFCTF
AFFCTF{3V01_C0rP_l1K35_H1570rY__!}
AFFCTF{3V01_C0rP_l1K35_H1570rY__!}
AFFCTF{3V01_C0rP_l1K35_H1570rY__!}
Just a Sanity Check. Flag is: AFFCTF{S4nity_1s_V4nity_!}
問題文のフラグがそのまま通りました。
AFFCTF{S4nity_1s_V4nity_!}
“… i remember times when proper floppy DISC ORDer matters…”
公式の Discord サーバに入ると、#general
チャンネルのトピックにフラグが設定されていました。
AFFCTF{Pr0p3r_C0ms_aR3_4lways_g00d!}
plaintext plaintext everywhere….
添付ファイル: midi.pcap
与えられた pcap ファイルを Wireshark で見ていると、以下のような HTTP 通信が見つかりました。
GET /keyfile HTTP/1.1
Host: affinity.ctf
User-Agent: curl/7.65.3
Accept: */*
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 30 Jul 2019 14:31:32 GMT
Content-Type: application/octet-stream
Content-Length: 176
Last-Modified: Tue, 30 Jul 2019 14:31:12 GMT
Connection: keep-alive
ETag: "5d4054b0-b0"
Accept-Ranges: bytes
CLIENT_RANDOM 1CA43E0AF85CC083F6E636424124010831FD828A172A0AAAB8C17919D3585BE1 E7EACA03F7CBF6CFEE21F7AC7D62A10873A722F4C7DBA60BBECC0A7E91F1BA44BDFAE83264F40404C7CE43D4C82232DE
この通信の直前にある TLS 通信の client random と master secret のようです。これを使って TLS 通信を復号すると、HTTP/2 で /0xFFFFFF_0x20.midi
を取得している様子が確認できました。
問題名にある MIDI は取得できましたが、フラグの場所が分かりません。色々試しているとこの pcap を strings
することでフラグが得られました。
$ strings midi.pcap | grep AFFCTF
"AFFCTF{s3lf_sign3d_is_good_3nough}1
"AFFCTF{s3lf_sign3d_is_good_3nough}1
tcp contains "AFFCTF"
というフィルターを適用して AFFCTF
を含むパケットを確認してみたところ、どうやらフラグは証明書の組織名に仕込まれていたようです。MIDI 関係ないじゃないですか…。
AFFCTF{s3lf_sign3d_is_good_3nough}
John White is a programmer and musician. John likes to keep his space private and things that are not common.
添付ファイル: midi.pcap.gz
添付ファイルは MIDI1 と同じものです。どうやら先ほどの問題の続きのようです。
0xFFFFFF_0x20.mid
は以下のような内容でした。
$ xxd 0xFFFFFF_0x20.mid | head
0000010: 726b 0000 0f5c 0090 2014 6e80 2000 0090 rk...\.. .n. ...
0000020: 2014 6e80 2000 0090 2014 6e80 2000 0090 .n. ... .n. ...
0000030: 2014 6e80 2000 0090 2014 6e80 2000 0090 .n. ... .n. ...
0000040: 2014 6e80 2000 0090 0914 6e80 0900 0090 .n. .....n.....
0000050: 2014 6e80 2000 0090 2014 6e80 2000 0090 .n. ... .n. ...
0000060: 2014 6e80 2000 0090 2014 6e80 2000 0090 .n. ... .n. ...
0000070: 2014 6e80 2000 0090 0914 6e80 0900 0090 .n. .....n.....
0000080: 0a14 6e80 0a00 0090 0914 6e80 0900 0090 ..n.......n.....
0000090: 0a14 6e80 0a00 0090 2014 6e80 2000 0090 ..n..... .n. ...
xx14 6e80 xx00 0090
というバイト列が延々続いています。xx
の部分を抽出してみましょう。
with open('0xFFFFFF_0x20.mid', 'rb') as f:
f.read(24)
res = ''
while True:
t = f.read(8)
if len(t) != 8:
break
res += chr(t[0])
with open('res.txt', 'wb') as f:
f.write(res.encode())
$ python extract.py
$ xxd res.txt | head
0000000: 2020 2020 2020 0920 2020 2020 090a 090a . ....
0000010: 2020 2020 2020 0920 2020 0909 200a 090a . .. ...
0000020: 2020 2020 2020 0920 2020 0909 200a 090a . .. ...
0000030: 2020 2020 2020 0920 2020 2009 090a 090a . .....
0000040: 2020 2020 2020 0920 0920 0920 200a 090a . . . ...
0000050: 2020 2020 2020 0920 2020 0909 200a 090a . .. ...
0000060: 2020 2020 2020 0909 0909 2009 090a 090a .... .....
0000070: 2020 2020 2020 2020 0909 2020 0909 0a09 .. ....
0000080: 0a20 2020 2020 2009 0909 2020 0909 0a09 . ... ....
0000090: 0a20 2020 2020 2020 2009 0920 2020 200a . .. .
20
09
0a
のみを含むテキストファイルが出てきました。Whitespace のコードとして実行するとフラグが得られました。
AFFCTF{3s0t3r1c_l4ngs_4r3_Fun}
nc (IP アドレス) 9999
与えられた接続情報を使って問題サーバに接続すると、以下のような文字列が送られてきました。
$ nc (IP アドレス) 9999
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++++++++++++++++.------------.-------.+++++++++++++++++++.<<++.>>+++.---------------.-------.+++++++++++++++++++.<<.>>-------------------.+++++++++++++++++.-------------.<<.>>++++++++++++++++++++.----------.++++++.<<.>>---------.+++..----.--.+++++.-------.<<.>>-.+++++++++.+++.<<.>>---------.++++++++++.<<.>>----------.+++++.<<.>>-----------.++.+++++++..<<.>------------------.----.<+.
Brainf*ck のコードのようです。送信したコードを実行してくれるようなので、とりあえずメモリ上の値を読み出してみましょう。
>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.
>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.>.
AFFCTF{!s_th!s_th3_r3@l_l!f3__or__!s_th!s_just_f@nt@sy___}
フラグが得られました。
AFFCTF{!s_th!s_th3_r3@l_l!f3__or__!s_th!s_just_f@nt@sy___}
添付ファイル: task.gif
binwalk で task.gif
に含まれているファイルを確認しましょう。
$ binwalk task.gif
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 GIF image data, version "89a", 480 x 480
285539 0x45B63 gzip compressed data, from Unix, last modified: 1970-01-01 00:00:00 (null date)
285695 0x45BFF GIF image data, version "89a", 540 x 283
491472 0x77FD0 gzip compressed data, from Unix, last modified: 1970-01-01 00:00:00 (null date)
gzip compressed data
を抽出して a.gz
として保存し、展開すると flag.txt
が出てきました。これにフラグが書かれていました。
AFFCTF{m@k3s_y0u__th0nk}
Note: put flag into AFFCTF{} format.
添付ファイル: Man_In_The_Middle.pcap.gz
与えられた pcap ファイルを Wireshark で眺めていると、以下のように SMTP で何らかのパスワードを送信している様子が確認できました。
220 m-VirtualBox ESMTP Postfix (Ubuntu)
email to: k@affinity.com
502 5.5.2 Error: command not recognized
email from: m@affinity.com
502 5.5.2 Error: command not recognized
.[A.[D.[A.[Bemail from: m@affinity.com
502 5.5.2 Error: command not recognized
email from: m@affinity.com
502 5.5.2 Error: command not recognized
help
502 5.5.2 Error: command not recognized
...
502 5.5.2 Error: command not recognized
mail from: m@affinity.com
250 2.1.0 Ok
rcpt to: k@affinity.com
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
the password is Horse Battery Staple Correct
.
250 2.0.0 Ok: queued as 732F741243
quit
221 2.0.0 Bye
また、以下のように STRICTLY_CONFIDENTIAL
というファイルを FTP で受け取っている様子が確認できました。
220 (vsFTPd 3.0.3)
USER m
331 Please specify the password.
PASS m
230 Login successful.
SYST
215 UNIX Type: L8
PASV
227 Entering Passive Mode (10,0,2,15,93,141).
LIST
150 Here comes the directory listing.
226 Directory send OK.
TYPE I
200 Switching to Binary mode.
PASV
227 Entering Passive Mode (10,0,2,15,147,152).
RETR STRICTLY_CONFIDENTIAL
150 Opening BINARY mode data connection for STRICTLY_CONFIDENTIAL (69 bytes).
226 Transfer complete.
QUIT
221 Goodbye.
STRICTLY_CONFIDENTIAL
を抽出し、どのようなファイルか確認しましょう。
$ file STRICTLY_CONFIDENTIAL
STRICTLY_CONFIDENTIAL: Vim encrypted file data
Vim で暗号化されたファイルのようです。Vim で STRICTLY_CONFIDENTIAL
を開き、Horse Battery Staple Correct
というパスワードを入力すると I_Should_Have_Used_Safer_Connection_...
という文字列が出力されました。
AFFCTF{I_Should_Have_Used_Safer_Connection_...}
(URL)
与えられた URL にアクセスすると、以下のようにダウンロードできるファイルの一覧が出力されました。
Download file:<pre><a href="download.php?file=file1.txt&token=6f2268bd1d3d3ebaabb04d6b5d099425">file1.txt</a><br /><a href="download.php?file=file2.txt&token=e6cb2a3c14431b55aa50c06529eaa21b">file2.txt</a><br /><a href="download.php?file=file3.txt&token=65658fde58ab3c2b6e5132a39fae7cb9">file3.txt</a><br /><a href="download.php?file=flag.txt&token=">flag.txt</a><br />
flag.txt
のリンクにアクセスしましたが、Invalid token.
と表示されてしまいます。他のファイルの token
パラメータから flag.txt
の token
パラメータを推測必要がありそうです。
試しに file1.txt
の token
である 6f2268bd1d3d3ebaabb04d6b5d099425
でググってみると、これは 753
という文字列の MD5 ハッシュであることがわかりました。file1.txt
をダウンロードしてみたところ、753 バイトのファイルがダウンロードされたので、どうやら token
パラメータはファイルのサイズの MD5 ハッシュのようです。
雑に総当たりしましょう。
import hashlib
import requests
i = 1
while True:
h = hashlib.md5(str(i).encode()).hexdigest()
r = requests.get('http://165.22.22.11:25632/download.php?file=flag.txt&token=' + h)
if b'Invalid token.' not in r.content:
print(i, r.content)
break
i += 1
$ python solve.py
34 b'AFFCTF{Pr3dic71bl3_t0k3n5_4r3_b4d}'
フラグが得られました。
AFFCTF{Pr3dic71bl3_t0k3n5_4r3_b4d}