← Back to Writeups
CUSTOMEasySteganography

Три в одном (Three in One)

XESXOR9/2/20264 min read
#steganography#custom#easy#color_channel_extraction#rgb_ascii_encoding

Три в одном (Three in One)

Platform: duckerz | Category: Steganography | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-02-09 | Status: Solved Techniques: color_channel_extraction, rgb_ascii_encoding

Summary

Task: PNG image with text where each letter has a unique color. Solution: Interpret RGB values as ASCII characters (3 bytes per color = 3 chars per letter).

Три в одном (Three in One) — duckerz CTF

Description

"Это же про кофе так говорят? Я больше чай люблю, особенно с цветами"

File: deliciousdrink.png (PNG image data, 1420 x 360, 8-bit/color RGBA)

The image contains text "coffee maybe?" with a coffee cup and beans on the right.

Analysis

Standard recon — nothing

  • file / exiftool — regular PNG, no anomalies in metadata
  • strings — no flag
  • binwalk — no embedded files, no data after IEND
  • zsteg / stegsolve — standard LSB techniques yield nothing ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Breaking down the hints

The task contains three key hints:

  1. "Три в одном" (Three in one) — task name. In RGB context: 3 bytes (ASCII characters) encoded in 1 color (R, G, B).

  2. "особенно с цветами" (especially with flowers/colors) — the word "цветами" in Russian has a double meaning:

    • "цветы" (flowers) — a red herring
    • "цвета" (colors) — pointing to color channels
  3. "Это же про кофе так говорят?" (That's what they say about coffee, right?) — "three in one" = 3-in-1 coffee (coffee + cream + sugar). But the data is hidden in text colors, not in coffee. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Discovery

Upon close inspection, each letter of the text "coffee maybe?" is rendered in its own unique color. The colors look visually similar (dark shades), but hex values differ:

LetterColor (hex)ASCII
c#445543DUC
o#4b4552KER
f#5a7b6eZ{n
f#6f215fo!_
e#695f77i_w
e#616e74ant
m#5f6d79_my
a#5f6661_fa
y#766f75vou
b#726974rit
e#655f74e_t
?#65617dea}
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The space between "coffee" and "maybe?" is skipped (contains no data).

Vulnerability

RGB Color Channel Steganography — encoding ASCII in text colors.

Each RGB color is 3 bytes. If we interpret (R, G, B) as three ASCII characters, we get a hidden message. 12 letters × 3 characters = 36 characters of the flag.

Solution

python
#!/usr/bin/env python3
"""
Solution for "Three in One" — duckerz CTF
RGB steganography: each letter of text is colored with a color
where R, G, B = 3 ASCII characters of the flag.
"""
from PIL import Image
import numpy as np
from collections import defaultdict, Counter
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

img = Image.open('deliciousdrink.png')
data = np.array(img)

# Step 1: Find all dark (text) pixels in the lower part
text_pixels = []
for y in range(100, 360):
    for x in range(data.shape[1]):
        r, g, b, a = data[y, x]
        if r < 170 and g < 170 and b < 170:
            text_pixels.append((x, y, r, g, b))

# Step 2: Group pixels by X-coordinates (each letter = group)
x_counts = defaultdict(int)
for x, y, r, g, b in text_pixels:
    x_counts[x] += 1

xs = sorted(x_counts.keys())
groups = []
current_group = [xs[0]]
for i in range(1, len(xs)):
    if xs[i] - xs[i-1] > 5:  # gap > 5px = new letter
        groups.append((current_group[0], current_group[-1]))
        current_group = [xs[i]]
    else:
        current_group.append(xs[i])
if current_group:
    groups.append((current_group[0], current_group[-1]))
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Step 3: For each group find the dominant color
hex_string = ""
for start, end in groups:
    colors = Counter()
    for x, y, r, g, b in text_pixels:
        if start <= x <= end:
            colors[(r, g, b)] += 1
    top_color = colors.most_common(1)[0][0]
    r, g, b = top_color
    hex_string += f"{r:02x}{g:02x}{b:02x}"

# Step 4: Decode hex as ASCII
flag = bytes.fromhex(hex_string).decode('ascii')
print(f"[+] Flag: {flag}")

Output:

[+] Flag: DUCKERZ{REDACTED}

Related Techniques

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  • LSB steganography — data in least significant bits, here data is in full color bytes
  • Palette-based stego — similar principle, but through PNG palette
  • Font color stego in HTML/documents — same technique, but in web context (invisible text via font color = background color) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍