rsa-buffet
boston-key-party-2017
encrypt.py
import random
from Crypto.Cipher import AES,PKCS1_OAEP
from Crypto.PublicKey import RSA
def get_rand_bytes(length):
return "".join([chr(random.randrange(256)) for i in range(length)])
def encrypt(public_key, message):
"""Encrypt a message with a given public key.
Takes in a public_key generated by Crypto.PublicKey.RSA, which must be of
size exactly 4096
"""
symmetric_key = get_rand_bytes(32)
msg_header = PKCS1_OAEP.new(public_key).encrypt(symmetric_key)
assert len(msg_header) == 512
msg_iv = get_rand_bytes(16)
msg_body = AES.new(symmetric_key,
mode=AES.MODE_CFB,
IV=msg_iv).encrypt(message)
return msg_header + msg_iv + msg_body
def decrypt(private_key, ciphertext):
"""Decrypt a message with a given private key.
Takes in a private_key generated by Crypto.PublicKey.RSA, which must be of
size exactly 4096
If the ciphertext is invalid, return None
"""
if len(ciphertext) < 512 + 16:
return None
msg_header = ciphertext[:512]
msg_iv = ciphertext[512:512+16]
msg_body = ciphertext[512+16:]
try:
symmetric_key = PKCS1_OAEP.new(private_key).decrypt(msg_header)
except ValueError:
return None
if len(symmetric_key) != 32:
return None
return AES.new(symmetric_key,
mode=AES.MODE_CFB,
IV=msg_iv).decrypt(msg_body)
if __name__=="__main__":
# Test!
message = "This is my test message. It's kind of ssilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregsilly.wireheirhgwieruhgwieurghwiregilly.wireheirhgwieruhgwieurghwireg"
private_key = RSA.generate(4096)
public_key = private_key.publickey()
ciphertext = encrypt(public_key, message)
assert message == decrypt(private_key, ciphertext)
generate-plaintexts.py
# pip install secretsharing
# https://github.com/blockstack/secret-sharing
from secretsharing import PlaintextToHexSecretSharer as SS
with open('message1.txt', 'r') as f:
PLAINTEXTS = [f.read()] * 5
for i in range(2, 6):
with open('message' + str(i) + '.txt', 'r') as f:
msg = f.read()
shares = SS.split_secret(msg, i, 5)
for j in range(5):
PLAINTEXTS[j] += shares[j] + '\n'
for j in range(5):
with open('plaintext-' + str(j+1) + '.txt', 'w') as f:
f.write(PLAINTEXTS[j])
admin的WP
这个挑战涉及两个密码系统:RSA和Shamir的Secret Sharing。必须首先恢复足够的RSA公钥的私钥,才能成功解密5个给定密文中的3个。github上有一个方便的小库,可以通过顺序执行对RSA的知名攻击(维纳,哈斯塔德,费马,因子DB等)来帮助您做到这一点。使用该库的分支,我在程序中运行了每个公钥,并成功恢复了3个私钥。
$ python RsaCtfToolcli.py --publickey ../rsa-buffet/key-1.pem --private
$ python RsaCtfToolcli.py --publickey ../rsa-buffet/key-2.pem --private
$ python RsaCtfToolcli.py --publickey ../rsa-buffet/key-3.pem --private
这会将恢复的私钥写入文件。现在我们有了3个私钥,我们可以开始尝试解密一些密文。我编辑了提供encrypt.py文件的主要部分以执行解密。
if __name__ == "__main__":
k_text = open(sys.argv[1], 'r').read()
c = open(sys.argv[2], 'rb').read()
k = RSA.importKey(k_text)
print(decrypt(k, c))
经过反复试验,我发现以下密钥密文解密对:key1 +密文5,key2 +密文1,key3 +密文4。
$ python encrypt.py key1 ciphertext-5.bin
Congratulations, you decrypted a ciphertext! One down, two to go :)
5-7d29041c468b680fcff93c16011a2869f17de75b929b787503b412becde0321ec72fe1e499f2150a1dacb9a5f701c0b37470049dd560cef5163543469817971f50782f763f0b05ab7088f7ae
5-a7a1e271cf263279cece532b540545fa539b0f3650e2929163b02ee5459debdc53c1e07149eb2153015bb5c88e6270e8
5-149480c5c75cbe320564adfa432ac8ea241e048ed39c8bc6be14ca80c392487f43a7882075d785d62cb314ea6c89a6b5f28adfa56ec481e124567b88241de2a6cabcc7ec9de3acac8be5375b
5-7285289084282d559573f68eef10191091d76d6670014202670651f867cd2bc8640a86eef1c1e482affc7ae801fa446956c2186972fb6b7bac88c91d050c9d3cca
$ python encrypt.py key2 ciphertext-1.bin
Congratulations, you decrypted a ciphertext! One down, two to go :)
1-32a1cd9f414f14cff6685879444acbe41e5dba6574a072cace6e8d0eb338ad64910897369b7589e6a408c861c8e708f60fbbbe91953d4a73bcf1df11e1ecaa2885bed1e5a772bfed42d776a9
1-e0c113fa1ebea9318dd413bf28308707fd660a5d1417fbc7da72416c8baaa5bf628f11c660dcee518134353e6ff8d37c
1-1b8b6c4e3145a96b1b0031f63521c8df58713c4d6d737039b0f1c0750e16e1579340cfc5dadef4e96d6b95ecf89f52b8136ae657c9c32e96bf4384e18bd8190546ff5102cd006be5e1580053
1-c332b8b93a914532a2dab045ea52b86d4d3950a990b5fc5e041dce9be1fd3912f9978cad009320e18f4383ca71d9d79114c9816b5f950305a6dd19c9f458695d52
$ python encrypt.py key3 ciphertext-4.bin
Congratulations, you decrypted a ciphertext! One down, two to go :)
4-4a87367d053c533fd995032ed1e651487cb5dc1e0b1cb70a7662b152c73650f039a60f391a52f2413f43bd54eb7b12c41b42f31ac557edd4bfe46a396a8cdbe19dc9d8121924f43be51c976d
4-abbbcee71f140198ff8c50f51069465075979c31d32b052e7ae82ec7f6783aef7b41a597f9504d3340967b8d70cbe5a3
4-35fbbe40058e20463547b363d1f164c0bbbb97cfd9ffe7619bce31a59392f0e9625a2cd035276e09c4df3c0932f22bd322f16e375c7c7fd88da0f972832707eb549ff1e776db37649019ebce
4-12b466934911986bda845d8d26710a12250d210546f46716c78d7a17b1f2c893b95b934c8c7beafcf81a3123eb2ea05ca89101b23349e455794a8d56608c8ee49dd
现在有了来自秘密共享的足够的秘密,我就可以恢复该flag。
from secretsharing import PlaintextToHexSecretSharer as SS
files = ["ciphertext5.txt", "ciphertext1.txt", "ciphertext4.txt"]
secrets = []
for fn in files:
a = []
with open(fn, "r") as f:
a = f.readlines()
a = a[1:] # strip the "Congratulation" line
for i in range(0,len(a)):
a[i] = a[i].strip("\n")
secrets.append(a)
for i in range(0, 2):
a = SS.recover_secret([secrets[0][i], secrets[1][i], secrets[2][i]])
print(a)
运行这个小脚本会产生一些结果,并生成flag。
And another one's down, and another one's down, and another one bites the dust!
Three's the magic number! FLAG{ndQzjRpnSP60NgWET6jX}.