← Back to Writeups
CUSTOMEasySteganography

Bundle 99

XESXOR9/10/20263 min read
#steganography#custom#easy#file_type_identification#kis_text_brush_extraction#png_text_chunk_metadata#zip_extraction

Bundle 99

Platform: CUSTOM | Category: Steganography | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: file_type_identification, kis_text_brush_extraction, png_text_chunk_metadata, zip_extraction

Summary

Walkthrough of the CUSTOM Steganography Bundle 99. Task: unknown extensionless file that is a Krita .bundle (ZIP) of brush presets. Solution: identify via file/MIME, unzip, then read the Brush 99.kpp PNG metadata with exiftool to extract the kis_text_

Description

Yoshie found this random file laying around. Do you have any idea what this 'bundle' is? Apparently someone told him it's the 99th bundle of brushes? What is that supposed to mean... P.S. This challenge can be solved without downloading any software, but you'll have to hunt for a way to run the related program online and send the bundle to it.

English summary: An extensionless file Bundle_99 (83110 bytes) is provided. The wordplay ("bundle", "brushes", "99th bundle", "run the related program online") points to Krita and its .bundle resource bundle format. The goal is to recover a bronco{...} flag. The intended path was to load the bundle in Krita (or a web build of Krita) and paint with a text brush to stamp the flag — but the flag is stored as plaintext in the brush preset's PNG metadata, so no software is required.

Analysis

Recon on the unknown file:

$ file Bundle_99
Bundle_99: Zip data (MIME type "application/x-krita-resourcebundle"?)

$ strings Bundle_99 | grep -Ei 'mimetype|kpp|preview|manifest|meta'
mimetype
application/x-krita-resourcebundle
paintoppresets/Brush 99.kpp
preview.png
META-INF/manifest.xml
meta.xml

Key deductions:

  • The file magic + MIME application/x-krita-resourcebundle identifies this as a Krita resource bundle — a ZIP container. Krita is the "related program" the description hints at.
  • The internal entry paintoppresets/Brush 99.kpp is the "99th bundle of brushes" / "Brush 99" wordplay: a single brush preset named Brush 99.
  • Krita .kpp brush presets are PNG files (here 200×200 RGBA) that store the serialized preset XML inside a PNG text chunk named Preset. Krita text brushes (kis_text_brush) store their rendered string in a text= attribute — a perfect place to hide a flag.

No pixel LSB stego is involved; preview.png is just a laughing emoji and is a decoy.

Solution

  1. Extract the ZIP container:
bash
unzip Bundle_99 -d extracted
  1. The brush preset is a PNG with the serialized preset XML in its Preset text chunk. Read the embedded metadata with exiftool (no need to run Krita):
bash
exiftool "extracted/paintoppresets/Brush 99.kpp"
  1. Among the <param> entries in the serialized preset, the brush_definition param holds a Krita text brush. The relevant snippet:
xml
<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}"/>

The text= attribute of the kis_text_brush is the flag — the string that would be stamped onto the canvas if you actually painted with this brush in Krita.

One-liner alternative

bash
unzip -p Bundle_99 "paintoppresets/Brush 99.kpp" | strings | grep -o 'text="bronco{[^"]*}"'
# text="bronco{REDACTED}"

Key Takeaways / Lessons

  • Mastering file_type_identification, kis_text_brush_extraction, png_text_chunk_metadata, zip_extraction was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-07-11 • CUSTOM • Bundle 99 • Difficulty: Easy