← Back to Writeups
HTBN/AReversing

Mirror Mirror

XESXOR8/23/20264 min read
#reversing#htb#n/a

Mirror Mirror

Platform: Broncoctf2026 | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-07-11 | Status: Solved Techniques: checker_reimplementation, self_referential_hash, source_code_analysis, xor_keystream_reconstruction

Summary

Task: a self-referential Python flag checker reconstructs the flag at runtime by XORing a hardcoded byte blob with a SHA-256 keystream computed over a 300-char slice of its own source. Solution: ignore the anti-debug guards and re-implement the pure XOR math externally, feeding the same source file so the SHA-256 slice matches.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: broncoctf2026 | ID: 20260711_broncoctf2026_mirror_mirror
  • Tags: sha256, python, xor, anti_debug, flag_checker, source_code, self_referential
  • Indicators: reads file and hashes a slice of its own source, XOR of hardcoded blob with SHA-256 keystream, anti-debug guards: sys.gettrace, frame name check, name check
  • Source: 20260711_broncoctf2026_mirror_mirror.md

Foothold

Vulnerability / Misconfiguration

  1. Checker_reimplementation
  2. Self_referential_hash
  3. Source_code_analysis
  4. Xor_keystream_reconstruction
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • checker_reimplementation
  • self_referential_hash
  • source_code_analysis
  • xor_keystream_reconstruction
  • Tags: sha256, python, xor, anti_debug, flag_checker, source_code, self_referential

Original Writeup

<details><summary>Click to expand original content</summary>

Description

"I've been told that this magic mirror will only clear when I give it the flag. Can you help me figure out what I should say?"

A single Python file mirror.py is provided — a self-referential flag checker themed around Snow White's magic mirror. The flag is never stored directly; it is reconstructed at runtime from a hardcoded byte array and a keystream derived from the script's own source.

Recon

The full challenge source:

import sys
import hashlib

def verify(attempt):
    try:
        with open(__file__, 'r') as f:
            src = f.read()
            pivot = src.index("MIRROR_SURFACE_DO_NOT_SCRATCH")
            specular_map = hashlib.sha256(src[pivot:pivot+300].encode()).digest()
    except (FileNotFoundError, ValueError):
        return "The mirror has been shattered."

    if sys.gettrace() is not None:
        return "Nice try, but the glass turns opaque. No observers allowed!"
    if sys._getframe().f_code.co_name != 'verify' or __name__ != "__main__":
        return "You are looking at the mirror from a distorted angle."

    blob = [17, 241, 10, 247, 215, 233, 146, 221, 156, 40, 37, 198, 153, 173, 10, 103, 20, 56, 232, 116, 208, 121, 53, 12, 122, 86, 127, 164, 109, 62, 88, 200, 127, 234, 5]
    try:
        looking_glass = "MirrorMirror"
        flag = ""
        for i, b in enumerate(blob):
            reflection_byte = specular_map[i % len(specular_map)] ^ ord(looking_glass[i % len(looking_glass)])
            flag += chr(b ^ reflection_byte)

        if attempt == flag:
            return f"The reflection clears!"
    except Exception:
        pass

    return "All you see is a distorted blur. (Wrong Password)"

if __name__ == "__main__":
    inp = input("Enter the password to gaze into the mirror: ")
    print(verify(inp))

Analysis

The flag is derived, not stored. For each index i:

flag[i] = blob[i] ^ specular_map[i % 32] ^ ord("MirrorMirror"[i % 12])

where specular_map = SHA-256( src[pivot : pivot+300] ) and pivot is the offset of the literal string MIRROR_SURFACE_DO_NOT_SCRATCH inside the source.

Key observations:

  1. Self-referential keystream. The marker string MIRROR_SURFACE_DO_NOT_SCRATCH appears in the source only inside the src.index(...) call, so pivot and the 300-char slice are fully deterministic. To reproduce the SHA-256 you must use the exact same source text (byte-for-byte), so keep mirror.py unmodified when extracting.

  2. Anti-analysis guards are red herrings. Three checks try to block dynamic analysis:

  • sys.gettrace() is not None — refuses to run under a debugger/tracer.
  • sys._getframe().f_code.co_name != 'verify' — must be called as verify.
  • __name__ != "__main__" — refuses to run when imported.

None of these matter for a static re-implementation: we do not run the checker at all. We re-derive the pure XOR math ourselves and only feed it the same source file for the hash.

  1. Theme/joke. The recovered flag bronco{REDACTED} ("who is the fairest reverser") is the classic Snow White magic-mirror line.

Solution

Re-implement the exact computation outside the checker, pointing it at the unmodified mirror.py so the SHA-256 slice matches:

python3 -c 'import hashlib
s=open("mirror.py").read()
p=s.index("MIRROR_SURFACE_DO_NOT_SCRATCH")
m=hashlib.sha256(s[p:p+300].encode()).digest()
b=[17,241,10,247,215,233,146,221,156,40,37,198,153,173,10,103,20,56,232,116,208,121,53,12,122,86,127,164,109,62,88,200,127,234,5]
k="MirrorMirror"
print("".join(chr(x^m[i%len(m)]^ord(k[i%len(k)])) for i,x in enumerate(b)))'

Output:

bronco{REDACTED}

Verification against the unmodified checker (so all guards pass naturally):

printf '%s\n' 'bronco{REDACTED}' | python3 mirror.py
# Enter the password to gaze into the mirror: The reflection clears!
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR