Static Client 2
Description
Write Up: offpath
Difficulté: Medium - Hard
Points: 120
Format du flag: crypto{flag}
Enoncé
Le coffre-fort sécurisé de Cryptohack nécessite deux clés pour déverrouiller son secret. Cependant, Jack et Hyperreality ne se souviennent pas des clés, seulement du début de l'une d'entre elles. Pouvez-vous aider à retrouver les clés perdues pour déverrouiller le coffre-fort ?
Cryptohack's secure safe requires two keys to unlock its secret. However, Jack and Hyperreality can't remember the keys, only the start of one of them. Can you help find the lost keys to unlock the safe?
Solution détaillée
Dans ce challenge, nous avons une situation où Bob et Alice échangent des paramètres pour établir une clé secrète partagée via le protocole Diffie-Hellman. Bob a introduit certaines précautions, mais il semble qu'il pourrait avoir manqué un détail crucial.
Aperçu du Contexte
Le protocole Diffie-Hellman permet à deux parties de générer une clé secrète partagée sur un canal public. Les paramètres publics incluent :
- p : un grand nombre premier.
- g : une base (ou générateur).
Chaque partie choisit un secret privé (a pour Alice et b pour Bob) et échange les valeurs calculées suivantes :
- A=ga % p : envoyé par Alice à Bob.
- B=gb % p: envoyé par Bob à Alice.
La clé partagée est ensuite calculée comme :
- s=Ba % p pour Alice.
- s=Ab % p pour Bob.
Analyse et Solution
Bob insiste pour utiliser les valeurs ppp et ggg fournies par son partenaire. Notre objectif est de trouver une faille dans cette approche pour récupérer le drapeau.
- Trouver un ppp lisse :
- Un p lisse (slice) est un nombre premier tel que p−1p-1p−1 a seulement de petits facteurs premiers. Cela facilite la résolution du logarithme discret, rendant ainsi l'attaque possible.
- Calcul du logarithme discret :
- Avec un p lisse, on peut résoudre b dans B=gb % p de manière efficace.
- Générer la clé partagée :
- Une fois b trouvé, on peut calculer la clé partagée et déchiffrer le message.
Le Code de la Solution
Voici le code Python pour exploiter la faille et récupérer le flag :
import hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from pwn import *
from Crypto.Util.number import *
### From Alice ###
p = 0xffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff
g1 = 0x2
A1 = 0x913d73278790e0d0562142e1718a9a9e3488da96f7c954c42ac57e177df6dd8d70c57be883083a078634e3e38de1e06ef38b925f4fb82aee4ea773360e22ce8db93b9bd77424aaf10a2fa6caee41ca3590c9d139418ae3de2684704ba50111f728346cb8deb87e246b46e99645096b415408bd29cdf9e243937524fb948126ffc9e6f48cc6e038687b19014346671833fb4c7cc987edb6b32b0873a258af492d4eb865fcce0ed804ba3cb5d475771c34ce36a55615fb8d261d5cee96ce0bbb85
### From Bob ###
B1 = 0xd0d69585c6586c3b1a23e04245826be6db4aed1c9bc70f7110a30165ca878d31434aa357c2bd26d3c398284a17319504e1aeead141234afeb57dfef11417fdec44b21cea83920f300f4e0c3fb573a895371b24652c5e6ea0539b7719f0f966ac7adb9a292cc49f4d8b39560e02fa82aab3c273cc7df512a80e2de6f0e8840c00554f09460eaa2e221173a9ca13182d4e1342b1e54965e16ca5fc23b1aae80aedc7fb80e1aa9be8b0274812676e8e570e1abf65eea0c49f18794a5afba975c7c7
### From Alice ###
iv_from_alice = "f314363b6b20484963648c2968be2234"
c_from_alice = "0ae0fd4bc7ecfaab5219cdbcd705af36790a501d0854efd60a964df6d638bc73c2e9b73c4886a76daf018093419330a2"
### Bob connects to you, send him some parameters: ###
def find_a_smooth():
p_smooth = 1
i = 1
while True:
p_smooth *= i
if (p_smooth + 1).bit_length() >= p.bit_length() and isPrime(p_smooth + 1):
return p_smooth + 1
i += 1
p_smooth = find_a_smooth()
g = 0x02
A = 0x913d73278790e0d0562142e1718a9a9e3488da96f7c954c42ac57e177df6dd8d70c57be883083a078634e3e38de1e06ef38b925f4fb82aee4ea773360e22ce8db93b9bd77424aaf10a2fa6caee41ca3590c9d139418ae3de2684704ba50111f728346cb8deb87e246b46e99645096b415408bd29cdf9e243937524fb948126ffc9e6f48cc6e038687b19014346671833fb4c7cc987edb6b32b0873a258af492d4eb865fcce0ed804ba3cb5d475771c34ce36a55615fb8d261d5cee96ce0bbb85
### From Bob ###
B = 0x56c2f62b18614911dfca2942d1c5f60d84f2d95dde472190424658940f9fd7aeb5af05432b2e4db94feb69040a7d7d39700463f4e1c98903edc7740e2f72eb7beea78206c0a1cc80c7adc5de2a1599e5cfcc183a790d8ee4cb009627899341298722cc2fb9c30f7ce78fad0ed743fa227341d4085335504a26febca986e25bb8fab55d0b3396e49d4db63a52ac33f0a31b6d8a55e7902dce7b8ee66a3d338a2d62d647297adcc7fd1007bb1be74db7c7af9d6374db3f3950dad7895c2244ff40e2d1ae577740ef672b0c29d97a8175dc34a8e2a014a8e279fed464fbbd9e79109ec2aba2cefddfedfc092c0dd48e8d276da5e3b04959bf8829bc8187215cc6cc063842bc3ac69d5b62eddfc00f1fe2678c65656d
iv = "e677753ebf602eb55e065d9cda992f2c"
c = "29e7bec1d207037d148553c1509becf01172bf9a9bb4556bcf0133dc0480b020fb05d22341381961ee0a710e98adb4f823ee74b863698426ee00320ec95d0dfbd297ab8d08e0b4e5fa46b0434120648e"
# A = g^a mod p
# B = g^b mod p
#s = B^a mod p
def solve_discrete_log(p, g, B):
F = GF(p) # def finit field
g, B = F(g), F(B) # apply fint field on the value
b = discrete_log(B,g)
return b
def generate_shared_secret(A, p, b):
return pow(A, int(b), p)
def is_pkcs7_padded(message):
padding = message[-message[-1]:]
return all(padding[i] == len(padding) for i in range(0, len(padding)))
def decrypt_flag(shared_secret: int, iv: str, ciphertext: str):
# Derive AES key from shared secret
sha1 = hashlib.sha1()
sha1.update(str(shared_secret).encode('ascii'))
key = sha1.digest()[:16]
# Decrypt flag
ciphertext = bytes.fromhex(ciphertext)
iv = bytes.fromhex(iv)
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext)
if is_pkcs7_padded(plaintext):
return unpad(plaintext, 16).decode('ascii')
else:
return plaintext.decode('ascii')
b = solve_discrete_log(p_smooth, g, B)
s = generate_shared_secret(A, p_smooth, b)
print(decrypt_flag(s, iv, c))
s = solve_discrete_log(A, p, b)
print(decrypt_flag(s, iv_from_alice, c_from_alice))