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を渡してやれば、フラグゲット。