icon

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.

  1. Convert the decimal 21 of the target to the binary (10101).
  2. Convert the decimal 2 of the key to the binary (00010).
  3. 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.

  1. Convert the character ‘a’ to the Unicode 97. It’s 1100001 in binary.
  2. Convert the character ‘b’ to the Unicode 98. It’s 1100010 in binary.
  3. 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.

  1. Convert the ciphertext to the binary.
  2. Convert the XOR key to the binary.
  3. Loop each byte and XOR each one.
  4. Convert the result bytes to Hex.
  • Using strxor of PyCryptodome

    We can also use strxor method of pycryptodome 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 ''