Terminal Diff
Terminal Diff
Platform: Broncoctf2026 | Category: Misc | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-07-11 | Status: Solved Techniques: ascii_art_ocr_via_pixel_render, braille_image_recognition, directional_arrow_spiral_reading, factoring_character_count_to_terminal_width, figlet_ticks_font_recognition, text_wrapping_at_97_columns
Summary
Task: a single 3395-char Unicode line (flag.txt) whose riddle hints at a terminal size. Solution: factor the length (3395 = 5797), wrap at 97 columns to reveal FIGlet 'ticks' block letters around a Braille oyster, render to pixels for OCR, then read the glyphs in a clockwise spiral dictated by edge arrows.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
broncoctf2026| ID:20260711_broncoctf2026_terminal_diff - Tags: steganography, ascii_art, wordplay, braille, misc, figlet, ticks_font, terminal_width, text_wrapping
- Indicators: single very long line of Unicode text, characters limited to / \ _ < > ^ v plus U+2800 Braille block, riddle hints at terminal width/height, character count factors cleanly (3395 = 5797)
- Source:
20260711_broncoctf2026_terminal_diff.md
Foothold
Vulnerability / Misconfiguration
- Ascii_art_ocr_via_pixel_render
- Braille_image_recognition
- Directional_arrow_spiral_reading
- Factoring_character_count_to_terminal_width
- Figlet_ticks_font_recognition
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- ascii_art_ocr_via_pixel_render
- braille_image_recognition
- directional_arrow_spiral_reading
- factoring_character_count_to_terminal_width
- figlet_ticks_font_recognition
- text_wrapping_at_97_columns
- Tags: steganography, ascii_art, wordplay, braille, misc, figlet, ticks_font, terminal_width, text_wrapping
Original Writeup
<details><summary>Click to expand original content</summary>Description
I used to be too big picture, never focusing on the details (keeping track of like 90 things at once). Then, I starting looking into things a little bit too much (this phase only lasted like 7 days). Nowadays though, I am primed to look at things with just the right width (and height too). Anyways, here's the flag! You should be able to read it just fine, as long as you align with my mindset.
The only artifact is flag.txt from the CTFd instance: one very long single line of Unicode text. The riddle is a disguised hint about the correct terminal width (and height) at which the line should be wrapped so that the ASCII art becomes readable.
Analysis
file flag.txt reports "Unicode text, UTF-8, with very long lines". The file is a single line of 3395 characters (4140 bytes). The character set is small:
/ \ _ < > ^ v— ASCII art strokes and directional markers- many codepoints in the Braille block (U+2800) — used to draw a small picture
The riddle encodes a terminal size through numeric decoys:
- "keeping track of like 90 things" → width 90 is too big
- "this phase only lasted like 7 days" → width 7 is too small
- "just the right width (and height too)" + "align with my mindset" → wrap the single line at the correct terminal width
The clean way to find the width is to factor the character count:
3395 = 5 × 7 × 97
Wrapping the line every 97 characters yields exactly 35 rows (5 × 7). The intended terminal is 97 columns × 35 rows.
Solution
1. Factor the length and wrap
import sympy
s = open('flag.txt').read().rstrip('\n')
print(len(s), sympy.factorint(len(s))) # 3395 {5:1, 7:1, 97:1}
w = 97
print('\n'.join(s[i:i+w] for i in range(0, len(s), w)))
Wrapped at 97 columns, the / and \ characters form large block letters — a narrow FIGlet "ticks"-style font (the standard figlet -f ticks uses /\/\ per stroke; this puzzle uses a half-width /\ variant). The _ characters are the font's background padding. The Braille block (U+2800) in the middle draws an oyster — a thematic nod to the idiom "the world is your oyster".
2. Render to pixels for OCR
Treat / and \ as filled pixels and everything else as background, then rasterize to a scaled PNG so the letters become legible:
from PIL import Image
rows = [s[i:i+97] for i in range(0, len(s), 97)]
scale = 9
img = Image.new('1', (97*scale, 35*scale), 1)
px = img.load()
for r in range(35):
line = rows[r] if r < len(rows) else ''
for c in range(97):
if c < len(line) and line[c] in '/\\':
for dy in range(scale):
for dx in range(scale):
px[c*scale+dx, r*scale+dy] = 0
img.save('clean.png')
3. The letter grid
The glyphs are laid out in a 6×8 grid of FIGlet letters wrapping around the central oyster:
bronco{r
le_world
o as <- oyster image occupies the middle of these three lines
h ti
w z
_say_...
4. Directional arrows define reading order
Non-letter markers appear at the edges of the grid:
>>at the left of bandle_world→ read rightwardvvvvdown the right side → read downward<<at the bottom-right → read leftward^^^^up the left side → read upward
These trace a clockwise spiral around the oyster. Reading the grid naively in row-major order produces gibberish (bronco{rle_worldoashtiwz_say_...); the arrows are mandatory.
5. Decode the words
Verified glyph-by-glyph from the render:
- Rightmost column top→bottom, continuing into the bottom-right corner =
resizing(r-e-s-i-z-i-n-g) - Left column bottom→top, turning right into band
le_world=whole_world(who + le + _world) - Bottom-middle =
say(appears reversed as_yas_because the spiral crosses the bottom right-to-left)
Assembling the spiral in natural reading order yields the sentence matching the riddle's theme — fitting "the whole world" into the terminal by resizing it:
bronco{REDACTED}
</details>
Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR