Martin Rozariyo
Back to writeups

L3m0nCTF2025-Writeups

Challenge Overview: Rotating Shadows

Cryptography/RotatingShadows/Readme.md

Challenge Overview: Rotating Shadows

Category: Cryptography
Event: L3m0nCTF 2025
Role: Challenge Author

๐Ÿ› ๏ธ Author Note
This challenge was authored by me for L3m0nCTF 2025.
The following explanation describes the intended analysis path used to solve the challenge.

image

Intended Analysis Path

The challenge was designed to test a player's ability to:

  • distinguish contextual hints from ciphertext
  • extract structured identifiers rather than decode text
  • recognize external references used as indirect disclosure mechanisms
  • reconstruct a custom mechanical cipher from descriptive notes

Brute-force or blind decoding approaches were intentionally ineffective.

Analysis Phase 1 โ€” Identifying Non-Cipher Data

The challenge text includes a sentence that stands out:

โ€œNot Less Visible, but always rotating, gears under.โ€

This sentence is not part of the ciphertext file and does not resemble encrypted data. Given the hints and wording (โ€œetchedโ€, โ€œhandwrittenโ€), it is likely intentional and meaningful.

Analysis Phase 2 โ€” Structural Extraction

Instead of interpreting the sentence semantically, we examine its structure.

Taking the initial letters of each word:

Not        โ†’ N  
Less       โ†’ L  
Visible,   โ†’ V  
but        โ†’ b  
always     โ†’ a  
rotating,  โ†’ r  
gears      โ†’ g  
under.     โ†’ u  

This yields:

NLVbargu

The mixed casing suggests it should be used as-is, not decoded further.

Analysis Phase 3 โ€” Interpreting the Extracted Identifier

This Pastebin was intentionally referenced indirectly to test whether solvers would treat the sentence as an identifier rather than encoded data.

The extracted string NLVbargu does not resemble ciphertext, a key, or encoded data.

In CTFs, such identifiers are often used as external references*. Trying it as a path on common public paste services leads to:

https://pastebin.com/NLVbargu

This Pastebin contains the missing description of the cipher mechanism, including:

  • Two alphabet wheels
  • Their initial configurations
  • How they change after each character

Analysis Phase 4 โ€” Reconstructing the Cipher Mechanism

From the Pastebin notes, the cipher operates mechanically as follows:

image

Analysis Phase 5 โ€” Applying the Reconstructed Cipher

Using the algorithm from the Pastebin and the ciphertext provided in the text file:

PFRLGJEUCZIVFCNGXOMTRKYH

We simulate the wheel behavior in reverse:

  • For each ciphertext character:
    • Find its index in Wheel B
    • Take the character at the same index in Wheel A as plaintext
    • Apply the same rotations and wheel mutations

Reference Implementation

# decrypt_custom.py
# Decrypts the custom rotating-wheel cipher

CIPHERTEXT = "PFRLGJEUCZIVFCNGXOMTRKYH"

LEFT_START  = "GZQFHKDOVYINRUXTCALSPBEMWJ"
RIGHT_START = "YPMRDFZKQHATVJCNWOGIBLUESX"

def rotate(lst, n):
    return lst[n:] + lst[:n]

L = list(LEFT_START)
R = list(RIGHT_START)

plaintext = ""

for ch in CIPHERTEXT:
    idx = R.index(ch)
    pt = L[idx]
    plaintext += pt

    # rotate BOTH wheels by same index
    L = rotate(L, idx)
    R = rotate(R, idx)

    # wheel mutations
    L = L[:2] + L[3:] + [L[2]]
    R = R[:1] + R[2:] + [R[1]]

print("Recovered message:")
print(plaintext)

Running this process over the full ciphertext yields:

ZKQBLUEMTHPRDXCSWAGNQHFV

Final Output

The challenge specifies the submission format:

L3m0nCTF{...}

Placing the recovered message inside the wrapper:

Flag

L3m0nCTF{ZKQBLUEMTHPRDXCSWAGNQHFV}