Skip to main content

(Facile) Un simple Oracle 1/2

Description

Lors de la connexion au serveur nous obtenons les informations suivantes :

image-1654947239345.png



Nous sommes ici sur un challenge de Cryptanalyse avec ce qui semble être du RSA.

Théorie

Avec le chiffrement RSA, la clé publique correspond à (d,N) et la clé privée  à (e,N)

Pour calculer C le chiffré d'un message M nous réalisons l'opération suivante : 

C = Me [N]

Pour déchiffrer le message on réalise alors l'opération :

M = Cd [N]

Cela repose sur le fait que :

Xed = X [N]

Analyse

Le serveur attent un retour de notre part. On va alors "jouer" un peu avec lui afin de voir son comportement. Le but est de deviner ce que le serveur fait.

  • Essayons de lui envoyer le message chiffré :

    image-1654947565628.png

    Comme on peut s'y attendre le serveur refuse de nous répondre. On peut alors supposer que le serveur déchiffre notre message et nous renvoie la solution.

Pour vérifier cette théorie nous allons nous même chiffrer un message afin de l'envoyer au serveur pour voir si il nous renvoie notre message de départ.

On réalise alors un début de script :

import socket

client = socket.socket()
client.connect(('challenge.404ctf.fr',32128))
init = client.recv(4096).split(b'\n')

# Permet de bypass les problèmes de buffer si jamais les informations envoyés dépassent les 4096 caractères
while init[-1] != b'> ':
    l = client.recv(4096).split(b'\n')
    init[-1] += l[0]
    init = init + l[1:]

cypher = int(init[1])
N = int(init[3].split(b" = ")[1])   
e = int(init[4].split(b" = ")[1])

C = 2**e
s = str(C) + "\n"
client.send(s.encode())
s = client.recv(4096)
print(s)

Le serveur nous renvoie 2 on a donc bien la confiramtion du fait qu'il déchiffre le message envoyé.

Solution

Il existe alors une attaque très simple à réaliser lorsque l'on connait le chiffré ainsi que l'exposant de chiffrement e et qu'on a à disposition un serveur qui permet de déchiffrer un message.

Notons notre chiffré. Nous allons calculer C2 tel que :

C2 = 2e * C [N]

Lors de l'envoie au serveur, ce dernier va alors nous renvoyer :

C2d = (2e * C)d = 2ed * Cd = 2 * M [N]

On va donc non pas obtenir notre message M mais 2M. Il suffit alors de diviser notre message par 2 pour obtenir notre message de départ !

On modifie donc notre code :

import socket

client = socket.socket()
client.connect(('challenge.404ctf.fr',32128))
init = client.recv(4096).split(b'\n')

# Permet de bypass les problèmes de buffer si jamais les informations envoyés dépassent les 4096 caractères
while init[-1] != b'> ':
    l = client.recv(4096).split(b'\n')
    init[-1] += l[0]
    init = init + l[1:]

cypher = int(init[1])
N = int(init[3].split(b" = ")[1])   
e = int(init[4].split(b" = ")[1])

c2 = 2**e
s = str(c2 * cypher) + "\n"
client.send(s.encode())
s = client.recv(4096)
p = str(int(int(s.split(b'\n')[1]) // 2))
print(p)

On obtient donc : 435443464152558712693184048274146959862522804856095627166033329707349306547937972259561846027645

Il faut maintenant récupérer le flag em partant de ce nombre. Pour cela rien de plus simple, il suffit de calculer l'hexa correspondant et de split le résultat par octet. Chaque octet correspondant à un caractère ASCII.

Pour cela on rajoute ça à la fin de notre code :

bytes_object = bytes.fromhex(hex(int(p))[2:])
ascii_string = bytes_object.decode("ASCII")
print(ascii_string)

On obtient alors notre flag : 

404CTF{L3s_0r4cl3s_RSA_s0n7_si_fr4g1l35}

Code complet

import socket

client = socket.socket()
client.connect(('challenge.404ctf.fr',32128))
init = client.recv(4096).split(b'\n')

# Permet de bypass les problèmes de buffer si jamais les informations envoyés dépassent les 4096 caractères
while init[-1] != b'> ':
    l = client.recv(4096).split(b'\n')
    init[-1] += l[0]
    init = init + l[1:]

cypher = int(init[1])
N = int(init[3].split(b" = ")[1])   
e = int(init[4].split(b" = ")[1])

c2 = 2**e
s = str(c2 * cypher) + "\n"
client.send(s.encode())
s = client.recv(4096)
p = str(int(int(s.split(b'\n')[1]) // 2))
bytes_object = bytes.fromhex(hex(int(p))[2:])
ascii_string = bytes_object.decode("ASCII")
print(ascii_string)