MISC-黑白琴弦

ISCC2026 WriteUp 提交模板

MISC-黑白琴弦

你是一位钢琴学徒,这天,著名的钢琴大师传授给你一个特殊的琴谱,请耐心参悟琴谱中蕴含的秘密,得到大师的真传吧。·

解题思路

1.解压后是一个rar和一堆条形码

image.png|851

2.分析条形码
元数据有Comment字段

image.png|852

image.png|856

其次还有低位隐写:

image.png|295

其次发现按照(R,G,B)输出时有的图片第一行有些像素是 (254,255,255),有些是 (255,255,255)。很明显存在数据流,可以按照一种是0,另一种是1组成数据。

image.png|823

3.拿到条形码内容
这些图片是 Code128 条形码,解码后能拿到一部分形如 B105d41BE17485dES175facS 的内容。

4.拿到PNG Comment信息
查看图片元数据可以拿到另一部分内容,例如

D175a5dD
U1fda12U
H000a00H
T105b8cT
...

5.拿到第一行隐写
读取图片第一行前 72 个像素:

  • (254,255,255) 记作 1
  • 其它记作 0
  • 然后再异或翻转一次
  • 每 8 bit 组成一个字节
  • 丢掉第一个字节,后面 8 个字节转字符串
    可以得到第三部分内容,例如:
F105341F
A1fc57fA
O1fdf00O
P104a2fP
...

经过分析,正确的数据格式应该是:

  • 首尾是同一个大写字母
  • 中间是 6 位十六进制
  • 字母范围是 A-U
    使用正则^([A-U])([0-9a-f]{6})\1$去匹配即可。

内容如下:

A 1fc57f
B 105d41
C 17445d
D 175a5d
E 17485d
F 105341
G 1fd57f
H 000a00
I 1f71aa
J 0f0751
K 116b26
L 1c81bc
M 0fc356
N 001eff
O 1fdf00
P 104a2f
Q 175dcf
R 175850
S 175fac
T 105b8c
U 1fda12

总共21组,又是杂项题,自然联想到二维码
每组是 6 位十六进制:

  • 6 hex = 24 bit
  • QR version 1 的尺寸是 21 x 21

所以做法是:

  1. 每组十六进制转成 24 位二进制
  2. 每行丢掉前 3 位
  3. 保留后 21 位
  4. 21 行拼成一个 21 x 21 的黑白矩阵
  5. 渲染成二维码图片后扫码

image.png|237

image.png|666

扫码结果:4hFYm4n8l9MctSn,这就是rar的密码了

6.解压后得到的就是钢琴谱,打开这个pdf,CTRL+A发现选中了一些不可见字符

image.png

KEY+bnVtYmVyYWJvdmVsaW5l3,粘贴后发现是这个。

base64解码后面的

image.png|473

bnVtYmVyYWJvdmVsaW5l
-> number above line 3,暗示第三行的数字呗,读一下是4151515,KEY的话就是压缩包密码呗

image.png|556

所以flag出来了:ISCC{4hFYm4n8l9MctSn4151515}

Exp

from pathlib import Path
from PIL import Image
import zxingcpp
import re

# ============== 自动找图片,绝对不会找不到 ==============
current_dir = Path(__file__).parent
work = current_dir / "work"

print("📂 图片目录:", work)
print("📂 是否存在:", work.exists())

# 列出所有 barcode_*.png 文件
files = sorted(work.glob("barcode_*.png"))
print("🖼️ 找到图片数量:", len(files))

# ============== 修复正则 ==============
pat = re.compile(r"^([A-U])([0-9a-f]{6})\1$", re.I)  # 大小写都匹配

rows = {}

# ============== 开始处理 ==============
for p in files:
    print("▶️ 处理:", p.name)
    
    try:
        im = Image.open(p).convert("RGB")
    except:
        print("❌ 打开图片失败")
        continue

    # 读条形码
    try:
        barcodes = zxingcpp.read_barcodes(im)
        barcode = barcodes[0].text if barcodes else ""
    except:
        barcode = ""

    # 读注释
    comment = Image.open(p).info.get("Comment", "")

    # 读顶部隐写
    bits = []
    for x in range(72):
        try:
            pixel = im.getpixel((x, 0))
            bit = 1 if pixel == (254, 255, 255) else 0
            bit ^= 1
            bits.append(bit)
        except:
            bits.append(0)

    data = bytearray()
    for i in range(0, 72, 8):
        v = 0
        for b in bits[i:i+8]:
            v = (v << 1) | b
        data.append(v)
    top = data[1:].decode(errors="ignore")

    # 打印所有内容(关键!)
    print("  barcode:", repr(barcode))
    print("  comment:", repr(comment))
    print("  top steg:", repr(top))

    # 匹配
    for s in (barcode, comment, top):
        m = pat.match(str(s).strip())
        if m:
            print("✅ 匹配成功:", m.group(1), m.group(2))
            rows[m.group(1)] = m.group(2)
            break

# ============== 输出结果 ==============
print("\n最终结果:")
for k in sorted(rows):
    print(k, rows[k])

image.png

posted @ 2026-05-19 16:32  MillionMind  阅读(24)  评论(0)    收藏  举报