Enhance! picoCTF 2022 Solution

Published: July 20, 2023

Description

Despite the hint to "enhance," there's no need to zoom the image; just read the text stored in the SVG file.

Run strings or open the SVG in a text editor.

Grep for lines containing > / < and strip the XML tags to reveal the embedded characters.

bash
strings drawing.flag.svg | grep ">" | cut -d '>' -f2 | cut -d '<' -f1 | tr -d '\n '

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Treat the SVG as text
    Observation
    I noticed the challenge file was an SVG rather than a raster image like PNG or JPEG, which suggested that its content is stored as XML markup and can be read directly as text without any image processing.
    SVG files are XML, so you can view them as text. The flag's letters appear as separate <text> nodes.
    Learn more

    SVG (Scalable Vector Graphics) is an XML-based image format that describes graphics through markup rather than pixels. Unlike raster formats (PNG, JPEG), SVG files are plain text that a browser or renderer interprets - you can open one in any text editor and read the underlying structure directly.

    Because SVG is XML, it embeds arbitrary text inside <text> elements. That XML stays in the file even when the renderer hides it. Authors can shrink the font (font-size="0.1pt"), position the text outside the canvas (x="-9999"), set visibility="hidden", or use color matching the background. None of those affect the underlying XML; strings reads them verbatim.

    The challenge title references the TV trope of endlessly enhancing low-resolution images to reveal hidden detail. The joke is that the real "enhancement" is recognizing that SVG is text, not a bitmap - no image processing needed.

  2. Step 2
    Remove the markup
    Observation
    I noticed the flag characters were spread across many individual XML text nodes rather than appearing on a single line, which suggested that standard text tools like cut and tr would be needed to strip the surrounding tags and concatenate the pieces into one readable string.
    Strip the tags, drop whitespace, and concatenate. Build it up step by step so you can sanity-check each stage.
    bash
    strings drawing.flag.svg | grep '>'
    bash
    strings drawing.flag.svg | grep '>' | cut -d '>' -f2
    bash
    strings drawing.flag.svg | grep '>' | cut -d '>' -f2 | cut -d '<' -f1
    bash
    strings drawing.flag.svg | grep '>' | cut -d '>' -f2 | cut -d '<' -f1 | tr -d '\n '
    bash
    xmlstarlet sel -t -v "//text()" drawing.flag.svg | tr -d '\n '

    Expected output

    picoCTF{...}
    What didn't work first

    Tried: Open the SVG in a browser and use the browser's built-in zoom to magnify the tiny text until it is readable.

    The text nodes have a font-size so small they render as invisible or single-pixel dots even at maximum browser zoom. The browser renders the visual output, not the raw XML, so zooming just scales up blank space. Reading the file as text with strings or a text editor bypasses the renderer entirely and reveals the characters directly.

    Tried: Run grep 'picoCTF' drawing.flag.svg to find the flag in one shot instead of building the cut pipeline.

    Each letter of the flag is stored in its own separate XML text element across many lines, so no single line contains the full flag string. grep matches whole lines, so it returns nothing useful. The cut and tr pipeline is needed to extract and concatenate the per-character inner text nodes into one continuous string.

    Learn more

    Walk the pipeline left to right. strings ... | grep ">" filters to lines that contain at least one tag terminator - i.e. lines with text content sitting next to markup. cut -d '>' -f2 keeps everything to the right of the first >, dropping the opening tag. cut -d '<' -f1 keeps everything to the left of the next <, dropping the closing tag. What survives is the inner text. tr -d '\n ' collapses the per-character lines into one continuous string - that's your flag.

    The pipeline is fragile (multiple tags per line, attributes containing >, mixed content all break it) but fast enough for well-formed simple SVG. For anything bigger, use a real XML parser: xmlstarlet sel -t -v "//text()" drawing.flag.svg uses XPath to extract all text nodes regardless of layout. Python's xml.etree.ElementTree works too:

    import xml.etree.ElementTree as ET
    tree = ET.parse('drawing.flag.svg')
    print(''.join(t.strip() for t in tree.getroot().itertext()))
Interactive tools
  • Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.

Flag

Reveal flag

picoCTF{...}

Sometimes "enhancing" means simply inspecting the source.

Key takeaway

SVG is an XML-based vector format, meaning the file is plain text that a renderer interprets rather than a grid of pixels. Data hidden inside SVG text elements stays fully readable as markup regardless of visual tricks like zero font size, off-canvas positioning, or color matching the background. This same principle applies broadly: any 'file format' that is actually XML or JSON (OOXML, ODT, SVG, RSS) exposes its full structure to a text editor or standard parsing tools, making renderer-level obfuscation ineffective against an attacker who reads the source.

Related reading

Want more picoCTF 2022 writeups?

Useful tools for Forensics

What to try next