@iPhan
2018-07-06T14:26:00.000000Z
字数 2971
阅读 609
step1: zipCrypto known plain text attack
step2 reverse py2exe
python script
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5, AES
from Crypto.Util.number import bytes_to_long, long_to_bytes
from os import urandom
bs = 16
def xor(x, y):
return bytes(a ^ b for a, b in zip(x, y))
class Pubcipher(object):
def __init__(self):
self._rsa = RSA.generate(1024)
self._pkcs = PKCS1_v1_5.new(self._rsa)
def encrypt(self, msg):
'''msg must be bytes'''
return self._pkcs.encrypt(msg)
def decrypt(self, ciphertext):
return self._pkcs.decrypt(ciphertext, None)
class Sycipher(object):
def __init__(self):
self.iv = urandom(AES.block_size)
def _pad(self, s):
return s + (AES.block_size - len(s) % AES.block_size) * bytes((AES.block_size - len(s) % AES.block_size, ))
def _unpad(self, s):
return s[0:-s[-1]]
def encrypt(self, msg, key):
self.cipher = AES.new(key, AES.MODE_OFB, self.iv)
return self.cipher.encrypt(self._pad(msg))
def decrypt(self, msg, key):
self.cipher = AES.new(key, AES.MODE_OFB, self.iv)
return self._unpad(self.cipher.decrypt(msg))
def main():
with open('admin.key', 'rb') as f:
admin_key = f.read(bs)
print('Welc.')
pub = Pubcipher()
sy = Sycipher()
print(pub._rsa.e)
print(pub._rsa.n)
while True:
choice = input("[t]icke or [k]ey or [f]lag :>>")
if not choice:
break
if choice[0] == 't':
name = input('ur name:>>')
if len(name) > 50:
exit()
name = bytes(name, 'ISO-8859-1')
pw = input('ur 16bytes passwd:>>')
if len(pw) != bs:
exit()
pw = bytes(pw, 'ISO-8859-1')
tic = name + pw + urandom(bs) + admin_key
try:
tic = pub.encrypt(tic)
except ValueError:
print('Plz input again. Remove space plz.')
continue
print(tic.hex())
elif choice[0] == 'k':
tmp_k = input('Input the key:>>')
tmp_k = bytes.fromhex(tmp_k)
if tmp_k != admin_key:
exit()
print(admin_key.hex())
elif choice[0] == 'f':
name = input('ur name:>>')
if len(name) > 50:
exit()
name = bytes(name, 'ISO-8859-1')
pw = input('ur 16bytes passwd:>>')
if len(pw) != bs:
exit()
pw = bytes(pw, 'ISO-8859-1')
tic = input('ur ticket:>>')
try:
tic = bytes.fromhex(tic)
except Exception:
continue
p = pub.decrypt(tic)
try:
_name = p[-4 * bs:-3 * bs]
_pw = p[-3 * bs:-2 * bs]
_admin_key = p[-bs:]
seed = p[-2 * bs:-bs]
if _name != name or _pw != pw or _admin_key != admin_key:
raise ValueError
except Exception:
print('u input wrong passwd or username')
continue
key = xor(seed, pw)
with open('attach.bin', 'rb') as f:
flag = f.read()
flag = sy.encrypt(flag, key)
print(flag.hex())
else:
exit()
if __name__ == '__main__':
main()
step3 PKCS#1 v1.5 decrypt oracle
Between 106th line-116th line:
try:
_name = p[-4 * bs:-3 * bs]
_pw = p[-3 * bs:-2 * bs]
_admin_key = p[-bs:]
seed = p[-2 * bs:-bs]
if _name != name or _pw != pw or _admin_key != admin_key:
raise ValueError
except Exception:
print('u input wrong passwd or username')
continue
As the decrypt function for pkcs is initialize like this:
def decrypt(self, ciphertext):
return self._pkcs.decrypt(ciphertext, None)
It won't raise an error when PKCS detect some error for padding, decrypt function will just return None, and when it try to _name = p[-4 * bs:-3 * bs]
, an error will raise.
SO here is a decrypt oracle. We can leak the information about whether the first 2 bytes is '0002'. Actualle, it is an old attack. Chosen Ciphertext Attacks Against Protocols Based on the RSA Encryption Standard PKCS #1
Why I choose this one? Because this paper. Real world never realize the danger from cryptography bug.