Custom encryption

Challenge Overview

Can you get sense of this code file and write the

function that will decode the given encrypted file

content.

Find the encrypted file here flag_info and code file

might be good to analyze and get the flag.

Solution

To get the files: wget https://artifacts.picoctf.net/c_titan/18/enc_flag https://artifacts.picoctf.net/c_titan/18/custom_encryption.py

Here was the script to get the flag:

from random import randint
import sys


def generator(g, x, p):
    return pow(g, x) % p


def encrypt(plaintext, key):
    cipher = []
    for char in plaintext:
        cipher.append(((ord(char) * key*311)))
    return cipher


def is_prime(p):
    v = 0
    for i in range(2, p + 1):
        if p % i == 0:
            v = v + 1
    if v > 1:
        return False
    else:
        return True


def dynamic_xor_encrypt(plaintext, text_key):
    cipher_text = ""
    key_length = len(text_key)
    for i, char in enumerate(plaintext[::-1]):
        key_char = text_key[i % key_length]
        encrypted_char = chr(ord(char) ^ ord(key_char))
        cipher_text += encrypted_char
    return cipher_text

def dynamic_xor_decrypt(plaintext, text_key):
    cipher_text = ""
    key_length = len(text_key)

    for i, char in enumerate(plaintext[::-1]):
        key_char = text_key[i % key_length]
        encrypted_char = chr(ord(char) ^ ord(key_char))
        cipher_text += encrypted_char

    plaintext = cipher_text
    cipher_text = ""

    for i, char in enumerate(plaintext[::-1]):
        key_char = text_key[i % key_length]
        encrypted_char = chr(ord(char) ^ ord(key_char))
        cipher_text += encrypted_char

    plaintext = cipher_text
    cipher_text = ""

    for i, char in enumerate(plaintext[::-1]):
        key_char = text_key[i % key_length]
        encrypted_char = chr(ord(char) ^ ord(key_char))
        cipher_text += encrypted_char
    
    return cipher_text


def test(plain_text, text_key):
    p = 97
    g = 31
    if not is_prime(p) and not is_prime(g):
        print("Enter prime numbers")
        return
    a = randint(p-10, p)
    b = randint(g-10, g)
    print(f"a = {a}")
    print(f"b = {b}")
    u = generator(g, a, p)
    v = generator(g, b, p)
    key = generator(v, a, p)
    b_key = generator(u, b, p)
    shared_key = None
    if key == b_key:
        shared_key = key
    else:
        print("Invalid key")
        return
    semi_cipher = dynamic_xor_encrypt(plain_text, text_key)
    cipher = encrypt(semi_cipher, shared_key)
    print(f'cipher is: {cipher}')

def decrypt(cipher, key):
    plaintext = ""
    for encrypted_value in cipher:
        decrypted_value = encrypted_value // (key * 311)
        plaintext += chr(decrypted_value)
    return plaintext

def test2():
    p = 97
    g = 31
    a = 94
    b = 29

    u = generator(g, a, p)
    v = generator(g, b, p)
    key = generator(v, a, p)
    b_key = generator(u, b, p)

    shared_key = None
    if key == b_key:
        shared_key = key
    else:
        print("Invalid key")
        return

    cipher = [260307, 491691, 491691, 2487378, 2516301, 0, 1966764, 1879995, 1995687, 1214766, 0, 2400609, 607383, 144615, 1966764, 0, 636306, 2487378, 28923, 1793226, 694152, 780921, 173538, 173538, 491691, 173538, 751998, 1475073, 925536, 1417227, 751998, 202461, 347076, 491691]

    semi_cipher = decrypt(cipher, shared_key)

    flag = dynamic_xor_decrypt(semi_cipher, "trudeau")

    print(flag)


if __name__ == "__main__":
    # message = sys.argv[1]
    # test(message, "trudeau")
    test2()

The functions that were added were dynamic_xor_decrypt, decrypt, and test2. `

The dynamic_xor_decrypt function is the same as dynamic_xor_encrypt but runs three times.

The decrypt function was derived from the encrypt function and it is just the reversed process.

For the test2 function there is a use of the previous 2 functions discussed and some values are needed. These are provided from enc_flag which gives a and b as well as the cipher while p and g were received from the test function that was used for encryption. The rest is very similar to the original test function other than for semi_cipher using the decrypt function and for the final flag the dynamic_xor_decrypt function.

Lastly, since the values were hard-coded into the test2 function the changes to the main function were to statically call test2() to get the flag.

Flag: picoCTF{custom_d2cr0pt6d_751a...}