DEV Community

Cover image for BroncoCTF : Bundle Writeup
Yogeshwar Peela
Yogeshwar Peela

Posted on • Originally published at exploitnotes.hashnode.dev

BroncoCTF : Bundle Writeup

Summary

A .kra-adjacent artifact named Bundle_99 is handed off for analysis. It turns out to be a Krita resource bundle (.bundle) — a plain ZIP archive containing a brush preset (Brush 99.kpp). The .kpp file is itself a PNG with an embedded XML Preset block in its metadata. Buried inside dozens of harmless paintop parameters is a kis_text_brush definition whose text attribute contains the flag — Krita lets you generate a brush that stamps out literal text, so the flag was simply typed into that field and saved as a custom brush.

Flag: bronco{REDACTED}

Walkthrough

1. Identify the file type

The starting artifact, Bundle_99, doesn't carry a useful extension. file identifies it as a ZIP:

file Bundle_99
Bundle_99: Zip data (MIME type "application/x-krita-resourcebundle"?)
Enter fullscreen mode Exit fullscreen mode

The MIME type hint (application/x-krita-resourcebundle) confirms this is a Krita resource bundle — Krita's format for packaging brush presets, patterns, and other assets for sharing.

2. Inspect and extract the archive

zipinfo Bundle_99
Archive:  Bundle_99
Zip file size: 83110 bytes, number of entries: 5
-r--r--r--  3.0 unx       34 b- stor 26-Feb-13 20:44 mimetype
-r--r--r--  3.0 unx    19143 b- defN 26-Feb-13 20:44 paintoppresets/Brush 99.kpp
-r--r--r--  3.0 unx    62913 b- defN 26-Feb-13 20:44 preview.png
-r--r--r--  3.0 unx      436 t- defN 26-Feb-13 20:44 META-INF/manifest.xml
-r--r--r--  3.0 unx      907 t- defN 26-Feb-13 20:44 meta.xml
5 files, 83433 bytes uncompressed, 82558 bytes compressed:  1.0%

unzip Bundle_99
Archive:  Bundle_99
 extracting: mimetype
  inflating: paintoppresets/Brush 99.kpp
  inflating: preview.png
  inflating: META-INF/manifest.xml
  inflating: meta.xml
Enter fullscreen mode Exit fullscreen mode

This is the same layout OpenDocument-family formats use — a mimetype file, a META-INF/manifest.xml, and asset payloads. Confirmed by the raw mimetype contents:

cat mimetype
application/x-krita-resourcebundle
Enter fullscreen mode Exit fullscreen mode

3. Check the bundle metadata

meta.xml and META-INF/manifest.xml describe the bundle but hold no flag — just author/license placeholders and a file manifest with an MD5 for the single preset:

cat meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<meta:meta>
 <meta:generator>Krita (5.2.10 (git ea9eb7a))</meta:generator>
 <meta:bundle-version>1</meta:bundle-version>
 <dc:author>Bundle 99</dc:author>
 <dc:title>Bundle 99</dc:title>
 <dc:description>99 bundles of brushes on the wall, 99 bundles of brushes.
Take one down and pass it around, 98 bundles of brushes on the wall.</dc:description>
 ...
</meta:meta>

cat META-INF/manifest.xml
<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2">
 <manifest:file-entry manifest:media-type="application/x-krita-resourcebundle" manifest:full-path="/"/>
 <manifest:file-entry manifest:media-type="paintoppresets" manifest:full-path="paintoppresets/Brush 99.kpp" manifest:md5sum="3c3433276c419d607d0a5b60d65fc21d"/>
</manifest:manifest>
Enter fullscreen mode Exit fullscreen mode

A quick recursive grep for the flag prefix across the extracted tree comes up empty — it isn't sitting in any plaintext file:

grep -rn "bronco{" .
Enter fullscreen mode Exit fullscreen mode

(no output — the flag isn't sitting in plaintext anywhere in the tree)

4. Identify the actual type of the .kpp preset

.kpp looks like a proprietary Krita extension, but file-type detection shows it's really a PNG:

file paintoppresets/Brush\ 99.kpp
paintoppresets/Brush 99.kpp: PNG image data, 200 x 200, 8-bit/color RGBA, non-interlaced
Enter fullscreen mode Exit fullscreen mode

Krita brush presets store a 200×200 thumbnail as the "real" PNG image, then stash the entire preset configuration (every paintop parameter — size, spacing, opacity curves, sensor curves, etc.) as XML inside a PNG text/metadata chunk. That's the natural place to dig with exiftool.

5. Pull the embedded metadata with exiftool

exiftool paintoppresets/Brush\ 99.kpp
...
Preset  : <Preset embedded_resources="0" paintopid="paintbrush" name="Brush 99">
  <param name="ColorSource/Type" type="string"><![CDATA[plain]]></param>
  ...
  <param name="brush_definition" type="string"><![CDATA[<Brush spacing="0.1" density="1" type="auto_brush" ...> <MaskGenerator .../> </Brush>]]></param>
  ...
  <param name="brush_definition" type="string"><![CDATA[<Brush font="Segoe UI,9,-1,5,50,0,0,0,0,0" spacing="0.2" pipe="false" type="kis_text_brush" BrushVersion="2" text="bronco{REDACTED}"/>]]></param>
  ...
</Preset>
Version      : 5.0
Image Size   : 200x200
Megapixels   : 0.040
Enter fullscreen mode Exit fullscreen mode

The Preset XML block is a long dump of nearly every paintop setting Krita tracks (pressure sensors, opacity/size/rotation curves, masking brush config, etc.) — almost all noise. Two brush_definition params stand out, though:

  • One is type="auto_brush" — a normal procedurally-generated circular brush tip.
  • The other is type="kis_text_brush" — Krita's text brush, which renders a literal string as the brush stamp shape instead of a generated tip. Its text attribute is the flag: text="bronco{REDACTED}".

Someone built this preset specifically to smuggle the flag: rather than hiding it in unrelated metadata fields, they used a first-class Krita feature (typing text to create a custom brush shape) and just never intended to actually paint with it.

Attack Chain

Bundle_99 (misnamed ZIP, MIME hints "krita-resourcebundle")
        │
        ▼
unzip → paintoppresets/Brush 99.kpp, meta.xml, manifest.xml, mimetype, preview.png
        │
        ▼
file Brush 99.kpp → actually a PNG (Krita preset thumbnails carry embedded config)
        │
        ▼
exiftool Brush 99.kpp → dumps <Preset> XML metadata chunk
        │
        ▼
Two <param name="brush_definition"> entries:
   - auto_brush (decoy, procedural tip)
   - kis_text_brush  text="bronco{FLAG}"  ← flag hidden as brush glyph text
Enter fullscreen mode Exit fullscreen mode

Key Points

Step Technique Notes
File identification file on an unlabeled artifact ZIP masquerading as a generic filename revealed as a Krita bundle via MIME string
Archive structure OpenDocument-style bundle layout mimetype + META-INF/manifest.xml + payload, same pattern as ODF/EPUB
Nested file-type mismatch .kpp extension vs. actual PNG magic bytes Krita presets are PNGs with an embedded XML config chunk
Metadata extraction exiftool Pulled the full <Preset> XML, including all paintop parameters
Data hiding technique Krita kis_text_brush Flag stored as the literal text attribute of a text-based brush definition — a legitimate feature repurposed for steganography

Top comments (0)