picoCTF 2018: SpyFi
問題
問題文
James Brahm, James Bond's less-franchised cousin, has left his secure communication with HQ running, but we couldn't find a way to steal his agent identification code. Can you? Conect with
nc 2018shell2.picoctf.com 37131
. Source.
Hints:
What mode is being used?
解答例
指針
- brute-force-attack
解説
与えられたソースコードは以下のようなものだった.
#!/usr/bin/python2 -u from Crypto.Cipher import AES agent_code = """flag""" def pad(message): if len(message) % 16 != 0: message = message + '0'*(16 - len(message)%16 ) return message def encrypt(key, plain): cipher = AES.new( key.decode('hex'), AES.MODE_ECB ) return cipher.encrypt(plain).encode('hex') welcome = "Welcome, Agent 006!" print welcome sitrep = raw_input("Please enter your situation report: ") message = """Agent, Greetings. My situation report is as follows: {0} My agent identifying code is: {1}. Down with the Soviets, 006 """.format( sitrep, agent_code ) message = pad(message) print encrypt( """key""", message )
このプログラムは入力を取り, その文字列を sitrep という変数に格納し, sitrep と flag が格納されている agent_code を含んだ message を AES の ECB モードで暗号化した値を16進文字列で表示している.
AES (Advanced Encryption Standard) は標準となる共通鍵暗号アルゴリズムのこと. AES では 128 bit (16 Bytes) ごとのブロック単位で暗号化処理を行う.
AES の ECBモード (Electronic Codebook Mode) では平文ブロックと暗号文ブロックが一対一の関係となる.
したがって, 同じ鍵を用いた場合, 同一の値を持つ平文ブロックを暗号化した結果の暗号文ブロックは常に同じ値となる.
message という文字列変数を 16 Bytes ごとに区切ってみる. Ascii コードは 1文字あたり 1 Bytes なので 16文字ごと区切る.
range で示しているのはそのブロックに対応する16進文字列が何文字目から何文字目までであるかである.
block num | range | value |
---|---|---|
1 | 0:31 | Agent,\nGreetings |
2 | 32:63 | . My situation r |
3 | 64:95 | eport is as foll |
4 | 96:127 | ows:\nXXXXXXXXXXX |
5 | 128:159 | fying code is: p |
6 | 160:191 | XXXXXXXXXXXXXXXX |
7 | 192:223 | XXXXXXXXXXXXXXXX |
8 | 224:255 | XXXXXXXXXXXXXXXX |
9 | 256:287 | XMy agent identi |
10 | 288:319 | fying code is: Y |
11 | 320:351 | YYYYYYYYYYYYYYYY |
ここで, 太字で示した部分は入力で与える自由に設定できる文字列で, YYY..Y の部分は flag となる文字列である.
例えば今, 5番目のブロックに fying code is: p
という文字列が来るように入力を与えたとする. ここで 5番目と10番目のブロックに相当する暗号文を比較することで flag の1文字目が p
であるかどうか判定できる.
このように一文字ずつ総当たりしていけば flag が分かる.
- exploit
#!/usr/bin/env python3 # coding: utf-8 import socket flag = "" tmp = "fying code is: " for i in range(48, 0, -1): for c in range(0x20, 0x7e): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('2018shell2.picoctf.com', 37131)) data = s.recv(4096); data += s.recv(4096); send_msg = 'X' * 11 + tmp + chr(c) + 'X' * i + '\n' s.send(send_msg.encode()) t = s.recv(4096).decode() if t[128:160] == t[288:320]: tmp = tmp[1:] + chr(c) flag += chr(c) break if flag[-1] == "}": break print(flag) print ("flag is: " + flag)
- 実行結果
$ python exploit.py p pi pic pico picoC picoCT (snip) picoCTF{@g3nt6_1$_th3_c00l3$t_81247 picoCTF{@g3nt6_1$_th3_c00l3$t_812476 picoCTF{@g3nt6_1$_th3_c00l3$t_8124762 flag is: picoCTF{@g3nt6_1$_th3_c00l3$t_8124762}
参考文献
- 暗号技術入門 第3版 秘密の国のアリス