XOR Bitwise Operations
Last modified: 2024-07-04
XOR is commonly used method for cryptography.
Basic
For XORing, we can use ^
operator.
Here is Python script example to XOR.
Also use the XOR key for xoring a target value.
target = 21
xor_key = 2
print(target ^ xor_key)
# 23
The above operation does the following calculation internally.
- Convert the decimal
21
of the target to the binary (10101
). - Convert the decimal
2
of the key to the binary (00010
). - XOR the bits at each position as below.
10101 # binary for 21
# XOR
00010 # binary for 2
# Result
10111 # binary for 23
By the way, each value can be replaced individually as follows.
a ^ b = c
a ^ c = b
b ^ c = a
In CTF, we may be able to use this principle to calculate the xor key.
XOR Characters
We can also XOR each character.
ord('a') ^ ord('b')
# 3
The above operation does the following calculation internally.
- Convert the character ‘a’ to the Unicode
97
. It’s1100001
in binary. - Convert the character ‘b’ to the Unicode
98
. It’s1100010
in binary. - XOR the bits at each position as below.
1100001 # binary for 'a'
# XOR
1100010 # binary for 'b'
# Result
0000011 # binary for 3
XOR Strings
In addition, we can also XOR strings by XORing the bits at each position.
ciphertext = "5d41402abc4b2a76b9719d911017c592"
key = "secret"
# Convert each string to bytes
ciphertext_bytes = bytes.fromhex(ciphertext)
key_bytes = key.encode()
# XOR operation
xored_bytes = bytes(a ^ b for a, b in zip(ciphertext_bytes, key_bytes))
# Convert the result to Hex
xored_hex = xored_bytes.hex()
print("Result:", xored_hex)
The above operation does the following calculation.
- Convert the ciphertext to the binary.
- Convert the XOR key to the binary.
- Loop each byte and XOR each one.
- Convert the result bytes to Hex.
-
Using
strxor
of PyCryptodomeWe can also use
strxor
method ofpycryptodome
module in Python.from Crypto.Util.strxor import strxor print(strxor(b"hello", b"world")) # b'\x1f\n\x1e\x00\x0b'
XOR with Pwntools
We can easily XOR using the xor
module of pwntools
.
First off, install pwntools
if you don't have.
pip install pwntools
To decrypt the encrypted text with XOR, write Python script such as below.
from pwn import xor
ciphertext = "5d41402abc4b2a76b9719d911017c592"
key = "secret"
xored = xor(bytes.fromhex(ciphertext), key.encode())
Brute Force XOR Key with 0/Null
If we specify 0 or \x00
to the target value, the result is the key as it is.
0 ^ 1 # result: 1
0 ^ 2 # result: 2
...
0 ^ 999 # result: 999
Using the principle, we may be able to get the XOR key by brute forcing.
xor_key = b'secret'
null_payload = b''
for i in range(10):
null_payload += b'\x00'
result = bytes([a ^ b for a, b in zip(null_payload, xor_key)])
print(result.decode())
The output of the above script will be the following:
s
se
sec
secr
secre
secret
secret
secret
secret
secret
Brute Force XOR Key leveraging Partial Strings
This is a common CTF problem. If we have already a partial string of the flag such as FLAG{
, we can use this string for brute forcing the XOR key. However, to achieve this, we also need to know the number of characters in the XOR key.
The following example is assuming that the XOR key length is 6
.
# All (almost) characters that are used for brute forcing
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&\'()~=~|`{}+*}<>?_@[]:;,./"
def calc_key(ciphertext: str) -> str:
# Convert string to bytes
ciphertext_bytes = ciphertext.encode()
# The final flag will be "FLAG{xxxxxxxxxxx}"
flag_part = "FLAG{"
# Determine the first 5 characters of the key
key_part = ''.join(chr(ciphertext_bytes[i] ^ ord(flag_part[i])) for i in range(0, len(flag_part)))
print(f"key_part: {key_part}")
# Find last key character by brute forcing characters and XORing
for c in chars:
key = key_part + c
# XORing with key
flag = ''.join(chr(b ^ ord(key[i % len(key)])) for i, b in enumerate(ciphertext_bytes))
if flag.startswith("FLAG{") and flag.endswith("}"):
print(f"flag: {flag}")
print(f"key: {key}")
return key
# Not found...
return ''