← Back to Writeups
CUSTOMEasyCrypto

Shifting Away

XESXOR9/10/20263 min read
#crypto#custom#easy#position_dependent_caesar#known_plaintext_attack#semantic_clue_analysis

Shifting Away

Platform: broncoctf2026 | Category: Crypto | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-07-11 | Status: Solved Techniques: position_dependent_caesar, known_plaintext_attack, semantic_clue_analysis

Summary

Task: ciphertext encrypted with a position-dependent Caesar where the shift grows per absolute character index. Solution: read the lyric clues (shift per char; underscores/braces skip transformation but still advance the counter), then decrypt with P[i]=(C[i]-base+i) mod 26 + base, confirmed by the bronco{ prefix.

Shifting Away — broncoctf2026

Description

I'm slowly shifting, shifting afar / Char after char, char after char / I'm slowly shifting (shifting afar) // And it feels like I'm fighting / Underscores against the stream / Braces against the stream // (Source Material: Mr. Probz, 2013)

Ciphertext:

bqmkyj{Ldfmam_Nfd_Abxjpb_Thhdqeia_Snqn_Vzey_Bok_TdudakQkwfy_Kkhxbte_Yo_Jnfvdeueqq}

English summary: A single ciphertext string is given along with a "Waves" (Mr. Probz) parody lyric. The lyric is the crux — it encodes exactly how the shift behaves and how non-alphabetic characters interact with the position counter.

Analysis

The lyric drives the whole solution:

  • "I'm slowly shifting ... Char after char" → this is a Caesar cipher whose shift amount increases per character (a progressive / position-dependent shift), not a fixed rotation.
  • "Underscores against the stream / Braces against the stream" → the _, {, and } characters are not transformed, but they still "swim against the stream", i.e. they remain part of the flow and still advance the position counter.

So the shift applied to a letter equals its absolute zero-based index in the whole string (including separators), not a counter that only ticks on letters.

Known-plaintext confirmation. The flag wrapper is known to be bronco{. Comparing ciphertext bqmkyj{ to plaintext bronco{:

iCPshift (C-P)
0bb+0
1qr+1 (r+1=s? -> decrypt C-i)
2mo+2
3kn+3
4yc+4
5jo+5
6{{unchanged, but i still advances

The required per-letter shifts are exactly +0,+1,+2,+3,+4,+5, matching the absolute-index rule. Decryption therefore subtracts the index (mod 26).

Solution

For each alphabetic character at absolute zero-based string index i:

P[i] = (C[i] - base + i) mod 26 + base      (base = 'A' or 'a')

Non-alphabetic characters ({, }, _) are emitted unchanged, but i still increments for them.

python
#!/usr/bin/env python3
CT = "bqmkyj{Ldfmam_Nfd_Abxjpb_Thhdqeia_Snqn_Vzey_Bok_TdudakQkwfy_Kkhxbte_Yo_Jnfvdeueqq}"

def decrypt(ciphertext: str) -> str:
    output = []
    for index, char in enumerate(ciphertext):
        if char.isalpha():
            base = ord("A") if char.isupper() else ord("a")
            output.append(chr((ord(char) - base + index) % 26 + base))
        else:
            output.append(char)
    return "".join(output)

print(decrypt(CT))
# bronco{REDACTED}

Failed approaches (for other agents)

  • Advancing the shift only on alphabetic characters (skipping braces/underscores): the first word decodes, but everything after a separator turns to gibberish because the index falls out of sync.
  • Resetting the shift counter at each underscore/separator: also produces gibberish.

The correct model keeps a single monotonic counter over the entire string, exactly as the lyric ("underscores/braces against the stream") describes.