Crypto CTF — Learning permutation

Golam Rabbany
4 min readOct 15, 2023

Hello Everyone!

I will walk through a simple permutation/transposition CTF challenge in this write-up.

def check_password(pass_str):
length = len(pass_str)
if length != 15:
return False

part1 = "Spr3ue45"
for i in range(0, 15, 2):
if pass_str[i] != part1[i // 2]:
return False

part2 = "5PrcS3u"
for i in range(13, 0, -2):
if pass_str[i] != part2[(length - 2 - i) // 2]:
return False

return True

def main():
password = input("Enter password: ")
if check_password(password):
print(f"Welcome admin!\nFlag: SKR{{{password}}}")
else:
print("Login failed!")

if __name__ == "__main__":
main()

A little about permutation Cipher

Permutation cipher aka Transposition cipher is a cryptographic algorithm that swaps the position of a string (the secret message). It has three types, but we will solve the straight algorithm.

It takes the key (permutation position) and the string (secret message) to encrypt and encrypted string for decrypting.

This image shows how a simple permutation looks. Think of the “b” as the message block (character) and the numbers as the “index number” (position).

Solving the Challenge

We have two functions check_password and main looking at the main function seems to be taking an input and storing it in a password variable. Then it passes the value to the check_password function and if the return value is true it prints the “Welcome” message. And if it returns false it prints “Login failed!”.

def main():
password = input("Enter password: ")
if check_password(password):
print(f"Welcome admin!\nFlag: SKR{{{password}}}")
else:
print("Login failed!")

Now let’s look at the check_password function.

    length = len(pass_str)
if length != 15:
return False

First, it checks the length of the input password. If it’s not 15 the function returns false immediately. So, we know that the password length is 15.

    part1 = "Spr3ue45"
for i in range(0, 15, 2):
# 0 2 4 6 8 10 12 14
if pass_str[i] != part1[i // 2]:
return False

Then there’s the first for loop with the string part1 variable. It’s going to a for loop from 0 -> 15, with a 2 increment. Then it matches the pass_str[i] input password with the position from the for loop part1[i // 2] that takes the string from part1 divides with 2 to cancel the 2 increment. And if it doesn't match return false and exit the function.

Let’s see what I mean by canceling the 2 increments.

>>> for i in range(0,15,2):
... print(i)
...
0
2
4
6
8
10
12
14
>>> for i in range(0,15,2):
... print(i//2)
...
0
1
2
3
4
5
6
7
>>>

Now to the part2 second for loop.

    part2 = "5PrcS3u"
for i in range(13, 0, -2):
# 13 11 9 7 5 3 1
if pass_str[i] != part2[(length - 2 - i) // 2]:
return False

Just like the first for loop it also uses a range from 13 -> 0 with (-2) increment. So this is matching the string from backward.

Print out the for loop i and (15 — 2 -i) // 2 to see the index.

Now let’s see how to solve it. First, we will have to extract the index position that’s matching with our password string. Which is 0 2 4 6 8 10 12 14 13 11 9 7 5 3 1 combined with the two for loops.

Now all we have to do is rearrange the part1 and part2 string with the index number we got. Just like this

# 0 2 4 6 8 10 12 14 13 11 9 7 5 3 1 
# S p r 3 u e 4 5 5 P r c S 3 u

# Sup3rS3cureP455

Now coding this in Python.

def pbox_cipher(code, key):
if len(code) != len(key):
raise ValueError("Code and key lengths must be the same")

cipher_text = [None] * len(code)
print(cipher_text)
for i, new_position in enumerate(key):
cipher_text[new_position] = code[i]
print(cipher_text)

return ''.join(cipher_text)

code = "Spr3ue455PrcS3u"
key = [0, 2, 4, 6, 8, 10, 12, 14, 13, 11, 9, 7, 5, 3, 1]
ciphered_text = pbox_cipher(code, key)
print("Original Code:", code)
print("Ciphered Text:", ciphered_text)

It only requires the cipher text and the key (index position).

The script first checks if the cipher length and the key length are the same or not. Because in the straight permutation cipher, the key and cipher length have to be the same. Then it creates a none value array with the cipher length.

Later in the for loop, we are just replacing the list with characters from cipher text following the key (index number).

More walkthroughs like this will be coming up next. If it was helpful please give a like (clap).

I also write in substack (in-depth, and hands-on), you can get it here https://cyberxcyber.substack.com/.

Thanks!

Twitter @_Golam Rabbany

--

--

Golam Rabbany

Cyber Security Professional | CySA+ | ISC2 CC | Splunk CDA | AWS CCP | AWS SAA | Content Creator