mirror of https://github.com/dirtbags/moth.git
173 lines
4.6 KiB
Plaintext
173 lines
4.6 KiB
Plaintext
|
XOR Masks
|
|||
|
=========
|
|||
|
|
|||
|
Exclusive or (XOR, ⊕) is the binary operation “one or the other but not
|
|||
|
both”. The following table demonstrates how a binary XOR works:
|
|||
|
|
|||
|
p q p⊕q
|
|||
|
---------
|
|||
|
0 0 0
|
|||
|
0 1 1
|
|||
|
1 0 1
|
|||
|
1 1 0
|
|||
|
|
|||
|
To XOR two multi-bit numbers, one must simply XOR each bit individually:
|
|||
|
|
|||
|
11110000
|
|||
|
⊕ 10101010
|
|||
|
==========
|
|||
|
01011010
|
|||
|
|
|||
|
|
|||
|
Reversing XOR
|
|||
|
-------------
|
|||
|
|
|||
|
XOR has the peculiar property that `(p⊕q)⊕q = p` for any value of `p` or `q`:
|
|||
|
|
|||
|
p q p⊕q (p⊕q)⊕q
|
|||
|
------------------
|
|||
|
0 0 0 0
|
|||
|
0 1 1 0
|
|||
|
1 0 1 1
|
|||
|
1 1 0 1
|
|||
|
|
|||
|
This also works for multi-bit numbers:
|
|||
|
|
|||
|
11110000 (0xF0)
|
|||
|
⊕ 10101010 (0xAA)
|
|||
|
==========
|
|||
|
01011010 (0x9A)
|
|||
|
|
|||
|
01011010 (0x9A)
|
|||
|
⊕ 10101010 (0xAA)
|
|||
|
==========
|
|||
|
11110000 (0xF0)
|
|||
|
|
|||
|
|
|||
|
XOR in encryption
|
|||
|
-----------------
|
|||
|
|
|||
|
XOR is used extensively in many encryption algorithms. One reason it is
|
|||
|
popular is because it is easy to implement in hardware, since there is
|
|||
|
no possibility for overflow or underflow, there are no “carry” bits as
|
|||
|
in addition, and XOR is one of the basic logic gates used in
|
|||
|
electronics. For these reasons, it is also one of the quickest
|
|||
|
operations most CPUs can carry out.
|
|||
|
|
|||
|
One of the most basic ways to use XOR in encryption is to XOR the
|
|||
|
plaintext (the thing to be encrypted) against the key:
|
|||
|
|
|||
|
ATTACK AT DAWN
|
|||
|
⊕ keykeykeykeyke
|
|||
|
================
|
|||
|
*1-*&2K$-K!8<+
|
|||
|
|
|||
|
Because of the reversible nature of XOR, the same key can be applied to
|
|||
|
decrypt the ciphertext:
|
|||
|
|
|||
|
*1-*&2K$-K!8<+
|
|||
|
⊕ keykeykeykeyke
|
|||
|
================
|
|||
|
ATTACK AT DAWN
|
|||
|
|
|||
|
|
|||
|
Doing XOR on strings in Python
|
|||
|
------------------------------
|
|||
|
|
|||
|
The following function in Python 3:
|
|||
|
|
|||
|
def xor(n, b):
|
|||
|
return bytes(c ^ n for c in b)
|
|||
|
|
|||
|
Will take a byte array, and return it XORed with n.
|
|||
|
|
|||
|
Let's try an example:
|
|||
|
|
|||
|
>>> def xor(n, b):
|
|||
|
... return bytes(c ^ n for c in b)
|
|||
|
...
|
|||
|
>>> xor(22, b'hello')
|
|||
|
b'~szzy'
|
|||
|
>>> xor(22, b'~szzy')
|
|||
|
b'hello'
|
|||
|
>>> xor(22, bytes([0, 1, 2, 3, 4]))
|
|||
|
b'\x16\x17\x14\x15\x12'
|
|||
|
>>>
|
|||
|
|
|||
|
We will use this last method of invoking xor in the following sections,
|
|||
|
to work with hex dumps.
|
|||
|
|
|||
|
|
|||
|
Converting hex strings to byte arrays
|
|||
|
-------------------------------------
|
|||
|
|
|||
|
The following Python 3 function will take an ASCII representation of hex
|
|||
|
octets, and convert it to a byte array:
|
|||
|
|
|||
|
>>> def unhex(s):
|
|||
|
... import binascii
|
|||
|
... return binascii.unhexlify(s.replace(' ', '').replace('\n', ''))
|
|||
|
|
|||
|
|
|||
|
Known-plaintext attacks against XOR encryption
|
|||
|
----------------------------------------------
|
|||
|
|
|||
|
We have intercepted a coded message. We suspect the plaintext to
|
|||
|
consist solely of ASCII characters. The hex dump of the message is:
|
|||
|
|
|||
|
|
|||
|
00000000 69 62 65 0a 7d 6f 0a 78 0a 79 7f 61 0a 6c 63 72 ┆ibe◙}o◙x◙y⌂a◙lcr┆
|
|||
|
00000010 0a 67 19 0a 6d 1a 6b 7e 70 ┆◙g↓◙m→k~p┆
|
|||
|
00000019
|
|||
|
|
|||
|
Right away we can see that the character ◙ (`0x0A`) occurs fairly
|
|||
|
frequently. We will first guess that `0x0A` represents the letter “e”,
|
|||
|
which is the most common letter in English. To find the XOR key needed
|
|||
|
to turn turn “e” (`0x65`) into `0x0A`, we can simply XOR the two values:
|
|||
|
|
|||
|
0x65 (e)
|
|||
|
⊕ 0x0A (◙)
|
|||
|
======
|
|||
|
0x6F (o)
|
|||
|
|
|||
|
Let’s try applying the XOR key `0x6F` to the ciphertext. First, we will
|
|||
|
load the hex octets into a Python 3 byte array using our `unhex`
|
|||
|
function from the previous section:
|
|||
|
|
|||
|
>>> a = unhex('69 62 65 0a 7d 6f 0a 78 0a 79 7f 61 0a 6c 63 72') + \
|
|||
|
... unhex('0a 67 19 0a 6d 1a 6b 7e 70')
|
|||
|
>>> a
|
|||
|
b'ibe\n}o\nx\ny\x7fa\nlcr\ng\x19\nm\x1ak~p'
|
|||
|
|
|||
|
Now, we'll xor it with our guess of `0x6F`:
|
|||
|
>>> xor(0x6F, a)
|
|||
|
b'\x06\r\ne\x12\x00e\x17e\x16\x10\x0ee\x03\x0c\x1de\x08ve\x02u\x04\x11\x1f'
|
|||
|
|
|||
|
That doesn't look right. Let's try another guess, maybe ◙ represents a space:
|
|||
|
|
|||
|
0x20 ( )
|
|||
|
⊕ 0x0A (◙)
|
|||
|
======
|
|||
|
0x2A (*)
|
|||
|
|
|||
|
Now we apply this key:
|
|||
|
|
|||
|
>>> xor(0x2A, a)
|
|||
|
b'CHO WE R SUK FIX M3 G0ATZ'
|
|||
|
|
|||
|
This is clearly English text, although possibly some sort of code. In
|
|||
|
any case, we have broken the code with the key `0x2A`.
|
|||
|
|
|||
|
|
|||
|
Question
|
|||
|
========
|
|||
|
|
|||
|
Use the known-plaintext attack technique against this XORed cyphertext:
|
|||
|
|
|||
|
00000000 01 2d 2c 25 30 23 36 37 2e 23 36 2b 2d 2c 31 62 ┆☺-,%0#67.#6+-,1b┆
|
|||
|
00000010 2d 2c 62 3b 2d 37 30 62 20 30 27 23 29 6c 62 62 ┆-,b;-70b␣0'#)lbb┆
|
|||
|
00000020 16 2a 27 62 29 27 3b 62 24 2d 30 62 36 2a 2b 31 ┆▬*'b)';b$-0b6*+1┆
|
|||
|
00000030 62 32 23 25 27 62 2b 31 62 60 20 23 21 2d 2c 60 ┆b2#%'b+1b`␣#!-,`┆
|
|||
|
00000040 6c ┆l┆
|
|||
|
00000041
|