← Back to Writeups
HTBN/ASteganography

Bundle 99

XESXOR8/23/20264 min read
#steganography#htb#n/a

Bundle 99

Platform: Broncoctf2026 | Category: Steganography | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-07-11 | Status: Solved Techniques: file_type_identification, kis_text_brush_extraction, png_text_chunk_metadata, zip_extraction

Summary

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_brush text= attribute containing the flag.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: broncoctf2026 | ID: 20260711_broncoctf2026_bundle_99
  • Tags: zip, exiftool, png_metadata, krita, resourcebundle, kpp, brush_preset, file_identification
  • Indicators: extensionless file identified as Zip data (MIME application/x-krita-resourcebundle), internal entries paintoppresets/*.kpp, preview.png, META-INF/manifest.xml, .kpp is a PNG carrying serialized preset XML in a Preset text chunk
  • Source: 20260711_broncoctf2026_bundle_99.md

Foothold

Vulnerability / Misconfiguration

  1. File_type_identification
  2. Kis_text_brush_extraction
  3. Png_text_chunk_metadata
  4. Zip_extraction
<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

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • file_type_identification
  • kis_text_brush_extraction
  • png_text_chunk_metadata
  • zip_extraction
  • Tags: zip, exiftool, png_metadata, krita, resourcebundle, kpp, brush_preset, file_identification

Original Writeup

<details><summary>Click to expand original content</summary>

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:
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):
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:
<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

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

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR