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版 秘密の国のアリス