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:
- Test classical ciphers quickly.
- If they fail, test keyboard-layout transforms.
- Try
Dvorak <-> QWERTYfirst. - Validate by round-tripping decoded text back to ciphertext.
This approach is fast and reliable for short misc crypto-like challenges.