The Temple

TempleOS — print to the terminal after `MountIDEAuto`, and decode the 8x8 glyphs from the noVNC canvas pixels instead of reading them by eye

2026.09.30 NNS CTF 2026 155 pts Misc
FLAG NNS{4_g0D1Y_Holy_50MetHiNg_some7Hin6_opeRa7iN6_systeM_th4t_rUn5_1N_r1ng_0_is_5uch_a_Be4U7y}

Flag: NNS{4_g0D1Y_Holy_50MetHiNg_some7Hin6_opeRa7iN6_systeM_th4t_rUn5_1N_r1ng_0_is_5uch_a_Be4U7y} (91 chars; FLAG.TXT is 0x5C = 92 bytes = 91 + trailing \n - consistency check passes)

Solution path

  1. Boot prompts: n + Return twice (“Install onto hard drive”, “Take Tour”).
  2. DrvRep; -> only B: (REDSEA RAM) and T: (REDSEA ATAPI). No flag drive mounted.
  3. MountIDEAuto; -> detects [1] ATA Primary IDE hard drive, [2] ATAPI CD.
  4. DrvRep; again -> now C: FAT32 ATA (QEMU HARDDISK), U: ISO9660.
  5. Dir("C:/*"); -> FLAG.TXT 005C bytes.
  6. Ed("C:/FLAG.TXT"); shows it but the editor CLIPS the long line at the window width (~39 cols) and does not wrap, and End jumps to end-of-document, so you can’t see past char 39. Shift-Esc to abort out.
  7. Instead print it in the TERMINAL, which does wrap: "%s",FileRead("C:/FLAG.TXT"); -> flag wraps over 3 display rows (39+39+13).

The important trick: never read the 8x8 font by eye

Reading the flag visually gets leet chars wrong (l/1/I, 0/O, 5/S, A/N…). noVNC renders into a plain 2D canvas, so the exact framebuffer is readable from JS:

const c=document.querySelector('canvas'), g=c.getContext('2d');
const d=g.getImageData(0,0,640,480).data;   // 16-color palette, no AA

TempleOS is an exact 80x60 grid of 8x8 cells aligned at (0,0), so cell (col,row) = pixels [col8 .. col8+7] x [row8 .. row8+7]. Threshold against the row’s background colour (0xffffff in the terminal window) -> 8 bytes per glyph -> build a bitmap->char table from known on-screen text and decode exactly.

Glyph pairs that actually differ (why eyeballing fails): ‘1’ = 1838181818187e00 ‘l’ = 3818181818183c00 ‘0’ = 3c666e7e76663c00 (has the internal diagonal) ‘o’ = 00003c6666663c00 ‘N’ = 6666767e6e666600 ‘U’ = 6666666666663c00

Keyboard input into noVNC (CDP typing is broken here)

computer type/key deliver only UNSHIFTED chars: noVNC sends a QEMU Extended Key Event carrying the raw scancode from event.code, and QEMU ignores the keysym, so without a real Shift keydown you get drvrep; instead of DrvRep;. Fix: dispatch KeyboardEvents on the canvas from JS with an explicit Shift keydown/keyup wrapped around each shifted char, ~120ms between steps (guest is laggy; ~2s per char wall-clock). Always send a final Shift keyup or the guest keeps SHIFT stuck (shown in the top-right of the TempleOS status bar).

Diff vs the eyeballed attempt

pos 14: ‘1’ -> ‘l’ (Ho1y -> Holy) pos 65: ‘A’ -> ‘N’ (1A -> 1N i.e. “runs 1N ring 0”)