Skip to main content

Lock and Key

Challenge

Content preserved from the original writeup source. Minimal normalization was applied to fit platform format.

Solution

Original Writeup Content (Preserved)

Lock and Key (100) - Comprehensive Writeup

Challenge

Given text:

GEJYU?<dro{Yflcbi+

No downloadable files, no hints, and no extra metadata.

Initial Analysis

The string has a few useful traits:

  • Mixed uppercase and lowercase letters
  • Special symbols (?, <, {, +)
  • Looks close to a flag shape but not quite valid

This suggests a transform that preserves character positions while changing symbols and letters, which is common in keyboard-layout mistakes.

Dead-End Checks (Quick Triage)

Before committing to one idea, it is useful to eliminate common low-effort ciphers:

  • Caesar shift
  • ROT47
  • Atbash
  • Single-byte XOR
  • Rail fence transposition

None produced a clean CTF flag.

Core Insight

The title is Lock and Key. That strongly hints at keyboard keys, not classical substitution.

If text is typed while the system keyboard layout is wrong, each pressed physical key becomes a different character under another layout.

The successful mapping here is:

  • Source layout: Dvorak
  • Intended layout: QWERTY

So decode by translating each ciphertext character from Dvorak key-position output back into the corresponding QWERTY character.

Solve Script (Dvorak -> QWERTY)

s = "GEJYU?<dro{Yflcbi+"

# Physical key order rows for QWERTY and Dvorak
q = "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./"
d = "`1234567890[]',.pyfgcrl/=\\aoeuidhtns-;qjkxbmwvz"

# Shifted variants in the same physical key order
Q = "~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"
D = "~!@#$%^&*(){}\"<>PYFGCRL?+|AOEUIDHTNS_:QJKXBMWVZ"

# Dvorak -> QWERTY mapping
d2q = {d[i]: q[i] for i in range(len(q))}
d2q.update({D[i]: Q[i] for i in range(len(Q))})

decoded = ''.join(d2q.get(c, c) for c in s)
print(decoded)

Output:

UDCTF{Whos_Typing}

Flag

UDCTF{Whos_Typing}

Validation Script (Both Directions)

Useful for sanity checking and for future keyboard-layout CTFs.

def build_maps():
q = "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./"
d = "`1234567890[]',.pyfgcrl/=\\aoeuidhtns-;qjkxbmwvz"
Q = "~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?"
D = "~!@#$%^&*(){}\"<>PYFGCRL?+|AOEUIDHTNS_:QJKXBMWVZ"

q2d = {q[i]: d[i] for i in range(len(q))}
q2d.update({Q[i]: D[i] for i in range(len(Q))})

d2q = {d[i]: q[i] for i in range(len(q))}
d2q.update({D[i]: Q[i] for i in range(len(Q))})

return q2d, d2q


def translate(text, mapping):
return ''.join(mapping.get(c, c) for c in text)


if __name__ == "__main__":
ciphertext = "GEJYU?<dro{Yflcbi+"
q2d, d2q = build_maps()

plaintext = translate(ciphertext, d2q)
print("Decoded:", plaintext)

re_encoded = translate(plaintext, q2d)
print("Re-encoded:", re_encoded)
print("Round-trip OK:", re_encoded == ciphertext)

Expected output:

  • Decoded: UDCTF{Whos_Typing}
  • Re-encoded: GEJYU?<dro{Yflcbi+
  • Round-trip OK: True

Why This Works

Keyboard-layout ciphers are position-based, not alphabet-based. If you press the same physical keys on two different layouts:

  • Letter shapes can change drastically
  • Punctuation often shifts in suspicious ways
  • Braces and angle symbols can appear where flag braces should be

All of that matches this challenge perfectly.

Reusable CTF Heuristic

When you see text that is almost a flag but with odd punctuation:

  1. Test classical ciphers quickly.
  2. If they fail, test keyboard-layout transforms.
  3. Try Dvorak <-> QWERTY first.
  4. Validate by round-tripping decoded text back to ciphertext.

This approach is fast and reliable for short misc crypto-like challenges.