skip to content

Encoding Challenges

r98inver 3 min read

The first two crypto challenges of CTF101 were about encoding.

sv-encoder

We are given a Python script encoding the flag:

 1import base64 as b64
 2
 3flag = b'sv{FAKE_FLAG}'
 4flag = b64.b16encode(flag)
 5for i in range(20):
 6	flag = b64.b64encode(flag)
 7for j in range(5):
 8	flag = b64.b16encode(flag)
 9flag = flag.decode()
10with open('out.txt', 'w') as fh:
11	fh.write(flag)

As we can see, the flag is encoded with base16, then base64 20 times, then again 5 times base16. We are also given the output in out.txt. The solution consists of reversing the process, step by step:

 1import base64 as b64
 2
 3with open('out.txt') as fh:
 4	flag = fh.read().strip()
 5flag = flag.encode()
 6for j in range(5):
 7	flag = b64.b16decode(flag)
 8for i in range(20):
 9	flag = b64.b64decode(flag)
10flag = b64.b16decode(flag)
11print(f'{flag = }')
12# flag = b'sv{enc0d1ng_1s_n0t_3ncrypt10n}'

sv-vs-encoder

The second challenge was a bit more involved. This time we have a custom encoding function, magic_shuffle:

 1def magic_shuffle(poor_string):
 2	assert type(poor_string) == bytes, 'Magicians only use bytestrings'
 3
 4	magic_string = b''
 5
 6	for c in poor_string:
 7		
 8		mask = 0b11
 9		b0 = c & mask
10		b1 = (c >> 2) & mask
11		b2 = (c >> 4) & mask
12		b3 = (c >> 6) & mask
13
14		magic_char = bytes([bi + 32 for bi in [b1, b3, b0, b2] ])
15		# print(f'{magic_char = }')
16
17		magic_string += magic_char
18
19	return magic_string

The flag is shuffled using this function and then base16 encoded.

1flag = b'sv{FAKE_FLAG}'
2
3flag = magic_shuffle(flag)
4flag = b64.b16encode(flag)

Let’s look closer at magic_shuffle. For each character c in our string we do three things:

  • first we derive values b0 to b3 by using a mask = 0b11 and a binary shift: this means that b0 are the last two significant bits of c, b1 the next two and so on; for instance, a in binary is 0b1100001, and hence we would get b0 = 0b01, b1 = 0b00, b2=0b10 and b3 = 0b01;
  • then we add 32 to each one of the bi;
  • finally, they are shuffled, and a bytestring made by [b1, b3, b0, b2] is returned; for instance, if we try to encode b'a' we get b' !!"'.

The key observation here is that for each single byte we encode, we get four bytes, and that these four bytes entirely define the starting byte. All we have to do is once again go backwards: for each tuple of four bytes we take them, we unshuffle them, we subtract 32 from each one of them and then put them one next to each other to rebuild the original byte.

 1def magic_unshuffle(shuffled_string):
 2	assert type(shuffled_string) == bytes
 3
 4	assert len(shuffled_string) % 4 == 0 # Each char is encoded in 4 chars
 5
 6	out = []
 7
 8	while shuffled_string != b'':
 9		# Take the first 4 chars
10		next_chars, shuffled_string = shuffled_string[:4], shuffled_string[4:]
11
12		# Unshuffle
13		b1 = next_chars[0] - 32
14		b3 = next_chars[1] - 32
15		b0 = next_chars[2] - 32
16		b2 = next_chars[3] - 32
17
18		# Reconstruct
19		c = b0 + (b1 << 2) + (b2 << 4) + (b3 << 6)
20		
21		out.append(c)
22
23	return bytes(out)

Before applying this, we have to base16 decode our input, and we are done:

1flag = b'20212323212122232221232320212323202020232321232122212123202020232121212323212321212020232021222320202323232123212120202323212321232121222120202321212322202021232021232220202123212020232321222223212321212120232020202320202023202021222020212223212123'
2flag = b64.b16decode(flag)
3flag = magic_unshuffle(flag)
4# flag = b'sv{s0_y0u_4r3_4_m4g1c14n_t00!!}'

about the author

r98inver

Core member of Stellar Vector. Specializes in Cryptography and Mathematical challenges.

more like this

crypto

RSA Challenges

Comprehensive walkthrough of three RSA challenges from CTF101, covering basic decryption, factoring using online tools, and exploiting totient multiples from leaked private exponents.

by r98inver 5 min
crypto

Substitution Ciphers

Classic cryptography overview featuring Caesar and Vigenere Ciphers. Demonstrates single-byte XOR bruteforcing and frequency analysis for multi-key XOR decryption.

by r98inver 7 min