We are given a python script which encrypts a given plaintext using the given key. First, it creates a 2d-array of alphabets by cyclic shifting. It shifts the alphabet to the left by i+j. Due to this, t[i][j][k] = t[i][k][j]. While encrypting the string it uses k1 and k2 where k1 is the key and k2 is the reverse of the key. Since we have the first 7 characters of the plaintext, which is “SECCON{“, we find a valid key’s first 7 and last 7 characters by brute forcing. When we count the amount of *s in the description, the key’s length looks like 14. If it is the real case, then we can decrypt the ciphertext with the valid key we found.

Here is my script to find a valid key.

getkey.py

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

#!/usr/bin/env python

importsys

def_l(idx,s):

returns[idx:]+s[:idx]

defmain():

s="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}"

t=[[_l((i+j)%len(s),s)forjinrange(len(s))]foriinrange(len(s))]

key_pairs=[]

cipher="POR4dnyTLHBfwbxAAZhe}}ocZR3Cxcftw9"

plain="SECCON{**************************}"

forkinxrange(7):

found=False

index=s.find(plain[k])

foriinxrange(len(s)):

iffound:

break

forjinxrange(len(s)):

ift[index][i][j]==cipher[k]:

key_pairs.append((s[i],s[j]))

found=True

key=['*']*14

foriinxrange(len(key_pairs)):

key[i],key[13-i]=key_pairs[i][0],key_pairs[i][1]

return''.join(key)

printmain()

Let’s run it and get our key.

1

2

$python getkey.py

AAAAAAA_aZ2PK_

Now, we can modify the encryption code to brute force plaintext characters using the key we just found.

decrypt.py

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#!/usr/bin/env python

importsys

def_l(idx,s):

returns[idx:]+s[:idx]

defmain():

s="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}"

t=[[_l((i+j)%len(s),s)forjinrange(len(s))]foriinrange(len(s))]

i1=0

i2=0

plain=''

cipher="POR4dnyTLHBfwbxAAZhe}}ocZR3Cxcftw9"

key="AAAAAAA_aZ2PK_"

k1=key

k2=key[::-1]

foriinxrange(len(cipher)):

forjinxrange(len(s)):

ift[j][s.find(k1[i1])][s.find(k2[i2])]==cipher[i]:

plain+=s[j]

break

i1=(i1+1)%len(k1)

i2=(i2+1)%len(k2)

returnplain

printmain()

Finally, let’s run it and decrypt the ciphertext.

1

2

$python decrypt.py

SECCON{Welc0me_to_SECCON_CTF_2017}

Here we get SECCON{Welc0me_to_SECCON_CTF_2017}.

Umut Barış Öztunç

Security researcher who participates in Capture The Flag events, also the founder of BreakPoint CTF team.