Well, this was not the busiest of weekends but still we didn't have much time for the CTF. Here is a small write up on crypto 200 challenge that we solved. A python source code was given which had the key generation and crypt algorithm. Below is the encryption algorithm

[*] Creates a 8 bitstring of each character in the plain text

[*] Splits the bitsring into blocks of 128 bits

[*] Pair the bitstring list with the 128 element public key list

[*] sum(map(lambda x: x[0] * x[1] ,zip(blk, public_key))) performs multiplication operation on each tuple pair and then adds them together

We were given a cipher text list with 10 elements ie. from the algorithm we can interpret that the plain text is 160 characters, 16 chars per block. Though we understood the code, we failed to recognize the crypto system until our teammate Aghosh figured out that its knapsack public key cryptosystem. First we looked at cryptanalysis of the system, this didn't work out. Later we figured out that the generated public_key is not random and can be used to retrieve the private key.

Key Generation Algorithm:

def crypt(open_text, public_key): bts = [] [bts.extend([int(b) for b in '00000000'[len(bin(ord(c))[2:]):] + bin(ord(c))[2:]]) for c in open_text] return [sum(map(lambda x: x[0] * x[1] ,zip(blk, public_key))) for blk in [bts[i * 128:(i+1) * 128] for i in range(len(open_text) // 16)]]Algorithm performs the following operations:

[*] Creates a 8 bitstring of each character in the plain text

[*] Splits the bitsring into blocks of 128 bits

[*] Pair the bitstring list with the 128 element public key list

[*] sum(map(lambda x: x[0] * x[1] ,zip(blk, public_key))) performs multiplication operation on each tuple pair and then adds them together

We were given a cipher text list with 10 elements ie. from the algorithm we can interpret that the plain text is 160 characters, 16 chars per block. Though we understood the code, we failed to recognize the crypto system until our teammate Aghosh figured out that its knapsack public key cryptosystem. First we looked at cryptanalysis of the system, this didn't work out. Later we figured out that the generated public_key is not random and can be used to retrieve the private key.

Key Generation Algorithm:

def generate_public_key(private_key): n = 199285318978668966527551342512997250816637709274749259983292077699440369 t = 32416190071 return list(map(lambda x: (t * x) % n, private_key))This is what we got when analysing the public_key list:

sage: factor(1050809378719198985041) 32416190071^2 sage: factor(2101618757438397970082) 2 * 32416190071^2 sage: factor(6304856272315193910246) 2 * 3 * 32416190071^2 sage: factor(18914568816945581730738) 2 * 3^2 * 32416190071^2 sage: factor(56743706450836745192214) 2 * 3^3 * 32416190071^2 sage: factor(170231119352510235576642) 2 * 3^4 * 32416190071^2Looks like the private_key is generated based on the given 't' value. This is how we generated the private_key list

private_key = [t] for i in range(0,127): private_key.append(2 * 3**i * t)The generated private keys can be verified using the given generate_public_key() function. We got a match. Then we wrote the routine for inverse knapsack sum to get the flag. Here is the final code:

#!/usr/bin/env python import sys n = 199285318978668966527551342512997250816637709274749259983292077699440369 t = 32416190071 t_inv = 3607086840002694423309872675805192458275553329397325526691156370525160 #sage: inverse_mod(t,n) private_key = [t] for i in range(0,127): private_key.append(2 * 3**i * t) cipher_text = [ 1387977778999926469357780220487630125151962348185941993910077394771302677, 1192236960845781949613172279312582839292898077268409678421304772227241438, 1295152741157953792099179799985052248167548374589648818528421499250916999, 828724787424187908000366458781164595076558885463707900320215679908512646, 1179926879709109047038661681316962368287877340885819899201698611790531134, 965171312356484716595815857561729919489896088681139239093293829323154838, 1099367377207651843612375443021502714028353049437532392256489525051038347, 1374891605015623267623322424512489936836885983493815443812918444687247914, 1152880248428103510879661300981452627677389745079857275081612568623556291, 962409003220820525413536841942678826309419979028794415812582008820263317 ] def decrypt(cipher, private_key): # inverse knapsack bts = ['0']*128 # generate bit string for i in range(len(private_key) - 1, -1, -1): if cipher >= private_key[i]: bts[i] = '1' cipher = cipher - private_key[i] for j in range(0,128,8): # generate chars out of every 8-bits sys.stdout.write(chr(int(''.join(bts[j:j+8]),2))) for c in cipher_text: # iterate through 10 blocks decrypt((c * t_inv) % n,private_key) print ''Running the code, we got the flag:

**bf145f32d9637c37de3cb5b470b2c44f8e466bc26e06725ac1517006ab70eac23907a37da8715981c7de813dc03f8104381eadf57d50dc4841d7956fffcd22eefe8ec62e9dea680f78d18352b4bb3ec2**
## No comments :

## Post a Comment