ctfshow元旦水友赛之再见童话镇WP
不懂CTF的萌新阿狸
编辑于 2024年11月14日 15:25

题目:又见童话镇

难度:中等

考点:视频隐写

 

解法

一、下载视频

本来这个题是要去b站下载视频的,不可能是觉得有为命题人拉流量的嫌疑,也可能是考虑b站上传文件的时候加了水印并进行了压缩,所以困难会稍微大一点点,或者是有的师傅不会下载b站视频,所以群主改成了百度网盘,下载无所谓,亲测都可以做,本wp所用的题目素材就是从b站下载的有损版。

二、分析题目

1.题目描述是,“光有眼睛也许还不太够,需要一点点数学”,包含了两层意思:一是这个题是要看的,不是音频隐写;二需要做图片的数学变换。

2.观看视频,很容易发现这些斑斑点点,明显是频域被破坏的特征,所以考虑二维傅里叶变换。 3.继续看提示,2s,也就是60帧一个字符。这说明了命题人会连续破坏60帧图像的频域信息,一般是放大或者缩小,但考虑到对画面整体破坏不严重(好歹还能能看)所以应该缩小了。

所以我们把每个连续60帧的视频帧做fft2然后叠加在一起理论上讲就可以看到flag了。大家可以想象一下每个像素是一个炒股的人,单看一个时间点的话有人赔有人赚,但是把时间段放长收益叠加的话,那些被薇薇安光环影响的倒霉蛋就很容易被找到了。

所以我们先做个实验,脚本如下

import cv2,numpy as np ,matplotlib.pyplot as plt

reader = cv2.VideoCapture('z:/ctf/flag.mp4') #打开视频文件

frames=np.array( [ reader.read()[1] for _ in range(60)] )#取60帧

reader.release() #关闭视频文件

tmp = np.zeros(frames[0].shape)

for frame in frames :

    fft =np.log2(np.abs(np.fft.fftshift(np.fft.fft2(frame)))+1)#做傅里叶变换,

    tmp+= fft/fft.max() #归一化之后求和

#分别先试一下三个通道的数据试试看

plt.figure(figsize=(10,5))

plt.imshow(tmp[:,:,0])

plt.figure(figsize=(10,5))

plt.imshow(tmp[:,:,1])

plt.figure(figsize=(10,5))

plt.imshow(tmp[:,:,2])

结果如下:

还是影影绰绰能看到字母c的,接下来的问题就是批量重复并采用某些方法提高这部分区域的对比度,比如我就是直接在结果后面乘方放大区别,当然效果很一般,但我很菜想不到其他好办法了,擅长图形处理的师傅请教教我。

完整脚本如下

import cv2,numpy as np ,matplotlib.pyplot as plt

reader = cv2.VideoCapture('z:/ctf/flag.avi')

ret, frame = reader.read()

frames=[]

while ret:

    frames.append(frame)

    ret, frame = reader.read()

print("FrameConut:",len(frames))

reader.release()

img = np.zeros((220,220,3),np.float64)

charrange = len(frames)//60

for w in range(charrange):

    img*=0

    for i in [ i+60*w  for i in range(60)]:

        # 这部分实在想不出怎么进一步提高清晰度了,懂的师傅请教教我

        a =np.log2(np.abs(np.fft.fftshift(np.fft.fft2(frames[i])))+1)**3

        a=a[100:,100:320,:]

        a[:,:,0]/=a[:,:,0].max()

        a[:,:,1]/=a[:,:,1].max()

        a[:,:,2]/=a[:,:,2].max()

        img+=a

    plt.figure(figsize=(10,3))

    plt.subplot(131)

    plt.imshow(img[:,:,0]/img[:,:,0].max())

    plt.subplot(132)

    plt.imshow(img[:,:,1]/img[:,:,1].max())

    plt.subplot(133)

    plt.imshow(img[:,:,2]/img[:,:,2].max())

    plt.show()

 通过这个脚本依然有些字符是看不清楚的,可以单独拿出来针对性的提高一下对比度,而且因为视频很长,flag播报了两三次,可以看看后面的情况,比如说ctfshow的f第一次根本看不清,但第二次播报的时候就很醒目了。 

最后结果是

ctfshow{610ea30b-1a2b-4a20-93dc-c32985c3a7cb}