はまやんはまやんはまやん

hamayanhamayan's blog

Automotive CTF 2024 World Final Writeup

予選国内決勝を経て、世界決勝にTeamONEの一員として行ってきました。結果は4位。チームメンバーのWriteupはここ。チームメンバーと運営に感謝です!

xNexus

これまでも出題されてきた、xNexusというVSOCプラットフォームが与えられて設問に答えていく問題群。#1に2時間溶かした挙句解けず、やらかしました…

CAN Bus Anomaly #2

Someone is trying to kill my engine. Determine what vehicle I am driving including the year by tracking CAN ID 0x7E0. Flag Format example: bh{Toyota Hilux 2021}

xNexusから該当する0x7E0のCAN通信を持って来ると以下の通信が記録されていた。

0x000007e0   05 31 01 40 44 ff 00 00

ここから車種と年代を特定する問題。この資料に該当する通信を見つけることができる。これの51ページを見ると、Ford社の車の通信であることが分かり、15ページを見ると更にa 2010 Ford Escapeと書いてあった。

つまり、bh{Ford Escape 2010}が答え。

RAMN

日本決勝でも題材として出題されたRAMNに関する問題群。実機を使わない問題もあった。

[FILE] SWD 1

問題文の転記を忘れたが、以下のようなロジアナのログが与えられるので、解釈してデータを取り出せという問題。

Time;CH 1 SWCLK;CH 2 SWDIO
0.000000000;1;1
0.594872000;0;1
0.594878000;1;1
0.594884000;0;1
0.594890000;1;1
0.594894000;0;0
0.594900000;1;0
…

SWCLKとSWDIOという用語で検索するとSerial Wire Debugという通信であることが分かる。ロジアナのログというのに慣れておらず、これを自力パースか?と思ってフォーマットを調べたり、無駄な1時間を過ごしてしまったが、素直にPulseViewで読み込むことが出来た。;,に変換して、csv形式でPulseViewに読み込ませる。Protocol DecoderでSWDを選択し、SWCLKとSWDIOと適切に指定すれば以下のように、いい感じにデコードされてくるので、全部ダンプしてくる。

1-15 SWD: : W AP4
18-22 SWD: : OK
28-90 SWD: : 0xe000ed00
94-108 SWD: : R APc
111-115 SWD: : OK
117-179 SWD: : 0x00000000
187-201 SWD: : RDBUFF
204-208 SWD: : OK
210-272 SWD: : 0x410fd212
280-294 SWD: : R CTRL/STAT
297-301 SWD: : OK
303-365 SWD: : 0xf0000040
372-386 SWD: : W AP4
389-393 SWD: : OK
399-461 SWD: : 0xe0042000
465-479 SWD: : R APc
482-486 SWD: : OK
488-550 SWD: : 0x00000000
…

エラーも無く、とてもいい感じ。出てきたものをなんとなく眺めていると、SWD: : W AP4の後にアドレスが書いてあって、SWD: : RDBUFFまでの間にデータが入っているような感じに読める。それっぽく取り出すスクリプトを書いて出してみると文字列が4bytes毎に逆になっていた、つまり、リトルエンディアンだったのでその辺りを調整して、以下のようなスクリプトを書いて全部持って来る。

res = {}

def rev(s):
    return s[6:] + s[4:6] + s[2:4] + s[:2]

with open("annon.txt") as fp:
    state = 0
    addr = ""
    buf = []
    for _line in fp.readlines():
        line = _line[:-1]
        if line.endswith("SWD: : W AP4"):
            assert state == 0
            state = 1
        elif state == 1 and line.endswith(" SWD: : OK"):
            state = 2
        elif state == 2:
            addr = line.split(':')[2]
            buf = []
            state = 3
        elif state == 3:
            if line.endswith("SWD: : RDBUFF"):
                res[addr] = buf
                state = 0
            elif "SWD: : 0x" in line:
                buf.append(rev(line.split(":")[2][3:]))

for addr,bufs in res.items():
    print(addr, bufs)

これで全部持ってこれるのでhex2binaryしてstringsすればフラグが出てくる。

[D] I2C

This flag will be transmitted every second on CAN with ID 0x778 if you can send any byte to ECU D on its I2C interface (port I2C2, address 0x63). Note: I2C pins have internal pull-up resistors. Pin layout is available here.

RAMNのECU DにI2C経由で書き込みを行うことができれば、CAN IDの0x778でフラグが出力されてくるという問題。ここを見ると、ECU Dのピン配置の番号は得られるのだが、番号と用途の対応表を見ても、どう刺せばいいか分からない。ロジアナで波形を見ながら用途を推測するような高等技術は持っていなかったため、検索を進めていくと、RAMNのGitHubのレポジトリにI2Cで使われる番号を書いた資料を見つけることが出来た。

https://github.com/ToyotaInfoTech/RAMN/blob/main/hardware/RAMNV1_pinout.pdf

ここから、

SCL -> PB10 ->14
SDA -> PB11 -> 15

ということが分かるので、その通り結線してやる。自分はtkitoさんから借りたBus Pirateを使ってI2C通信を行った。これで準備はできたので、CAN通信を受け取る準備をして、以下のようにBus Pirateでアドレス0x63に対して書き込みを行う。

I2C> [0x63 0x00 0x00 0x01]

するといい感じにACKが帰ってきて、CAN通信をcandumpで受け取っている方のコンソールで

 (1729536865.698447)  can0  778   [8]  62 68 7B 49 4E 46 41 4D   'bh{INFAM'
 (1729536865.700350)  can0  778   [8]  4F 55 53 5F 52 45 4D 41   'OUS_REMA'
 (1729536865.708109)  can0  778   [3]  4B 45 7D                  'KE}'

こんな感じにフラグが送られてくる。