~$ Dissecting the KnightCTF Cryptography challenges!

Posted on Jan. 30th, 2022. | Est. reading time: 10 minutes

Tags:CTFCryptographyWrite Up


Write-ups


Alphabet Knock Code (100 points)

The challenge provided a file called knock.txt, which contained the following text:

```txt ... ...... . ..... . ... ... ..... . ..... .... .. . ... ... . .. ... .. . .. .. .... .. ```

The description for the challenge was the following:

My friend send me this file and told me that C=K, Y=Z and the total number is 24. But I can not understand this. Can you help me?

From the C=K bit, we can extrapolate that this is a variation on the Tap Code, which is a code that is alleged to have been used by prisoners in order to communicate with one another.

The normal tap code works in a 5-by-5 grid like such:

12345
1ABC/KDE
2FGHIJ
3LMNOP
4QRSTU
5VWXYZ

The key to interpreting this cipher is not in the individual taps, but in the pairs of taps: the first set of taps specifies the line of the table, and the second set defines the column of the table, which together point to a letter.

That's all fine and dandy, but we're told the total number is 24, and not 25 like in the 5-by-5 grid. As such we look at the multipliers that make up 24: 2-by-12, 3-by-8 and 4-by-6 (and their transpose matrices).

To get a better idea, we can look at the amount of dots that we have in the provided ciphertext: The first set goes from 1 to 4, the second goes from 1 to 6.

This means we're dealing with a matrix with 4 lines and 6 columns, with two equivalencies (C=K and Y=Z):

123456
1ABC/KDEF
2GHIJLM
3NOPQRS
4TUVWXY/Z

From there we are able to split the message into (row, column) pairs:

```txt ... ...... -> 3,6 . ..... -> 1,5 . ... -> 1,3 ... ..... -> 3,5 . ..... -> 1,5 .... .. -> 4,2 . ... -> 1,3 ... . -> 3,1 .. ... -> 2,3 .. . -> 2,1 .. .. -> 2,2 .... .. -> 4,2 ```

By transposing the pairs with the values in our matrix, we get:

```txt ... ...... -> 3,6 -> s . ..... -> 1,5 -> e . ... -> 1,3 -> c/k ... ..... -> 3,5 -> r . ..... -> 1,5 -> e .... .. -> 4,2 -> u . ... -> 1,3 -> c/k ... . -> 3,1 -> n .. ... -> 2,3 -> i .. . -> 2,1 -> g .. .. -> 2,2 -> h .... .. -> 4,2 -> u ```

This spells out se(c|k)reu(c|k)nighu. This... doesn't make much sense, but we can already guess the result as being secretknight. There must have been an issue with the encryption script.

The final flag was: KCTF{secretknight}.


Feistival (150 points)

The challenge provided a file called cipher.txt, which contained the following text:

```txt H@WExefjpwfo\x015(#\x06\x16\x02#\x00\x15\x07\x04 ```

It also provided a Python file called enc.py which contained the following script:

```py m, n = 21, 22 def f(word, key): out = "" for i in range(len(word)): out += chr(ord(word[i]) ^ key) return out flag = open("flag.txt", "r").read() L, R = flag[0:len(flag)//2], flag[len(flag)//2:] x = "".join(chr(ord(f(R, m)[i]) ^ ord(L[i])) for i in range(len(L))) y = f(R, 0) L, R = y, x x = "".join(chr(ord(f(R, n)[i]) ^ ord(L[i])) for i in range(len(L))) y = f(R, 0) ciphertext = x + y ct = open("cipher.txt", "w") ct.write(ciphertext) ct.close() ```

Finally, the description for the challenge was the following:

My friend Ridwan is interested into ciphers lately. He sent me the encrypted flag. Can you help me decrypt?

Before launching ourselves in the reversal / decryption process, let us decompose some of the code:

  1. Line 2 provides us with a function which takes as input a string of characters (word) and a secondary value (key).
    This function will take the word, convert each character to its ASCII value, XOR that value with the key, then convert it back into ASCII letters.
    This is the Feistel operation
  2. Line 8 opens up the ciphertext and extracts the encrypted flag from within.
  3. Line 10 splits the flag in 2 distinct parts L and R.
  4. Line 11 performs the Feistel operation on R, then XORs each of the characters of the result with the value at the same index in L.
  5. Line 12 performs the Feistel operation on R with value 0.
  6. Line 14 flips switches L and R, and the next two lines repeats step 4.
  7. The new flag is written to a new file.

This is in the Cryptography category because it deals with a Feistel Cipher, but it also has a lot to do with programming. We createt dec.py to perform the opposite operations on the provided ciphertext:

```py def f(word, key): out = "" for i in range(len(word)): out += chr(ord(word[i]) ^ key) return out m, n = 21, 22 ct = open("cipher.txt", "r") ciphertext = ct.read() ct.close() L, R = ciphertext[0:len(ciphertext)//2], ciphertext[len(ciphertext)//2:] y = f(R, 0) x = "".join(chr(ord(f(R, n)[i]) ^ ord(L[i])) for i in range(len(L))) L, R = y, x y = f(R, 0) x = "".join(chr(ord(f(R, m)[i]) ^ ord(L[i])) for i in range(len(L))) flag = x + y f = open("plain.txt", "w") f.write(flag) f.close() ```

The main takeaway is that the operations in lines 13 to 19 are simply in the reverse order than the original encryption code.

The flag - found in plain.txt - is: KCTF{feistel_cipher_ftw}.


RSA-One (100 points)

The challenge provided private.pem, a private key, which looked like this (although some of it is omitted for brevity):

```txt -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAyiytHt1AKzYLwZPm1dd9uT7LgsqVj0eSLpheNd0H4xyiZCYG ZtRYnNtGNnq7A/ubyFalExm61QNewfy71h6xhM/❌IEIoNT0VfMOIzcq0Jmoh+v6k 6/x/3GRkk/vLVolsLbkOKd4aorPMwEsZX4vMd+Sga5Mz0tx5xLFZsbl0r1vvtBl7 ... xqG9YAHVmm4iJzcHeMdzLwmzR6D/x6+k2cFWwox6PxvA7ikJQEYr -----END RSA PRIVATE KEY----- ```

It also provided flag.enc, the encrypted flag.

My approach to this challenge was to bruteforce all the possible certs that could be generated:

```py from codecs import open as copen import string f = copen('private.pem', mode='r', encoding='utf-8') d = f.readlines() f.close() j = 0 for i in list(string.ascii_letters) + list(string.digits) + ['+']: d[2] = list(d[2]) d[2][39] = i d[2] = "".join(d[2]) f = copen(f'private-{i}.pem', mode='w', encoding='utf-8') f.writelines(d) f.close() ```

We then attempt to produce a decoded file:

```sh find -type f -wholename "./private-*.pem" -exec sh -c 'echo $0; SUBSTR=$(echo $0 | cut -d'/' -f 2); openssl rsautl -decrypt -inkey $0 -in ../flag.enc -out "./decoded-${SUBSTR}" 2>/dev/null' {} \; ```

This command lists all the files named private-[letter].pem, and executes a command to retrieve the new symbol from the file name, then using openssl's rsautl tool to decrypt the file and output it to another. The main idea is that the file that successfully gets decrypted will be of a different size.

We can then use find ./ -size 0 -print -delete to delete all empty files leaving decoded-private-a.pem which contains the flag: KCTF{M4Y_TH3_8RUT3F0rc3_B3_W1TH_Y0U}