[关闭]
@iPhan 2018-07-06T14:26:00.000000Z 字数 2971 阅读 609

baby_pub write up


WP

  1. from Crypto.PublicKey import RSA
  2. from Crypto.Cipher import PKCS1_v1_5, AES
  3. from Crypto.Util.number import bytes_to_long, long_to_bytes
  4. from os import urandom
  5. bs = 16
  6. def xor(x, y):
  7. return bytes(a ^ b for a, b in zip(x, y))
  8. class Pubcipher(object):
  9. def __init__(self):
  10. self._rsa = RSA.generate(1024)
  11. self._pkcs = PKCS1_v1_5.new(self._rsa)
  12. def encrypt(self, msg):
  13. '''msg must be bytes'''
  14. return self._pkcs.encrypt(msg)
  15. def decrypt(self, ciphertext):
  16. return self._pkcs.decrypt(ciphertext, None)
  17. class Sycipher(object):
  18. def __init__(self):
  19. self.iv = urandom(AES.block_size)
  20. def _pad(self, s):
  21. return s + (AES.block_size - len(s) % AES.block_size) * bytes((AES.block_size - len(s) % AES.block_size, ))
  22. def _unpad(self, s):
  23. return s[0:-s[-1]]
  24. def encrypt(self, msg, key):
  25. self.cipher = AES.new(key, AES.MODE_OFB, self.iv)
  26. return self.cipher.encrypt(self._pad(msg))
  27. def decrypt(self, msg, key):
  28. self.cipher = AES.new(key, AES.MODE_OFB, self.iv)
  29. return self._unpad(self.cipher.decrypt(msg))
  30. def main():
  31. with open('admin.key', 'rb') as f:
  32. admin_key = f.read(bs)
  33. print('Welc.')
  34. pub = Pubcipher()
  35. sy = Sycipher()
  36. print(pub._rsa.e)
  37. print(pub._rsa.n)
  38. while True:
  39. choice = input("[t]icke or [k]ey or [f]lag :>>")
  40. if not choice:
  41. break
  42. if choice[0] == 't':
  43. name = input('ur name:>>')
  44. if len(name) > 50:
  45. exit()
  46. name = bytes(name, 'ISO-8859-1')
  47. pw = input('ur 16bytes passwd:>>')
  48. if len(pw) != bs:
  49. exit()
  50. pw = bytes(pw, 'ISO-8859-1')
  51. tic = name + pw + urandom(bs) + admin_key
  52. try:
  53. tic = pub.encrypt(tic)
  54. except ValueError:
  55. print('Plz input again. Remove space plz.')
  56. continue
  57. print(tic.hex())
  58. elif choice[0] == 'k':
  59. tmp_k = input('Input the key:>>')
  60. tmp_k = bytes.fromhex(tmp_k)
  61. if tmp_k != admin_key:
  62. exit()
  63. print(admin_key.hex())
  64. elif choice[0] == 'f':
  65. name = input('ur name:>>')
  66. if len(name) > 50:
  67. exit()
  68. name = bytes(name, 'ISO-8859-1')
  69. pw = input('ur 16bytes passwd:>>')
  70. if len(pw) != bs:
  71. exit()
  72. pw = bytes(pw, 'ISO-8859-1')
  73. tic = input('ur ticket:>>')
  74. try:
  75. tic = bytes.fromhex(tic)
  76. except Exception:
  77. continue
  78. p = pub.decrypt(tic)
  79. try:
  80. _name = p[-4 * bs:-3 * bs]
  81. _pw = p[-3 * bs:-2 * bs]
  82. _admin_key = p[-bs:]
  83. seed = p[-2 * bs:-bs]
  84. if _name != name or _pw != pw or _admin_key != admin_key:
  85. raise ValueError
  86. except Exception:
  87. print('u input wrong passwd or username')
  88. continue
  89. key = xor(seed, pw)
  90. with open('attach.bin', 'rb') as f:
  91. flag = f.read()
  92. flag = sy.encrypt(flag, key)
  93. print(flag.hex())
  94. else:
  95. exit()
  96. if __name__ == '__main__':
  97. main()
  1. try:
  2. _name = p[-4 * bs:-3 * bs]
  3. _pw = p[-3 * bs:-2 * bs]
  4. _admin_key = p[-bs:]
  5. seed = p[-2 * bs:-bs]
  6. if _name != name or _pw != pw or _admin_key != admin_key:
  7. raise ValueError
  8. except Exception:
  9. print('u input wrong passwd or username')
  10. continue
As the decrypt function for pkcs is initialize like this:
  1. def decrypt(self, ciphertext):
  2. 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.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注