Reversing XOR Encryption with SHA-256: Tap Into Hash Walkthrough

Reversing XOR Encryption with SHA-256: Tap Into Hash Walkthrough

Challenge Info:

  • Platform: picoCTF 2025 - Tap Into Hash
  • Category: Reverse Engineering
  • Difficulty: Easy
  • Techniques: SHA-256 Hashing, XOR Encryption, Key Recovery

Prerequisites

Before you start, make sure you've got:

  • Python basics: You should know how variables and loops work.
  • Command line: Just need to know how to run scripts.
  • Python 3: Installed on your system.

Understanding the Challenge

We get a source code file (block_chain.py) and an encrypted file (enc_flag). The hint tells us blockchains use hashing - that's our clue.

Figure 1: The challenge description providing the source code and encrypted file.


Analyzing the Files

Two files:

  1. block_chain.py: The script that created the encryption.
  2. enc_flag: The encrypted output with the flag we want.

When you open enc_flag, you'll see:

  • A Key (looks like a byte string).
  • An Encrypted Blockchain (also a byte string).

Source Code Analysis

Let's check out block_chain.py to see what's going on.

The main function does a few things:

Key Generation

Creates a random 32-byte hex key.

random_string = generate_random_string(64)
key = bytes.fromhex(random_string)
print("Key:", key)  # <--- They just... gave us the key!

Blockchain Construction

Makes a fake blockchain with some dummy blocks.

Flag Hiding

Takes your input (the flag) and stuffs it right in the middle of the blockchain.

Encryption Process

The script uses a custom function that works on 16-byte chunks.

key_hash = hashlib.sha256(key).digest() # <--- This is important
ciphertext = b''
# Goes through the data 16 bytes at a time
for i in range(0, len(plaintext), block_size):
    block = plaintext[i:i + block_size]
    cipher_block = xor_bytes(block, key_hash) # XOR magic
    ciphertext += cipher_block

Understanding the Cryptography

To crack this, we need to get two things: hashing and XOR.

Why "Tap into Hash"? (SHA-256)

The title is a hint. The script doesn't use the random key directly.

  1. It takes the random key (which we have).
  2. Runs it through SHA-256 to get a hash.
  3. Uses that hash to scramble the data.

How XOR Works

XOR is like a light switch cipher.

  • Flip the switch with the Key on your Message → you get Scrambled Data.
  • Flip the switch again with the same Key on the Scrambled Data → you get your Message back.

Visual Breakdown

Locking it:

[ Your Message ]  ⊕  [ Hash of Key ]  ===>  [ Scrambled Mess ]

Unlocking it (what we'll do):

[ Scrambled Mess ] ⊕  [ Hash of Key ]  ===>  [ Your Message ]

XOR Example

Let me show you with actual bits to make this clear.

Say we have:

  • Message: The letter 'A' (ASCII 65) → Binary: 01000001
  • Key: Random byte (5) → Binary: 00000101

1. Lock it ($P oplus K = C$): Compare each bit. Different = 1, Same = 0.

  01000001 (Message 'A')
⊕ 00000101 (Key 5)
----------
  01000100 (Encrypted)

Result: 01000100 is 68 in decimal, which is 'D'. 'A' just became 'D'.

2. Unlock it ($C oplus K = P$): Now take 'D' and XOR with the same Key (5).

  01000100 (Encrypted 'D')
⊕ 00000101 (Key 5)
----------
  01000001 (Result)

Result: 01000001 which is 'A' again. Got it back!

This is exactly what our script does, but with 32 bytes instead of one.

The Mathematics

Here's what's happening:

  • $P$ = Your original message (the flag)
  • $K$ = The hash we calculate from the key
  • $C$ = The encrypted junk in enc_flag
  • $oplus$ = XOR symbol

Locking: $$ P oplus K = C $$

Unlocking: $$ C oplus K = P $$

Why This Works For Us

Normally, you'd only have the scrambled data ($C$) and no key ($K$). But here, the enc_flag file literally gives us:

  1. The Scrambled Data ($C$): Called "Encrypted Blockchain".
  2. The Random String: Called "Key".

Since we have the random string, we can calculate the SHA-256 hash ($K$). With both $K$ and $C$, we can get $P$.


Attack Strategy

No brute forcing needed. Just:

  1. Grab the Key string from enc_flag.
  2. Hash it: Run SHA-256 on that string.
  3. XOR it back: Take the encrypted bytes and XOR with our hash.

The Solver Script

Here's a Python script that does it all:

import hashlib
import ast
def xor_bytes(a, b):
    # XOR two byte strings together
    return bytes(x ^ y for x, y in zip(a, b))
def solve():
    print("--- Cracking enc_flag ---")
    try:
        # Read the file
        with open("enc_flag", "r") as f:
            content = f.read().strip().split('n')
        # Find the key and encrypted data
        key_line = [line for line in content if "Key:" in line][0]
        cipher_line = [line for line in content if "Encrypted Blockchain:" in line][0]
        # Parse the byte strings
        key_bytes = ast.literal_eval(key_line.split("Key:", 1)[1].strip())
        cipher_bytes = ast.literal_eval(cipher_line.split("Encrypted Blockchain:", 1)[1].strip())
    except Exception as e:
        print(f"Something went wrong: {e}")
        return
    # 1. Make the XOR key (the "Tap into Hash" part)
    # Hash the key with SHA256 like the source code did
    full_hash = hashlib.sha256(key_bytes).digest()
    # Only need first 16 bytes (block size)
    xor_key = full_hash[:16]
    # 2. Decrypt
    decrypted_data = b""
    block_size = 16
    for i in range(0, len(cipher_bytes), block_size):
        chunk = cipher_bytes[i : i + block_size]
        decrypted_block = xor_bytes(chunk, xor_key)
        decrypted_data += decrypted_block
    # 3. Show the result
    plaintext = decrypted_data.decode('utf-8', errors='ignore')
    print(f"n[+] Here's what we got:n{plaintext[:100]}...")
    # Pull out the flag
    import re
    match = re.search(r"picoCTF{.*?}", plaintext)
    if match:
        print(f"n[SUCCESS] FLAG: {match.group(0)}")
if __name__ == "__main__":
    solve()

Flag Capture

Run the script and watch it spit out the decrypted blockchain with the flag sitting right in the middle.

Flag: picoCTF{block_3SRhViRbT1qcX_XUjM0r49cH_qCzmJZzBK_60647fbb}

Figure 2: Successful decryption revealing the flag embedded in the blockchain data.


Key Takeaways

Cryptographic Concepts

  • XOR encryption is symmetric: same operation encrypts and decrypts
  • SHA-256 transforms keys into fixed-size hashes
  • Block ciphers process data in fixed chunks (16 bytes here)
  • Key leakage completely breaks XOR encryption

Attack Methodology

  1. Analyze source code to understand encryption logic
  2. Identify key material in output files
  3. Replicate hashing process (SHA-256)
  4. Reverse XOR operation with recovered key
  5. Extract flag from decrypted plaintext