Tags: crypto 


#Vigenere - Crypto 100

As the title for this chall claims, this is all about Vigenere cipher. Interestingly the alphabet used is not [A-Z], but also includes '{' and '}'. Besides giving us a full Vigenere table, the chall also provides some information about the key, the plaintext and the ciphertext.

k: ????????????
p: SECCON{???????????????????????????????????}

k=key, p=plain, c=cipher, md5(p)=f528a6ab914c1ecf856a1d93103948fe

From now on we are going to assume *len(k)==12*. Our main goal is clearly to find *p*. Since we have the first 7 chars of *p* we could easily find the first 7 chars of *c*. We could even use the given table and do it manually. For the first char of *p*, *p[0]=='S'*, we would check the row of the table corresponding to *S*. Since *S* gets mapped to *c[0]=='L'*, we look for *L* in this row, which is in the column of *V*. The figure below ilustrates this process:


pt = "SECCON{"
ct = "LMIG}RP"

res = ""
for p,c in zip(pt, ct):
res += chr( ord('A') + ( (ord(c) - ord(p)) % 28) )


This simple procedure results in *VIGESEN* as the first part of the key. Evidently the chars '{' and '}' are not being correctly treated in positions 5 and 7. Either by correcting them manually or by guessing, we might deduce that *VIGENER* is indeed the first part. It is not hard to find that *VIGENERE* are the first 8 chars of the key.

We have the following result so far:


It seems the alphabet is part of *p*. After *G* there might be *HIJ_* or maybe *_HIJ*. The same goes for *STU_* or *_STU* right after *R*. Before going for a bruteforce solution we decided to test a few possibilities manually. We tried *H* in position 23 and *S* in position 32. Surprisingly, we got *C* as the result for the key in both cases. Certainly a good sign. Trying the other chars we got *VIGENERECOD_* for the key. Not hard to guess the answer should be *VIGENERECODE*, proving our first guess was correct!

With the key in hands all we had to do was decode the ciphertext in order to obtain **SECCON{ABABABCDEDEFGHIJJKLMNOPQRSTTUVWXYYZ}**.

Although we used a lot of guessing to make things quicker, our next approach would be bruteforcing the given md5 hash. In fact, we decided to confirm our guesses with a little coding:

import itertools
import hashlib
import binascii

hashChall = "f528a6ab914c1ecf856a1d93103948fe"
res = ""

# the range could include the whole alphabet for more extensive search
for a in itertools.product("AB",repeat=4):
for b in itertools.product("HIJJ",repeat=4):
for c in itertools.product("STTU",repeat=4):
pt = "SECCON{A"+"".join(a)+"BCDEDEFG"+"".join(b)+"KLMNOPQR"+"".join(c)+"VWXYYZ}"
hashTst = hashlib.md5(pt.encode('utf-8')).hexdigest()
if hashTst == hashChall:
res = pt

Original writeup (https://github.com/pogTeam/writeups/blob/master/2016/seccon/Vigenere/README.md).