Challenge
The challenge description only gives us the command nc prob.vulnerable.kr 20034
which is used to connect to the service. Doing so results in the following greeting:
==================================================
This is BiMilCode
I'll give you a chance to type 3 times.
Good Luck
# if you got it how to solve this problem type 0 .
==================================================
You can encode me? : 89 95 79 2e 53 87 62 51
Input :
Service analysis
There is an input where we can enter arbitrary text. We have 3 tries before the service closes the connection. A sample of such a connection looks like this:
==================================================
This is BiMilCode
I'll give you a chance to type 3 times.
Good Luck
# if you got it how to solve this problem type 0 .
==================================================
You can encode me? : 89 95 79 2e 53 87 62 51
Input : 11111111
83 58 56 39 45 83 64 4e
you have 2 chance left.
Input : 22221111
84 59 57 3a 45 83 64 4e
you have 1 chance left.
Input : 33221100
85 5a 57 3a 45 83 63 4d
you have 0 chance left.
Think more about it.
While the challenge is located in the category Misc
, it is a cryptographic challenge. Looking at the output shows us that the output is a list of hexadecimal numbers based on the input. We can, therefore, perform a chosen-plaintext attack on the service to reverse the encoded message.
From this example output alone, we can already derive some assumptions:
- Each character is encoded independently. This can be seen because only the bytes are changed where the input character was changed as well.
- The key does not change between the tries.
- It is a character (block) based substitution cipher, which changes the character on a fixed offset. This can be seen because the first character was incremented by one every time, and the resulting byte incremented by one as well. A stream cipher with bitwise xor would not show this behavior.
- The substitution differs on every input position. This can be seen because the same input character results in different output bytes depending on its position. This suggests a polyalphabetic cipher.
We can reason from those observations, that the challenge likely implements a block-based polyalphabetic substitution cipher, where each block has the size of one character. The subsitution is implemented as a fixed shift of the character, as done in the Caesar cipher. Because the substitution differs for every input character, it is a polyalphabetic substitution like employed in the Vigenère cipher.
Basic idea
- input at least n characters as encoded bytes present
- read the encoded message and calculate offsets, assuming an ASCII encoding of the characters
- apply the position-dependent offset on the encoded bytes to the calculate input
Exploit
Instead of doing it manually, I wrote a simple script to do the chosen-plaintext attack:
#!/usr/bin/env python3
encoded = input(" * Encoded message in hex: ").strip().split(' ')
encoded_bytes = [int(ch, 16) for ch in encoded]
input_str = "1"*len(encoded_bytes) # can be anything
print(f" * Now send '{input_str}' to the server")
output = input(" * Result from server in hex: ").strip().split(' ')
output_bytes = [int(ch, 16) for ch in output]
offsets = [ord(i) - o for i, o in zip(list(input_str), output_bytes)]
print(f" * Calculated offsets: {offsets}")
plaintext = [chr(e+o) for e, o in zip(encoded_bytes, offsets)]
plaintext_str = "".join(plaintext)
print(f" * Plaintext is: {plaintext_str}")
Get the flag
==================================================
This is BiMilCode
I'll give you a chance to type 3 times.
Good Luck
# if you got it how to solve this problem type 0 .
==================================================
You can encode me? : bb 7a 49 87 9b 99 86 6c
Input : 11111111
8d 4c 3e 8f 8d 77 91 3c
you have 2 chance left.
Input : 0
Oh really? come on ! : __<)?S&a
You did it!
KorNewbie{Nace_I_believed_it}