python で ソケット通信 を組み立て,総当たり攻撃で解く問題。
こういうの,おしいところまではいくけど自力で成功することはめったにないので,ものすごく嬉しい。
この記事を自分で何度も見直すので picoCTF Practice Writeup 6 から独立させた。
Compress and Attack
Category: Cryptography
Description:
Your goal is to find the flag. compress_and_attack.py nc mercury.picoctf.net 50899
Hints:
- The flag only contains uppercase and lowercase letters, underscores, and braces (curly brackets)
# !/usr/bin/python3 -u
import zlib
from random import randint
import os
from Crypto.Cipher import Salsa20
flag = open("./flag").read()
def compress(text):
return zlib.compress(bytes(text.encode("utf-8")))
def encrypt(plaintext):
secret = os.urandom(32)
cipher = Salsa20.new(key=secret)
return cipher.nonce + cipher.encrypt(plaintext)
def main():
while True:
usr_input = input("Enter your text to be encrypted: ")
compressed_text = compress(flag + usr_input)
encrypted = encrypt(compressed_text)
nonce = encrypted[:8]
encrypted_text = encrypted[8:]
print(nonce)
print(encrypted_text)
print(len(encrypted_text))
if __name__ == '__main__':
main()
重要なのは
flag + user_input を圧縮し,encrypted_textの長さを出力している点
nc
$ nc mercury.picoctf.net 50899
Enter your text to be encrypted:
適当な文字 abcdefghi
$ nc mercury.picoctf.net 50899
Enter your text to be encrypted: abcdefghi
b'\\y\xf9^6\xa1:v'
b'\x0c\xa2H1y\xd6\x16b\x03tf|\x9c\x11\xa7-\xe5\xc9\x15\x1a\xa4\xc8N\x1d9\x8a\xbc\x8c\xc7V\xba\xd3\xe4\x7f7\xcf\x91\xeeD\x9d\x8a\x13\xff\xb9p\xcbm:o\x95\xca\x86+;'
54
picoCTF
Enter your text to be encrypted: picoCTF
b'~TJ{#.N '
b"\x15>\x0b\x07\x8d\n\xc6\x8d\x07'l\x8e\xfa\r\x11\xa3NcM\x0cv\xd3Y\xb7\xf3\x07< \x06Xl\xa6\xe3\x95\xaf\x92L:\xf8s\x9b\x1exm\x82\xa7\x86\x92"
48
encrypted_text の長さに注目
flag + "abcdefghi" --> 54
flag + picoCTF --> 48
flagは picoCTF{任意文字}なので,user_inputがflagと同じなら,圧縮効率が高い=encrypted_textの長さが短くなることがわかる
Enter your text to be encrypted: picoCTF{a
b"\xc5vTn\xc4F'N"
b'\t}\xff(\xbc\x0b\xcd\x02\xeffD\tjfm\xca\xadN\xaf\\\xb3\xb2\xb7\x04?\x1c@\xbbd\xc1\x80u\xdcJ\x1c5]\xa8\xabW\xc2Y\xbbe\xaflg\xe5\xad'
49
Enter your text to be encrypted: picoCTF{b
b'\x14\x10o6N\xc3_\x92'
b'\xa4\x7fJ\x07\xbfOe\x04\x84\x9a\x16\x07\x97\xa5\xc8\t\x80ZXb\x86\x12\xb0E\xca\x0b\x87\xf6\xd5\xf0\x04\x8aZ\x12WM\xb3g\xa0\xf25\xb7 \xb9\xddJ\xb7\x7f9'
49
(中略)
Enter your text to be encrypted: picoCTF{s
b'0u\xab\xfbN\n\x87\xdd'
b'KQ\xbc\x06\x14\xd4\xfc\xec\xd2@\\\x03\x02\x08W\x08rpi\xd0\x02\xd7\xe2\x82J\xba\xefd\xe4qHG\x93T\xe2\xfc\xecT\x8fe\x1c*\xf6\x19\xd7G!O'
48
picoCTF{a --> 49
picoCTF{b --> 49
picoCTF{s --> 48 ビンゴ
あとは印字可能な文字で総当たりする python を書く。
# python 2.7
import socket
import string
import time
start = time.time()
def recvuntil(s, tail):
data = ''
while True:
if tail in data:
return data
data += s.recv(1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('mercury.picoctf.net', 50899))
# 最初のプロンプトを受信,出力
data = recvuntil(s, 'encrypted:')
print(data)
# 基準となる encrypted_text の長さを取得する
ans = "picoCTF{"
s.sendall(ans + '\n')
data = recvuntil(s, 'encrypted:')
print(data)
min_size = data.split('\n')[2]
print("min_size")
print(min_size)
# ここからブルートフォース
while True:
# 終了判定 "}" を送信した結果が min_size と同じなら終了(フラグGet)
s.sendall(ans + '}' + '\n')
data = recvuntil(s, 'encrypted:')
print(data)
chk = data.split('\n')[2]
#print("chk")
#print(chk)
if (chk==min_size):
print(ans + '}' + '\n')
break
#for c in ("_" + string.ascii_letters):
for c in ("_abcdefghijklmnopqrstuvwxyz"):
s.sendall(ans + c + '\n')
data = recvuntil(s, 'encrypted:')
print(data)
chk = data.split('\n')[2]
#print("chk")
#print(chk)
if (chk==min_size):
ans = ans + c
print(ans + '\n')
break
elapsed_time = time.time() - start
print ("speed:{0}".format(elapsed_time) + "[sec]")
実行結果