[关闭]
@iPhan 2018-07-06T14:26:28.000000Z 字数 6451 阅读 1388

hitb crypto

未分类


这是一道关于对称加密、CBC、sha256的题目

提供的功能

细节

密钥

有3个密钥:encrypt_key , mac_key, authen_key

encrypt_key 用来对用户名+类别做加密
mac_key用来在hash时加到信息前(即sha256(mac_key+message))
authen_key 用来加密hash

AES加密时均采用CBC,padding格式采用如下经典形式:

  1. def pad(s):
  2. return s + (bs - len(s) % bs) * bytes((bs - len(s) % bs, ))
  3. def unpad(s):
  4. lb = s[-1]
  5. if lb > 16 or lb < 1:
  6. raise ValueError
  7. for i in range(lb):
  8. if s[-1 - i] == lb:
  9. pass
  10. else:
  11. raise ValueError
  12. return s[0:-s[-1]]

鉴权方式

用户注册时:

注:AES加密时均采用CBC

用户登录时,服务端解密cookie中的MAC部分和用户名部分,先判断用户名部分的MAC是否匹配

攻击方式

主要思路是拿到正常用户名 + user的hash值,然后做hash扩展攻击,得到用户名 + user + \x0000000... + admin + \x10*16的hash值 (前面的\x00要补足到整个账号名是aes块长的整数倍),然后拿到新hash值与新用户名的aes密文,即可实现攻击。

code

  1. from Crypto.Cipher import AES
  2. from Crypto import Random
  3. from hashlib import sha256
  4. from signal import alarm
  5. bs = 16
  6. def pad(s):
  7. return s + (bs - len(s) % bs) * bytes((bs - len(s) % bs, ))
  8. def unpad(s):
  9. lb = s[-1]
  10. if lb > 16 or lb < 1:
  11. raise ValueError
  12. for i in range(lb):
  13. if s[-1 - i] == lb:
  14. pass
  15. else:
  16. raise ValueError
  17. return s[0:-s[-1]]
  18. class cipher(object):
  19. def __init__(self, key):
  20. self.key = key
  21. def cbcPrimeEnc(self, key, iv, plain):
  22. cipher = AES.new(key, AES.MODE_CBC, iv)
  23. plain = pad(plain)
  24. return cipher.encrypt(plain)
  25. def cbcPrimeDec(self, key, iv, ciphertext):
  26. cipher = AES.new(key, AES.MODE_CBC, iv)
  27. plain = cipher.decrypt(ciphertext)
  28. return unpad(plain)
  29. def encrypt(self, plaintext):
  30. hash = sha256(mac_key + plaintext).digest()
  31. iv = Random.new().read(bs)
  32. ciphertext = iv + \
  33. self.cbcPrimeEnc(authen_key, iv, hash) + \
  34. self.cbcPrimeEnc(self.key, iv, plaintext)
  35. return ciphertext.hex()
  36. def decrypt(self, ciphertext):
  37. cookie = bytes.fromhex(ciphertext)
  38. iv = cookie[:bs]
  39. mac = cookie[bs:4 * bs]
  40. ciphertext = cookie[4 * bs:]
  41. plaintext = self.cbcPrimeDec(self.key, iv, ciphertext)
  42. hash = self.cbcPrimeDec(authen_key, iv, mac)
  43. if sha256(mac_key + plaintext).digest() == hash:
  44. return str(plaintext, 'utf-8')
  45. else:
  46. raise Exception(
  47. sha256(mac_key + plaintext).digest().hex()[:32], hash.hex()[:32])
  48. encrypt_key = Random.new().read(bs)
  49. mac_key = Random.new().read(2 * bs)
  50. authen_key = Random.new().read(bs)
  51. with open('flag', 'r') as fp:
  52. flag = fp.readline()
  53. # alarm(20)
  54. while True:
  55. c = cipher(encrypt_key)
  56. choice = input("Please [r]egister or [l]ogin :>>")
  57. if not choice:
  58. break
  59. if choice[0] == 'r':
  60. name = input('your name is:>>').strip()
  61. name = bytes(name, 'utf-8') + b'user'
  62. if(len(name) > 64):
  63. print("username too long!")
  64. break
  65. else:
  66. print("Here is your cookie:")
  67. print(c.encrypt(name))
  68. elif choice[0] == 'l':
  69. data = input('your cookie:>>').strip()
  70. try:
  71. msg = c.decrypt(data)
  72. if msg[-4:] == 'user':
  73. print("Welcome %s!" % msg[:-4])
  74. elif msg[-4:] == 'admin':
  75. print(flag)
  76. except Exception as e:
  77. print('Wrong MAC! ')
  78. print(
  79. 'the suppposed first 128 bits of sha256 and the actual first 128 bits is:>>' + str(e))
  80. else:
  81. exit()
  82. else:
  83. print("Unknown choice!")
  84. break

another one----baby_pub

流程

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
因为初始定义时这样定义的:
  1. def decrypt(self, ciphertext):
  2. return self._pkcs.decrypt(ciphertext, None)

PKCS格式不对时不会报错,只会返回一个None。将会触发try。

因此有了选择明文攻击。

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