Thursday, July 11, 2019

Asis CTF Quals 2019 - A delicious soup

by Lucas "K4L1" Nathaniel

Hi everyone! C:

This is a crypto challenge that I enjoyed a lot and I wanted to make my first blog post on my blog with this writeup, so finally now I have it :D

So basically we have…

This is the flag encrypted:

11d.3ilVk_d3CpIO_4nlS.ncnz3e_0S}M_kn5scpm345n3nSe_u_S{iy__4EYLP_aAAall

and this is the code that encrypted that:

import random
from flag import flag

def encrypt(msg, perm):
	W = len(perm)
	while len(msg) % (2*W):
		msg += "."
	msg = msg[1:] + msg[:1]
	msg = msg[0::2] + msg[1::2]
	msg = msg[1:] + msg[:1]
	res = ""
	for j in xrange(0, len(msg), W):
		for k in xrange(W):
			res += msg[j:j+W][perm[k]]
	msg = res
	return msg

def encord(msg, perm, l):
	for _ in xrange(l):
		msg = encrypt(msg, perm)
	return msg

W, l = 7, random.randint(0, 1337)
perm = range(W)
random.shuffle(perm)

enc = encord(flag, perm, l)
f = open('flag.enc', 'w')
f.write(enc)
f.close()

We need to reverse this code and create a decrypt function.

Reversing…

  • So, first let’s look at the starting lines: we have a random list from 0 to 6 and a random number from 0 to 1337;
  • The encord function call encrypt function L(random number(0,1337)) times;
  • In the encrypt function, the first while will just add 2 points on encrypted flag;
  • The msg[1:] + msg[:1] will put the first char at the end;
  • The msg[0::2] + msg[1::2] will split the msg and zip;
  • The msg[1:] + msg[:1] will put the first char back at the end again;
  • The last double for will shuffle the msg with the random list logic.

Solution…

The weakness of this crypto is that the key(The random list) has 5040 possibilities, so we just need to brute it C:

We can do it with itertools.permutations

The Jurandir Solver

import itertools

def str_perm(flag, p):
	res = ""
	
	for j in xrange(0, 70, 7):
		for k in xrange(7):
			res += flag[j:j+7][p.index(k)]
	return res

def shuf(flag):
	flag = flag[-1:] + flag[:-1]
	new = ""
	
	for i, j in zip(flag[:len(flag)//2], flag[len(flag)//2:]):
		new += i+j
	flag = new
	flag = flag[-1:] + flag[:-1]
	return flag

def main():
	perm = list(itertools.permutations([0,1,2,3,4,5,6]))
	first_flag = open("flag.enc", "r").readlines()[0]
	
	for idx, p in enumerate(perm):
		flag = first_flag
		print str(idx)+"/5040"
		
		for _ in xrange(1337):
			res = str_perm(flag, p)
			res = shuf(res)

			if res.startswith("ASIS{"):
				print res
				exit(1)
			flag = res

if __name__ == "__main__":
	main()

Running the solver, after 805 iterations, we have the flag: ASIS{1n54n3ly_Simpl3_And_d3lic1Ous_5n4ckS_eVEn_l4zY_Pe0pL3_Can_Mak3}

Reference

Cryptography , Capture the Flag , Writeup