Subtract

TL;DR

We’re given a list of pixel coordinates for a 500×500 canvas. Plotting all points looks like noisy blobs.

“Remove some stuff” → remove duplicates / take odd parity (XOR) of repeated coordinates, then strip interior pixels to keep only the outline. The outline spells the flag:

ctf{5ub7r4c7_7h3_n01s3}


Challenge

  • Category: Misc
  • Given: coordinates.txt containing lines like (x, y)
  • Hint: “The image size is 500x500. You might want to remove some stuff... Note: Some may call it guessy!”

High-level Idea

  1. Treat each (x, y) as a white pixel on a black 500×500 image.
  1. Remove some stuff → two interpretations that both help:
    • Odd parity (XOR duplicates): if a coordinate appears multiple times, keep it only if it appears an odd number of times.
    • Remove interior pixels: keep only pixels that touch background in their 4-neighborhood (an outline).
  1. The odd-parity outline reveals crisp characters you can read as the flag.

Steps

1) Parse and plot

  • Read coordinates.txt with readlines().
  • Parse integers with a regex.
  • Plot raw points (will look like chunky blobs).

2) Odd-parity (XOR) filter

  • Count occurrences of each (x, y).
  • Keep points whose count is odd.

    This “subtracts” duplicates and often removes large filled areas.

3) Outline extraction

  • Take the remaining points and drop interior pixels:
    • For a point (x, y), if any of its 4-neighbors (up, down, left, right) is missing (or out of bounds), keep it.
    • Otherwise, it’s interior → remove it.
  • The result is a thin outline.

4) (Optional) Odd/Even line split

  • Another “guessy” trick: render points from odd vs even lines separately. Sometimes only one subset is meaningful. Here, odd-parity + outline already does the job.

5) Read the flag

  • The outline image shows ctf{5ub7r4c7_7h3_n01s3}.

Code (single-file, uses Pillow)

# solve.py
# pip install pillow
import re
from collections import Counter
from PIL import Image

W = H = 500
SCALE = 3  # for saving bigger previews

def read_coords(path="coordinates.txt"):
    with open(path, "r", encoding="utf-8") as f:
        lines = f.readlines()
    pts = []
    for ln in lines:
        nums = re.findall(r"-?\d+", ln)
        if len(nums) >= 2:
            x, y = int(nums[0]), int(nums[1])
            if 0 <= x < W and 0 <= y < H:
                pts.append((x, y))
    return pts, lines

def save_points_img(points, outname):
    im = Image.new("L", (W, H), 0)
    px = im.load()
    for (x, y) in points:
        px[x, y] = 255
    if SCALE != 1:
        im = im.resize((W*SCALE, H*SCALE), Image.NEAREST)
    im.save(outname)

def outline(points):
    s = set(points)
    out = []
    for (x, y) in s:
        for dx, dy in ((1,0),(-1,0),(0,1),(0,-1)):
            nx, ny = x+dx, y+dy
            if not (0 <= nx < W and 0 <= ny < H) or (nx, ny) not in s:
                out.append((x, y))
                break
    return out

def main():
    pts, lines = read_coords("coordinates.txt")
    print(f"Loaded {len(pts)} pts")

    # Raw
    save_points_img(pts, "points.png")

    # Odd parity (XOR)
    c = Counter(pts)
    odd_parity = [p for p in c if c[p] % 2 == 1]
    save_points_img(odd_parity, "odd_parity.png")

    # Outline of odd parity
    out_odd = outline(odd_parity)
    save_points_img(out_odd, "outline_oddparity.png")

    # (Optional) Odd/Even lines split
    odd_lines, even_lines = [], []
    for i, ln in enumerate(lines, start=1):
        nums = re.findall(r"-?\d+", ln)
        if len(nums) >= 2:
            x, y = int(nums[0]), int(nums[1])
            if 0 <= x < W and 0 <= y < H:
                (odd_lines if i % 2 == 1 else even_lines).append((x, y))
    save_points_img(odd_lines, "odd_lines.png")
    save_points_img(even_lines, "even_lines.png")

    print("Saved:",
          "points.png, odd_parity.png, outline_oddparity.png,",
          "odd_lines.png, even_lines.png")

if __name__ == "__main__":
    main()

Why this works

  • Many misc image/coordinates CTFs hide the signal in the parity of repeated pixels (think XOR).
  • Filled areas make reading hard; extracting the outline preserves shape while removing clutter.
  • The hint “remove some stuff… guessy” nudges toward trying simple heuristics like dedup, XOR, outline, odd/even splits.

Pitfalls / Gotchas

  • Make sure (0,0) is top-left (invert Y when plotting with typical math axes).
  • Keep coordinates within bounds 0…499.
  • If your outline still looks fat, run outline twice or downsample then re-outline.

Artifacts to attach (screens)

  • points.png — raw points (blobby).
  • odd_parity.png — XORed points.
  • outline_oddparity.png — thin outline showing readable text (the flag).

Flag

ctf{5ub7r4c7_7h3_n01s3}