Its a simple symmetric key encryption, I am sure you will be able to solve it (what do you mean the key looks weird)
Author : Revak Pandkar
challenge.py
encoded_flag.txt
encoded_key.txt
Here’s the source file:
import numpy as npimport random
polyc = [4,3,7]poly = np.poly1d(polyc)
def generate_random_number(): while True: num = random.randint(100, 999) first_digit = num // 100 last_digit = num % 10 if abs(first_digit - last_digit) > 1: return num
def generate_random_number_again(): while True: num = random.randint(1000, 9999) if num % 1111 != 0: return num
def transform(num): number = random.randint(1, 100000) org = number number *= 2 number += 15 number *= 3 number += 33 number /= 6 number -= org if number == 13: num1 = random.randint(1, 6) num2 = random.randint(1, 6) number = num1 * 2 number += 5 number *= 5 number += num2 number -= 25 if int(number / 10) == num1 and number % 10 == num2: number = generate_random_number() num1 = int(''.join(sorted(str(number), reverse=True))) num2 = int(''.join(sorted(str(number)))) diff = abs(num1 - num2) rev_diff = int(str(diff)[::-1]) number = diff + rev_diff if number == 1088: org = num num *= 2 num /= 3 num += 5 num *= 4 num -= 9 num -= org return num else: number = generate_random_number_again() i = 0 while number != 6174: digits = [int(d) for d in str(number)] digits.sort() smallest = int(''.join(map(str, digits))) digits.reverse() largest = int(''.join(map(str, digits))) number = largest - smallest i += 1
if i <= 7: org = num num *= 2 num += 7 num += 5 num -= 12 num -= org num += 4 num *= 2 num -= 8 num -= org return num else: org = num num **= 4 num /= 9 num += 55 num *= 6 num += 5 num -= 23 num -= org return num else: org = num num *= 10 num += 12 num **= 3 num -= 6 num += 5 num -= org return num else: org = num num += 5 num -= 10 num *= 2 num += 12 num -= 20 num -= org return num
def encrypt(p,key): return ''.join(chr(p(transform(i))) for i in key)
key = open('key.txt', 'rb').read()enc = encrypt(poly,key)print(enc)Basically, this file contains a lot of checks on some operations that tell us what number is returned. To be honest, this is very easily cheesed by simply reusing the transform function on every possible character, and then creating a map that maps from output of the transform function to the corresponding character. The actual intended solution is to notice that every single check is always the same result no matter what (each sequence of operations always results in the same number), so therefore it is easy to figure out what the final result should be. (Note that each check can be easily tested with short Python scripts), and develop the same map from that.
Once you have the map, it’s easy to decode the key. Now, you’re supposed to realize that the flag must first be decoded from base64 (it is clearly in the correct format) but then blindly guess (based on the fact that it’s symmetric encryption) to use AES ECB. Not exactly sure why they even required you to guess that it’s AES ECB, as there are many more symmetric cryptoschemes, but yeah.
Here’s my full implementation:
import numpy as npimport randomimport base64from Crypto.Cipher import AES
polyc = [4,3,7]poly = np.poly1d(polyc) # 4x^2 + 3x + 7
# for i in range(1, 100001):# number = i# org = number# number *= 2# number += 15# number *= 3# number += 33# number /= 6# number -= org# if(number != 13): print(org)
# for i in range(1, 7):# for j in range(1, 7):# num1 = i# num2 = j# number = num1 * 2# number += 5# number *= 5# number += num2# number -= 25# if not (int(number / 10) == num1 and number % 10 == num2):# print(num1, num2)
def generate_random_number(): # first and last digit are more than 1 apart while True: num = random.randint(100, 999) first_digit = num // 100 last_digit = num % 10 if abs(first_digit - last_digit) > 1: return num
# for i in range(1000):# number = generate_random_number()# num1 = int(''.join(sorted(str(number), reverse=True)))# num2 = int(''.join(sorted(str(number))))# diff = abs(num1 - num2)# rev_diff = int(str(diff)[::-1])# number = diff + rev_diff# if number == 1088:# print(number)
def generate_random_number_again(): # not a multiple of 1111 while True: num = random.randint(1000, 9999) if num % 1111 != 0: return num
# for a in range(100):# number = generate_random_number_again()# i = 0# while number != 6174:# digits = [int(d) for d in str(number)]# digits.sort()# smallest = int(''.join(map(str, digits)))# digits.reverse()# largest = int(''.join(map(str, digits)))# number = largest - smallest# i += 1# if i > 7: print(i)
m = dict()for i in range(118): num = i org = num num *= 2 num += 7 num += 5 num -= 12 num -= org num += 4 num *= 2 num -= 8 num -= org # print(chr(poly(num)), chr(i)) m[chr(poly(num))] = chr(i)
enc_key = open("encoded_key.txt", "r").read()
key = b''for i in enc_key: key += m[i].encode()# print()# print(m.keys())print(key)
# literally just guess it's b64 --> AESenc_flag = open("encoded_flag.txt", 'r').read()enc_flag = base64.b64decode(enc_flag)
c = AES.new(key, AES.MODE_ECB)pt = c.decrypt(enc_flag)print(pt)Run the script to get the flag!
VishwaCTF{s33_1_t0ld_y0u_1t_w45_345y}Thoughts
Crypto challenge that was secretly rev + forensics