車輪の再開発ブログ

Python & CTF Beginner!

picoCTF 2022 SideChannel writeup

コンテスト中に解けなかった問題の復習です。

問題

There's something fishy about this PIN-code checker, can you figure out the PIN and get the flag?
Download the PIN checker program here pin_checker
Once you've figured out the PIN (and gotten the checker program to accept it), connect to the master server using nc saturn.picoctf.net 52680 and provide it the PIN to get your flag.

pin_checkerがダウンロードできる。

ヒント

Read about "timing-based side-channel attacks."
Attempting to reverse-engineer or exploit the binary won't help you, you can figure out the PIN just by interacting with it and measuring certain properties about it.
Don't run your attacks against the master server, it is secured against them. The PIN code you get from the pin_checker binary is the same as the one for the master server.

解法

ダウンロードしたファイルを、 file で確認する。

# file pin_checker 
pin_checker: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, stripped

(32-bitなので、dockerで、32-bitを動かせるようにしておいてから)

# chmod +x pin_checker 

# ./pin_checker 

これで、とりあえず、動かすことができた。

さっぱりわからなので、ヒントの サイドチャネル攻撃 - Wikipedia を読んでみる。

攻撃手法の雰囲気はわかったけど、この問題をどのように解くのかまでは、さっぱりということで、kusuwada先生の picoCTF2022 [Forensics] writeup - 好奇心の足跡 を読む。

実行時間に当たりをつけて、プログラムを実行していくことになるらしい。

プログラムの実行速度を測ると、最上位の桁を0から9に動かしながら、(00000000から90000000)を実行すると、明らかに、1つだけ、時間がかかっていた。 ということで、正解するほど、実行時間がかかると予想して、一番時間がかかった数字を、その桁に固定しながら、10x8の総当たりを実行する。

import time
import subprocess

pin = [0, 0, 0, 0, 0, 0, 0, 0]
for i in range(8):
    max_time = 0
    for j in range(10):
        pins = "".join(map(str, pin[0:i] + [j] + [0 for k in range(7-i)]))

        start = time.time()
        ret = subprocess.run(["./pin_checker"], input=pins+"\n", text=True, capture_output=True)
        elapsed_time = time.time() - start
        print(pins, elapsed_time)

        if max_time < elapsed_time:
            pin[i] = j
            max_time = elapsed_time
    print(i, pin)
print("".join(map(str, pin)))
... 略 ...
48390510 1.034353494644165
48390511 1.0456459522247314
48390512 1.0493018627166748
48390513 1.1346185207366943
48390514 1.0250821113586426
48390515 1.032979965209961
48390516 1.0264122486114502
48390517 1.066084861755371
48390518 1.0343708992004395
48390519 1.0656943321228027
7 [4, 8, 3, 9, 0, 5, 1, 3]
48390513

となり、pinが出てきた。実際に、pin_checkerに渡してみると、

# echo 48390513 | ./pin_checker
Please enter your 8-digit PIN code:
8
Checking PIN...
Access granted. You may use your PIN to log into the master server.

で、OKと。あとは、実際のサーバーで、同じPINを渡してやれば、フラグゲット。