Two time pad.
Information
- category : crypto
- points : 100
Description
hackerman is so dank that he decided to play around with OTPs. he did the following: message1 ^ key = cipher1 message2 ^ key = cipher2
He gives you cipher1 and cipher2 and challenges you to find the concatenation of messages 1 and 2. Are you dank enough to find this? Oh and also, ‘meme’ is so popular that hackerman used the word in both his messages. cipher1 is : ‘\x05F\x17\x12\x14\x18\x01\x0c\x0b4’, cipher2 is : ‘>\x1f\x00\x14\n\x08\x07Q\n\x0e’ Both without quotes
Writeup
It’s known that the One Time Pad conserves the perfect secrecy only if the key is used just once. What if (as the challenger) we use the key two times?
c1 = m1 xor key
c2 = m2 xor key
If we xor c1
and c2
we obtain: c1 xor c2 = m1 xor key xor m2 xor key = m1 xor m2
.
So xoring the two ciphertexts can leaks information about the two plaintexts. There’s a famous trick to use in this cases called crib-dragging
where we guess a substring and we xor the guessed substring with m1 xor m2
.
If we know that m1
has a substring " the "
, xoring it with m1 xor m2
we can get a valid part of the plaintext of m2
. Usually we don’t know if the substring guessed is from m1
or m2
, but this doesn’t prevent use to decrypt both of them.
In our case we have two short messages, and this made our decryption algorithm/guessing a lot easier.
Let’s write an exploit for testing the various cases interactively.
exploit.py
:
#!/usr/bin/env python3
def xor_string(s1, s2):
if(len(s1) == 1 and len(s1) == 1):
return bytes([ord(s1) ^ ord(s2)])
else:
return bytes(x^y for x, y in zip(s1, s2))
def main():
c1 = b'\x05F\x17\x12\x14\x18\x01\x0c\x0b4'
c2 = b'>\x1f\x00\x14\n\x08\x07Q\n\x0e'
while True:
crib = input("insert crib : ")
c = xor_string(c1, c2)
for i in range(0, len(c)):
res = xor_string(crib.encode(), c[i:len(crib.encode()) + i])
try:
print("result : " + str(res.decode()))
except UnicodeDecodeError:
continue
if __name__ == '__main__':
main()
We are very advantaged because we know the start of m1
and the end of m2
. When we xor the start of m1
with m1 xor m2
we obtain the start of m2
, viceversa when we xor the end of m2
with m1 xor m2
we obtain the end of m1
. We can decrypt the flag in just two step because the len(start) + len(end) = 10 bytes = len(m1) = len(m2)
.
Launching the exploit:
$ python exploit.py
insert crib : d4rk{ | start m1
result : _meme | start m2
result : =#tuk
result : s2l{}
result : b*bm&
result : z$t6z
result : t2/jA
result : bisQ
result : 95H
result : e
result : ^
insert crib : }c0de | end m2
result : F:'b{
result : $t6zu
result : je.tc
result : {} b8
result : cs69d
result : meme_ | end m1
result : {>1^
result : b
Flag
d4rk{meme__meme}c0de