st98 の日記帳


[ctf] Affinity CTF 2019 - Quals の write-up

9 月 7 日から 9 月 9 日にかけて開催された Affinity CTF 2019 - Quals に、チーム zer0pts として参加しました。最終的にチームで 4061 点を獲得し、順位は得点 450 チーム中 11 位でした。うち、私は 12 問を解いて 2361 点を入れました。

以下、私が解いた問題の write-up です。

Stego

Falling into Spiral (100)

添付ファイル: spiral.png

テキストを変形させたような画像が与えられました。GIMP の渦巻きと吸い込みフィルターを、Whirl を -1020 ぐらいに設定して適用すると読めるようになりました。

AFFCTF{h3arly_He4rly_th1s_WaS_sw1rly_!!!}

AFFCTF{h3arly_He4rly_th1s_WaS_sw1rly_!!!}

Stegoego (150)

添付ファイル: stegoego.png

立体視で何か読めそうな画像ファイルが与えられました。…が、私には頑張っても読めなかったので、stegsolve.jar に搭載されている Stereogram Solver 機能を使って、横方向にずらした同じ画像との差分を見ていくと以下のようにフラグが読めました。

AFFCTF{Defence123}

AFFCTF{Defence123}

Alphinity (300)

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}

RE

Evol Corp 1988 (700)

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__!}

Misc

Sanity Check (1)

Just a Sanity Check. Flag is: AFFCTF{S4nity_1s_V4nity_!}

問題文のフラグがそのまま通りました。

AFFCTF{S4nity_1s_V4nity_!}

DISC ORDer (10)

“… i remember times when proper floppy DISC ORDer matters…”

公式の Discord サーバに入ると、#general チャンネルのトピックにフラグが設定されていました。

AFFCTF{Pr0p3r_C0ms_aR3_4lways_g00d!}

MIDI1 (50)

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}

MIDI2 (600)

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}

Reading Disfunction (150)

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___}

Forensics

Reflection (50)

添付ファイル: task.gif

binwalktask.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}

Man In the Middle (100)

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_...}

Web

Download me … (150)

(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.txttoken パラメータを推測必要がありそうです。

試しに file1.txttoken である 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}
このエントリーをはてなブックマークに追加
st98.github.io / st98 の日記帳