All Articles

TFCCTF 2023 Writeup

7/28 から開催されていた TFCCTF に 0nePadding で参加して 35 位/ 1429 チームでした。

image-20230801221548741

個人的には Rev が難しくて全然解けずハードな CTF だったのですが、チームメンバーがかなりの問題を解いてくれたので参加チームが多い中でそこそこの順位になりました。

もくじ

PASS(Rev)

Can you get the right password?

問題バイナリを Ghidra でデコンパイルすると、入力文字を 1 文字ずつ評価して正しいパスワードかどうかを検証するプログラムでした。

シンプルに angr の得意領域なので以下の Solver で Flag を取得しました。

import angr

proj = angr.Project("pass", auto_load_libs=False)
obj = proj.loader.main_object
print("Entry", hex(obj.entry))

find = 0x4019b0
avoids = [0x401981]

init_state = proj.factory.entry_state()
simgr = proj.factory.simgr(init_state)
simgr.explore(find=find, avoid=avoids)

# 出力
simgr.found[0].posix.dumps(0)

# TFCCTF{f0und_th3_p44sv0rd}

PROCESS-MONITOR(Rev)

Just a simple process monitor. Or is it?

問題バイナリは、.sys、.inf、.cat のカーネルドライバ 3 点セットでした。

とりあえず、inf ファイルを右クリックして Windows の VM にドライバをインストールしつつ Ghidra で解析を進めていきます。

一通り解析を進めてみると、問題バイナリはプロセスのアクティビティをフックして独自のコールバック関数を実行するようだということがわかります。

さらにコールバック関数の処理を追っていくと、フック対象のプロセスの実行ファイル名やパスなど複数の情報を収集して、条件に合致するかを判定し、すべての条件を満たす場合は C:\flag.txt に何らかの文字列を書き込む処理を行うことがわかりました。

ここまで解析したところで、 C:\flag.txt に何らかの文字列を書き込む処理が少々複雑で静的解析で特定できなかったので、カーネルデバッグを仕掛けることにしました。

テストモードで稼働する VM にカーネルデバッグを仕掛けつつ、以下のコマンドを実行します。

bp ProcessMonitor+0x1410 bp ProcessMonitor+0x1426 bp ProcessMonitor+0x1435 bp ProcessMonitor+0x1446

.while(1){g;r zf=1;g;r zf=1;g;? poi(rcx);g;? poi(rcx)}

参考:Windowsカーネルドライバを自作してWinDbgで解析してみる - かえるのひみつきち

これで0967ce7f7c9e7e2e28bcab79c921398ba92dd3d9fc6045a546f4c4130252bf9という文字列が C:\flag.txt に書き込まれたので、TFCCTF{0967ce7f7c9e7e2e28bcab79c921398ba92dd3d9fc6045a546f4c4130252bf9} とすることで Flag を取得できました。

DOWN BAD(Forensic)

The flag is right there!

問題バイナリとして与えられた down_bad.png は、普通に開くことができませんでした。

pngcheck にかけてみると、CRC の不一致が発生していることがわかります。

pngcheck down_bad.png
>
down_bad.png  CRC error in chunk IHDR (computed 1d9c52c0, expected a9d5455b)
ERROR: down_bad.png

そのため、以下の Solver を使用して正しい CRC の値をブルートフォースで特定しました。

from binascii import crc32
correct_crc = int.from_bytes(b'\xa9\xd5\x45\x5b',byteorder='big')
for h in range(2000):
    for w in range(2000):
        crc=b"\x49\x48\x44\x52"+w.to_bytes(4,byteorder='big')+h.to_bytes(4,byteorder='big')+b"\x08\x06\x00\x00\x00"
        if crc32(crc) % (1<<32) == correct_crc:
            print ('FOUND!')
            print ('Width: ',end="")
            print (hex(w))
            print ('Height :',end="")
            print (hex(h))
            exit()

ここで特定した Height と Width を指定して画像のバイナリを書き換えることで、カットされていた Flag 部分を含む画像を取得することができました。

from zlib import crc32
import argparse
import struct

png = bytearray(open("down_bad.png", 'rb').read())
width = 0x780
height = 0x540
png[0x10:0x14] = struct.pack(">I",width)
png[0x14:0x18] = struct.pack(">I",height)
calculatedCrc = crc32(png[12:29])

with open("solve.png",'wb') as file:
    file.write(png)

image-20230801200251868

MCTEENX(Forensic)

I fly in the sky, I got wings on my feet.

問題バイナリとして与えられた暗号化 ZIP ファイルの中には script.sh というファイルが格納されているようですが、パスワードを取得できません。

チームメンバーがすでに解いていたのですが、どうやらスクリプトファイルの全文がわからなくても、 Shebang(#!/bin/bash\n)を推測して既知平文攻撃を行うことで ZIP のパスワードを解除できるらしいです。

実際の攻撃は、以下のような流れで行います。

# partial_script.sh には #!/bin/bash\n を書き込み
# bkcrack で 3 つのキーを生成
./bkcrack -C red.zip -c script.sh -p partial_script.sh

# 3 つのキーを使用して暗号化を解除する
bkcrack -C red.zip  -c script.sh -k c0b1bc78 c3206dfc e7e5bae1 -d decipheredfile.sh

参考:kimci86/bkcrack: Crack legacy zip encryption with Biham and Kocher’s known plaintext attack.

これで復号されたスクリプトファイルを実行すると、以下の画像を得ることができます。

img

この画像に対して RGB の LSB を抽出すると、030a111418142c783b39380d397c0d25293324231c66220d367d3c23133c6713343e343b3931という文字列が取得できます。

この 16 進数文字列は、“WLR” をキーとして XOR した Flag だったようで、復号することで Flag の取得ができます。

まとめ

今回はあんま解けなかったので要精進。