問題
Clam decided to return to classic cryptography and revisit the XOR cipher! Here's some hex encoded ciphertext:
ae27eb3a148c3cf031079921ea3315cd27eb7d02882bf724169921eb3a469920e07d0b883bf63c018869a5090e8868e331078a68ec2e468c2bf13b1d9a20ea0208882de12e398c2df60211852deb021f823dda35079b2dda25099f35ab7d218227e17d0a982bee7d098368f13503cd27f135039f68e62f1f9d3cea7c
The key is 5 bytes long and the flag is somewhere in the message.
解法
問題文によると5byteずつXORされているので5byteおきにsingle byte xorを試して候補を求める
コード
def disas(m:bytes)->[int]:
return list(*[iter(m)])
def single_xor(hex_str):
d =bytes.fromhex(hex_str)
candidates = []
for i in range(0x100):
b = ""
score = 0
for j in disas(d):
if 0x20 <= i^j <= 0x7e:
if 0x21<=i^j<=0x2f or 0x3a<=i^j<=0x40 or 0x3a<=i^j<=0x40 or 0x7b<=i^j<=0x7e:
score += 0.2
else:
score += 1
b += chr(i^j)
candidates.append((b,score,i))
return sorted(candidates,reverse=True ,key=lambda x:x[1])[0]
c = "ae27eb3a148c3cf031079921ea3315cd27eb7d02882bf724169921eb3a469920e07d0b883bf63c018869a5090e8868e331078a68ec2e468c2bf13b1d9a20ea0208882de12e398c2df60211852deb021f823dda35079b2dda25099f35ab7d218227e17d0a982bee7d098368f13503cd27f135039f68e62f1f9d3cea7c"
c_pad = c + "00"* (5-(len(c)//2)%5)
print(c_pad)
l = ["".join([x+y for x,y in zip(c_pad[i::10],c_pad[i+1::10])]) for i in range(0,10,2)]
for i, s in enumerate(l):
print(i,single_xor(s)[0],hex(single_xor(s)[2]))
0 Cat etteeegaweahovroun rp 0xed
1 otiocihs! cheeeue}oc o t 0x48
2 nuonrnes fitodsn__.dkttco 0x85
3 gln yg aTlsf_s__hx hhr! 0x5d
4 rasdp mgha {n_wyaoGloeeyf 0x66
single_xorでは、0x00~0xffまでのXORをスコアリング(英数スペース1点、その他記号0.2点)しながら実行し、一番スコアの高い文字列とそのスコア,XORした値を返します。
outputが縦読みできそうなので成功です。
うまくいかない時はsingle_xorで出力する候補数を増やしたり、状況によってスコアリング方法を変えたりもします。
key="ed48855d66"*25
ans = int(c_pad,16)^int(key,16)
print(bytes.fromhex(hex(ans)[2:]))
b'Congratulations on decrypting the message! The flag is actf{who_needs_aes_when_you_have_xor}. Good luck on the other crypto!f'