$ cat writeup.md…
$ cat writeup.md…
sunshinectf2026
Task: a Microsoft Keyboard Layout Creator (.klc) file encodes one ASCII char and one arrow glyph per key across three keyboard rows, with the ASCII chars scrambled. Solution: map keys to a physical QWERTY grid, treat arrow glyphs as movement directions, and walk the trail from the start key to the stop marker, collecting ASCII chars in visit order to reconstruct the flag.
im lost, but you can find the way!
English summary: A single small ASCII file suntrail.klc (a Microsoft Keyboard Layout Creator source file) is provided. The flag is hidden in the layout data and must be recovered by following a "trail" through the keyboard.
The .klc file uses the standard KLC structure: a KBD header, a SHIFTSTATE block declaring states 0/1/2, and a LAYOUT block. Each key row has the format:
scancode VK cap shiftstate0 shiftstate1 shiftstate2
Only three physical keyboard rows are populated (plus SPACE):
Q W E R TA S D F G HZ X C V B NTwo columns carry the payload:
shiftstate0) is a Unicode codepoint for an arrow glyph:
2198 = ↘2192 = →2196 = ↖25a0 = ■ (stop / terminal marker)shiftstate1) is a single ASCII character codepoint (e.g. 0073='s', 007b='{', 007d='}').Reading the ASCII chars in key order (Q,W,E,...) yields a scrambled string that is clearly not a flag. The description hint — "im lost, but you can find the way" — plus the arrow glyphs point to a path-walking puzzle: the arrows form a route across the physical keyboard, and the flag is the sequence of ASCII chars visited in trail order, not key order.
Key observations that pin the mechanic:
25a0) stop marker (H, which holds }) — the terminator of the walk.sun{ fragments and a }, consistent with a sun{...} flag once ordered correctly.LAYOUT row; extract field 4 (arrow codepoint) and field 5 (ASCII codepoint) per virtual key. Skip SPACE and the header/footer lines.(row, col)), and a reverse map from coordinates back to keys.^sun{...}$. The recovered mapping is:
2198) = Down (row+1, col+0)2192) = Right (row+0, col+1)2196) = Up (row-1, col+0)25a0) = stopcurrent_position + delta(arrow), appending each visited key's ASCII char, until the stop marker ■ is reached.sun{...} format (a joke about qwerty keyboards). The terminator key carries }.#!/usr/bin/env python3 import re, itertools # Physical keyboard grid (row, col) grid = { 'Q': (0, 0), 'W': (0, 1), 'E': (0, 2), 'R': (0, 3), 'T': (0, 4), 'A': (1, 0), 'S': (1, 1), 'D': (1, 2), 'F': (1, 3), 'G': (1, 4), 'H': (1, 5), 'Z': (2, 0), 'X': (2, 1), 'C': (2, 2), 'V': (2, 3), 'B': (2, 4), 'N': (2, 5), } pos2key = {v: k for k, v in grid.items()} STOP = '25a0' def parse_klc(path): data = {} # VK -> (arrow_codepoint, ascii_char) with open(path, encoding='utf-8') as f: in_layout = False for line in f: s = line.strip() if s == 'LAYOUT': in_layout = True continue if s == 'ENDKBD': break if not in_layout: continue parts = re.split(r'\s+', s) if len(parts) < 6: continue vk = parts[1] if vk not in grid: continue arrow = parts[3].lower() ch = chr(int(parts[4], 16)) data[vk] = (arrow, ch) return data def walk(data, start, delta): cur, out, seen = start, '', set() while True: if cur not in data: return None arrow, ch = data[cur] out += ch seen.add(cur) if arrow == STOP: break if arrow not in delta: return None r, c = grid[cur] dr, dc = delta[arrow] nxt = pos2key.get((r + dr, c + dc)) if nxt is None or nxt in seen: return None cur = nxt return out, seen def solve(path): data = parse_klc(path) arrows = sorted({a for a, _ in data.values()} - {STOP}) # 8 compass directions dirs = [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)] for start in data: for combo in itertools.permutations(dirs, len(arrows)): delta = dict(zip(arrows, combo)) res = walk(data, start, delta) if not res: continue out, seen = res if out.startswith('sun{') and out.endswith('}') and len(seen) == len(data): return out # full Hamiltonian walk producing a valid flag if __name__ == '__main__': print(solve('suntrail.klc')) # prints sun{...}
Use this technique when:
.klc (Microsoft Keyboard Layout Creator) file is given as the challenge artifact.2198, 2192, 2196, etc.) and a 25a0 (■) appears as a lone terminator.sun{/{/} fragments.$ cat /etc/motd
Liked this one?
Pro unlocks every complete writeup and expanded API access. $9/mo.
$ cat pricing.md$ grep --similar