Tags: stream-cipher regex crypto xor
Rating:
This is what we're given:
```
import secrets
import hashlib
import base64
import re
pattern = re.compile("[a-zA-Z0-9!-/:-?\[-`|~]+")
flag_content = b"@@REDUCTED@@"
assert pattern.fullmatch(flag_content.decode())
flag_hash = hashlib.md5(flag_content).digest()
flag = b"TSGCTF{"+flag_content+b"@"+base64.b64encode(flag_hash)+b"}"
key_stream = list(secrets.token_bytes(16))
encrypted_flags = [flag[i]^key_stream[i%16] for i in range(len(flag))]
print("cipher =",encrypted_flags)
print("flag_length =",len(flag))
```
And this is output.txt:
```
cipher = [163, 227, 86, 67, 200, 14, 176, 188, 101, 214, 117, 82, 99, 71, 199, 117, 139, 130, 78, 43, 224, 101, 183, 219, 82, 213, 70, 95, 101, 118, 133, 46, 146, 239, 98, 97, 250, 123, 183, 218, 82, 218, 1, 97, 62, 29, 145, 105, 168, 136, 116, 95, 253, 59, 148, 132, 98, 207, 118, 66, 52, 118, 197, 98, 168, 201, 126, 117, 195, 61, 184, 141, 82, 210, 86, 98, 47, 118, 144, 58, 221, 192, 99, 48, 224, 98, 185, 129, 108, 152, 25, 97, 96, 85, 173, 58, 148, 194, 104, 124, 182, 99, 162, 216, 99, 157, 117, 106, 59, 64, 213, 25, 148, 217, 109, 42, 224, 101, 183, 219, 127, 236, 67, 26, 12, 29, 174, 118, 153, 213, 78, 43, 245, 52, 151, 199, 113, 214, 117, 66, 121, 72, 141, 111, 168, 194, 112, 43, 244, 123, 183, 218, 82, 199, 86, 19, 47, 29, 141, 26, 139, 239, 112, 95, 239, 99, 185, 141, 57, 222, 117, 22, 58, 89, 153, 117, 133, 156, 78, 98, 233, 60, 148, 129, 121, 236, 67, 26, 12, 64, 159, 53, 196, 152, 100, 124, 174, 45, 148, 138, 104, 155, 75, 75, 32, 76, 174, 47, 131, 239, 100, 115, 175, 59, 148, 156, 101, 214, 117, 26, 103, 85, 173, 105, 139, 213, 78, 114, 168, 38, 175, 135, 96, 236, 68, 75, 62, 17, 194, 52, 211, 239, 99, 101, 224, 98, 248, 220, 38, 128, 86, 23, 63, 80, 223, 25, 146, 222, 123, 111, 229, 23, 163, 137, 101, 210, 66, 95, 12, 19, 220, 111, 218, 138, 56, 45, 166, 97, 139, 188, 90, 195, 28, 77, 2, 113, 152, 34, 165, 252, 88, 67, 250, 44, 163, 167, 64, 234, 1, 119, 18, 20, 204, 59]
flag_length = 304
```
So, this problem seems to just be doing XOR encryption with a key of length 16. Maybe we can figure out this key byte by byte?
First, we can use the flag prefix to get the 7 bytes of the key. After that, we can essentially brute force character by character with the commented-out script, which tests all 256 possible bytes for that position and checks if each byte results in a flag in which the flag_content section entirely matches the RegEx pattern.
At each step of this script, we can essentially just use logic by looking at all the different words and figuring out which is the most likely possiblity.
Once we figure out which byte is the correct byte in the key, we can simply append that to the key_stream. Note that in my program, I forgot that I could just print out the hex value of the byte, so I just appended the XOR of the character I figured out works in a certain position with the cipher's value at that position.
*Also note that this logical deduction should probably include the fact that this flag includes LEET speak, something I did not realize until after decrypting the entire thing :P*
```
import re
pattern = re.compile("[a-zA-Z0-9!-/:-?\[-`|~]+")
cipher = [163, 227, 86, 67, 200, 14, 176, 188, 101, 214, 117, 82, 99, 71, 199, 117, 139, 130, 78, 43, 224, 101, 183, 219, 82, 213, 70, 95, 101, 118, 133, 46, 146, 239, 98, 97, 250, 123, 183, 218, 82, 218, 1, 97, 62, 29, 145, 105, 168, 136, 116, 95, 253, 59, 148, 132, 98, 207, 118, 66, 52, 118, 197, 98, 168, 201, 126, 117, 195, 61, 184, 141, 82, 210, 86, 98, 47, 118, 144, 58, 221, 192, 99, 48, 224, 98, 185, 129, 108, 152, 25, 97, 96, 85, 173, 58, 148, 194, 104, 124, 182, 99, 162, 216, 99, 157, 117, 106, 59, 64, 213, 25, 148, 217, 109, 42, 224, 101, 183, 219, 127, 236, 67, 26, 12, 29, 174, 118, 153, 213, 78, 43, 245, 52, 151, 199, 113, 214, 117, 66, 121, 72, 141, 111, 168, 194, 112, 43, 244, 123, 183, 218, 82, 199, 86, 19, 47, 29, 141, 26, 139, 239, 112, 95, 239, 99, 185, 141, 57, 222, 117, 22, 58, 89, 153, 117, 133, 156, 78, 98, 233, 60, 148, 129, 121, 236, 67, 26, 12, 64, 159, 53, 196, 152, 100, 124, 174, 45, 148, 138, 104, 155, 75, 75, 32, 76, 174, 47, 131, 239, 100, 115, 175, 59, 148, 156, 101, 214, 117, 26, 103, 85, 173, 105, 139, 213, 78, 114, 168, 38, 175, 135, 96, 236, 68, 75, 62, 17, 194, 52, 211, 239, 99, 101, 224, 98, 248, 220, 38, 128, 86, 23, 63, 80, 223, 25, 146, 222, 123, 111, 229, 23, 163, 137, 101, 210, 66, 95, 12, 19, 220, 111, 218, 138, 56, 45, 166, 97, 139, 188, 90, 195, 28, 77, 2, 113, 152, 34, 165, 252, 88, 67, 250, 44, 163, 167, 64, 234, 1, 119, 18, 20, 204, 59]
flag_length = 304
key_stream = []
m = 'TSGCTF{'
for i in range(len(m)):
key_stream.append(ord(m[i]) ^ cipher[i])
key_stream.append(ord('o') ^ cipher[231])
key_stream.append(ord('m') ^ cipher[232])
key_stream.append(ord('e') ^ cipher[217])
key_stream.append(ord('_') ^ cipher[10])
key_stream.append(ord('u') ^ cipher[235])
key_stream.append(ord('m') ^ cipher[236])
key_stream.append(ord('8') ^ cipher[237])
key_stream.append(ord('3') ^ cipher[238])
key_stream.append(ord('r') ^ cipher[239])
key_stream.append(ord('$') ^ cipher[240])
'''
for c in range(256):
key_stream.append(c)
output = ''
for i in range(len(cipher)):
if i % 16 < len(key_stream):
a = chr(cipher[i] ^ key_stream[i % 16])
#if i > 7 and i < 271:
#if not pattern.match(a):
#print(a)
output += a
else:
output += '~'
#print(output)
#print('\nmatch' if pattern.fullmatch(output[7:278]) else '\nno match')
if pattern.fullmatch(output[7:278]):
print(output,end='\n\n')
del key_stream[-1]
'''
output = ''
for i in range(len(cipher)):
if i % 16 < len(key_stream):
a = chr(cipher[i] ^ key_stream[i % 16])
#if i > 7 and i < 271:
#if not pattern.match(a):
#print(a)
output += a
else:
output += '~'
print(output)
print('\nmatch' if pattern.fullmatch(output[7:278]) else '\nno match')
```
TSGCTF{The_l0n63|2_+|-|3_fla6_the_saf3|2_i+_m4`/_8e_as_lo|\|g_4$_you_use_a|\|_a|*pr0|*ria+3_3|\|cry|*+i0n._Thi$_ci|*|-|3r_i$_4_0ne_+i|\/|e_|*a|)_ra+h3|2_t|-|4|\|_a_s+re4m_(iph3r,_but_it_i$_ins3(u|2e_be(ause_it_us3s_the_$4|\/|e_r4ndom_num83r$_re|*34+3|)ly._enjoy_hahaha_:-)-:)-:)@TWp6sQXidRLICfdhOMY+IA==}