Powershelly picoCTF 2021 Solution

Published: April 2, 2026

Description

We give you the PowerShell transformation script and the output file. Can you reverse it to find the input?

Download the PowerShell script and the output file.

bash
wget <url>/script.ps1
bash
wget <url>/output.txt
  1. Step 1Read the script to understand the transformation
    Open script.ps1. It reads an input file as UTF-8, verifies each block of 6 characters consists of only '0' and '1', generates a list of pseudo-random seeds (one per block), then for each block calls a scramble function and XORs the result cumulatively, writing each cumulative XOR to output.txt. The output.txt has 264 lines.
    bash
    cat script.ps1
    Learn more

    The transformation has three stages:

    1. The input is 264 blocks of 6 binary digits (ones and zeros).
    2. Each block is scrambled using a seeded random number generator (the seed sequence is deterministic given the block count).
    3. Results are XORed cumulatively and written to output.txt.

    To reverse it, undo the cumulative XOR first (XOR adjacent lines), then unscramble each block, then interpret each 6-bit block as a binary digit.

  2. Step 2Reverse the cumulative XOR
    The script accumulates XOR results across blocks. The first line of output is the XOR of block 0 alone. The second line is that XOR block 1. To undo, XOR each line with the previous line to isolate individual block results. Write a PowerShell (or Python) script that reproduces the same seed sequence and unscramble function.
    bash
    # Reproduce the seed generation (copy from script.ps1):
    # RandomGen generates the same seed list given blocks.count = 264
    # Run the unscramble logic in PowerShell:
    pwsh -File reverse.ps1
    Learn more

    The seed sequence is deterministic: copy the RandomGen function directly from script.ps1 with blocks.count = 264. The unscramble function is the inverse of scramble: where scramble replaces one binary character with a longer pattern, unscramble maps those patterns back to 0 or 1.

  3. Step 3Interpret the unscrambled blocks as binary and convert to ASCII
    After reversing, each line has two 6-character blocks. The block with more '1' bits encodes a 1; the block with fewer '1' bits encodes a 0. Collect all bits and decode as 8-bit binary to ASCII. Use Python to do this final step.
    python
    python3 << 'EOF'
    # Read the unscrambled blocks output from the PowerShell reverse step
    with open("unscrambled.txt") as f:
        lines = f.read().splitlines()
    
    bits = ""
    for line in lines:
        parts = line.split()
        # The block with more 1s encodes bit 1
        g0, g1 = parts[0], parts[1]
        if g0.count("1") > g1.count("1"):
            bits += "1"
        else:
            bits += "0"
    
    # Decode 8 bits at a time to ASCII
    flag = ""
    for i in range(0, len(bits), 8):
        byte = bits[i:i+8]
        if len(byte) == 8:
            flag += chr(int(byte, 2))
    print(flag)
    EOF
    Learn more

    The encoding uses two variants of each binary digit: a pattern with more ones represents 1, and a pattern with fewer ones represents 0. This is a simple majority-vote encoding. After collecting all bits, reading them as 8-bit binary integers and converting to ASCII reveals the flag (printed multiple times in the output because of the cumulative structure).

Flag

picoCTF{...}

Reverse the cumulative XOR, unscramble each block using the same seed sequence, decode the bit encoding (more 1s = bit 1), then convert 8-bit groups to ASCII.

Want more picoCTF 2021 writeups?

Useful tools for Reverse Engineering

Related reading

What to try next