Le petit bain

C’est clairement la suite du bébé nageur

Donc on va reprendre le même fonctionnement, le code du challenge avec mes annotations pour chaque fonction et les calculs de A et B :

import random as rd
    from flag import FLAG

    assert FLAG[:12] == "404CTF{tHe_c"

    charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_-!"
    n = len(charset)

    def f(a,b,n,x):
            return (a*x+b)%n


    # OUTPUT : C_ef8K8rT83JC8I0fOPiN6P!liE03W2NXFh1viJCROAqXb6o
    # INPOUT : 404CTF{tHe_c                                   }

    # 4 = 56
    # 28 = (a*56+b)%67
    # { = 62
    # 60 = (a*62+b)%67
    # A=50
    # B=42

    # 0 = 52
    # 64 = (a*52+b)%67
    # t = 19
    # 17 = (a*19+b)%67
    # A=40
    # B=61

    # 4 = 56
    # 4 = (a*56+b)%67
    # H = 33
    # 45 = (a*33+b)%67
    # A=39
    # B=31

    # C = 28
    # 5 = (a*28+b)%67
    # e = 4
    # 60 = (a*4+b)%67
    # A = 34
    # B = 58

    # T = 45
    # 60 = (a*45+b)%67
    # _ = 64
    # 55 = (a*64+b)%67
    # A=35
    # B=26

    # F = 31
    # 36 = (a*31+b)%67
    # c = 2
    # 35 = (a*2+b)%67
    # A=37
    # B=28

    # A = 50,40,39,34,35,37
    # B= 42,61,31,58,26,28


    # fonction Round
    # Pour i de 0 a 48
    # x c'est l'index des caractères 1 a 1
    # a c'est le premier de la liste jusqu'au 6eme
    # Pareil pour B
    # on envoi tout a la meme fonction que le challenge d'avant
    def round(message,A,B,n):
            encrypted = ""
            for i in range(len(message)):
                    x = charset.index(message[i])
                    a = A[i%6]
                    b = B[i%6]
                    x = f(a,b,n,x)
                    encrypted += charset[x]
            return permute(encrypted)

    # Fonction encryt
    # Pour K entre 0 et 6
    # A = Liste aléatoire de 6 nombre entre 2 et 67
    # B = Liste aléatoire de 6 nombre entre 1 et 67
    # Envoi le flag (encrytped), A, B et n=67
    def encrypt(message):
            encrypted = message
            for k in range(6):
                    A = [ rd.randint(2,n-1) for i in range(6)]
                    B = [ rd.randint(1,n-1) for i in range(6)]
                    encrypted = round(encrypted,A,B,n)
            return encrypted


    def permute(message):
            p = [4, 3, 0, 5, 1, 2, 10, 9, 6, 11, 7, 8, 16, 15, 12, 17, 13, 14, 22, 21, 18, 23, 19, 20, 28, 27, 24, 29, 25, 26, 34, 33, 30, 35, 31, 32, 40, 39, 36, 41, 37, 38, 46, 45, 42, 47, 43, 44]
            permuted = [ message[p[i]] for i in range(len(message))]
            return ''.join(permuted)

    print(encrypt(FLAG))

On a récupérer tous les index avec :

charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_-!"

message2 = "C_ef8K8rT83J"
message = "404CTF{tHe_c"

# Calculer et afficher les index de chaque caractère du message dans le charset
for char in message:
    index = charset.index(char)
    print(f"Le caractère '{char}' a l'index {index} dans charset.")

for char in message2:
    index = charset.index(char)
    print(f"Le caractère '{char}' a l'index {index} dans charset.")

On résoud les équations en modifiant les deux équations a chaque fois (on aurait aussi pu mettre les 6 dans le meme script) :

# Définition des équations congruentes à résoudre
n = 67  # La taille du module

# Nous parcourons toutes les valeurs de a de 1 à 66
for a in range(1, n):
    # Nous parcourons toutes les valeurs de b de 1 à 66
    for b in range(1, n):
        # Testons si les valeurs de a et b satisfont les deux équations congruentes
        equation1 = (a * 56 + b) % n == 65  # Première équation congruente
        equation2 = (a * 52 + b) % n == 56  # Deuxième équation congruente

        # Si a et b satisfont les deux équations, afficher les valeurs de a et b
        if equation1 and equation2:
            print(f"Valeurs de a et b trouvées : a = {a}, b = {b}")
            break  # Sortir de la boucle si une solution est trouvée
    else:
        # Si les valeurs de a et b ne satisfont pas les équations, passer à l'itération suivante
        continue
    # Si une solution est trouvée, sortir de la boucle principale
    break

Et on décode le tout une fois qu’on à la liste des A et des B, on sait pas pourquoi, la fonction permute sert a rien, pas besoin de la reverse :

# Définissez votre charset et n.
charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_-!"
n = len(charset)  # n = 67

# Fonction pour calculer l'inverse modulaire d'un nombre
def mod_inverse(a, n):
    t, new_t = 0, 1
    r, new_r = n, a
    while new_r != 0:
        quotient = r // new_r
        t, new_t = new_t, t - quotient * new_t
        r, new_r = new_r, r - quotient * new_r
    if r != 1:
        raise ValueError(f"Aucun inverse pour {a} modulo {n}.")
    if t < 0:
        t += n
    return t

# Fonction de déchiffrement d'un caractère
def decrypt_char(encrypted_char, a, b):
    # Trouver l'index du caractère chiffré dans le charset
    encrypted_index = charset.index(encrypted_char)

    # Calculer l'inverse de a modulo n
    inv_a = mod_inverse(a, n)

    # Déchiffrer l'index du caractère original
    original_index = (inv_a * (encrypted_index - b)) % n

    # Retourner le caractère original
    return charset[original_index]

# Fonction pour déchiffrer un message entier
def decrypt(encrypted_message, A, B):
    decrypted_message = []

    # Déchiffrer chaque caractère du message
    for i in range(len(encrypted_message)):
        # Obtenir les coefficients a et b appropriés pour la position actuelle
        a = A[i % 6]
        b = B[i % 6]

        # Déchiffrer le caractère
        decrypted_char = decrypt_char(encrypted_message[i], a, b)
        decrypted_message.append(decrypted_char)

    # Convertir la liste de caractères en chaîne de caractères
    return ''.join(decrypted_message)

# Les listes de coefficients
A = [50, 40, 39, 34, 35, 37]
B = [42, 61, 31, 58, 26, 28]

# Le message chiffré
encrypted_message = "C_ef8K8rT83JC8I0fOPiN6P!liE03W2NXFh1viJCROAqXb6o"

# Déchiffrer le message
decrypted_message = decrypt(encrypted_message, A, B)

# Afficher le message déchiffré
print("Le message déchiffré est :", decrypted_message)

Le flag : 404CTF{tHe_c4fF31ne_MakE5_m3_StR0nG3r_th4n_y0u!}