[CTF] 睡眠排序的出题记录 w0w

# 题目列表

  • 花斯卡、火斯卡
  • 路由器 - 0、路由器 - 1
  • Chiikawa
  • void null() {}

# 花斯卡、火斯卡

本题所需要的工具 「ffmpeg」「flac」
这两个工具都可以很方便的下到
如果对音视频分析, 你通常还会用到 Audacity、REAPER、SPEK 、VLC
这些工具可以辅助你的解题和日常处理音视频的需求
但解出本题其实只需要ffmpeg即可

# FFMPEG

官网: ffmpeg.org
FFmpeg 是一款功能强大的开源多媒体处理工具。

在视频处理方面,它可以进行格式转换,将一种视频格式转换为另一种,满足不同设备和平台的需求。能对视频进行裁剪、拼接、添加字幕等操作,方便视频制作和编辑。在音频处理上,可进行音频格式转换、混音、提取音频等。它还支持流媒体处理,用于直播推流、拉流以及视频点播服务。FFmpeg 具有高效、稳定的特点,广泛应用于影视制作、在线教育、广播电视等众多领域,为多媒体内容的处理和传播提供了有力的支持。

你可能听过OBS Studio,这一软件的音视频处理部分就大量的使用了FFMPEG。

而在商业软件中,FFMPEG也是绝对的压倒性地位
诸如某某视频客户端,某某短视频,可以说几乎所有和音视频相关的软件中都有FFMPEG
我敢保证你现在的手机或电脑里就有很多个FFMPEG构建成的软件。

在Deb系列 Linux中安装 ffmpeg

1
2
apt update
apt install ffmpeg -y

macOS使用homebrew安装ffmpeg

1
brew install ffmpeg

Windows安装ffmpeg

1
ffmpeg.org 下载热心网友预编译的版本

# 分析

拿到本题,如果你有仔细听,在视频的结尾,会有一声爆音
你可以在Audacity、REAPER中明确的看到这一点

通过下载网络上公开的视频,可以发现,结尾是非常干净的

此时我们的切入点就是结尾这个爆音所隐藏的数据

我们可以使用 ffprobe 命令查看该视频文件的元信息

看看该视频所用的编码是哪些

1
ffprobe ~/Downloads/sparkle.mp4
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf61.7.100
  Duration: 00:02:01.40, start: 0.000000, bitrate: 26425 kb/s
  Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 25489 kb/s, 30 fps, 30 tbr, 15360 tbn (default)
      Metadata:
        handler_name    : Video Media Handler
        vendor_id       : [0][0][0][0]
        encoder         : AVC Coding
  Stream #0:1[0x2](und): Audio: flac (fLaC / 0x43614C66), 48000 Hz, stereo, s32 (24 bit), 931 kb/s (default)
      Metadata:
        handler_name    : SoundHandler
        vendor_id       : [0][0][0][0]

我们可以看到,该视频的容器是mp4,视频编码采用了h264, 音频编码采用了flac

如果经常接触视频创作的朋友可能就会很熟悉了

mp4容器配合分发常用的音频编码应该是 aac

就算稍微少见一点也应该是 opus,mp3

而这里却给了一个flac,很不常见,我们的入口点应该在flac编码身上

而如果你是玩Hi-Fi的朋友 (这就是音乐.raw)

或者平时关注听歌的品质

再或者你去问CHATGPT

你会得到一个信息「FLAC是一种无损压缩音频编码」

和zlib, gzip, 这些格式一样,无损压缩意味着你可以想解压一样还原数据原本的样子

而没有任何笋丝 (损失)

那么我们的思路就是

  1. 把音频没有任何笋丝的提取出来
  2. 把FLAC音频解码
  3. 看看解码后的音频结构

# 提取音频

在ffmpeg中,只要参数正确,我们可以无损的把视频数据或音频数据提取出来

1
ffmpeg -i sparkle.mp4 -vn raw.flac

这条命令的作用是 -i sparkle.mp4 (指定输入的文件)

-vn (video no, 不处理视频,我们只需要音频)

raw.flac (存储的文件名)

当你看到以下输出时,说明音轨被无损的提取了出来

1
2
3
4
Stream mapping:
  Stream #0:1 -> #0:0 (flac (native) -> flac (native))
Press [q] to stop, [?] for help
Output #0, flac, to 'raw.flac':

native to native

# 解码FLAC

使用 flac -d 命令,将FLAC解编码

1
2
3
4
5
6
7
8
9
flac -d raw.flac


flac 1.4.3
Copyright (C) 2000-2009  Josh Coalson, 2011-2023  Xiph.Org Foundation
flac comes with ABSOLUTELY NO WARRANTY.  This is free software, and you are
welcome to redistribute it under certain conditions.  Type `flac' for details.

raw.flac: done

在当前目录下,会生成一个 raw.wav

1
2
3
4
5
ffprobe raw.wav

Input #0, wav, from 'raw.wav':
  Duration: 00:02:01.40, bitrate: 2304 kb/s
  Stream #0:0: Audio: pcm_s24le ([1][0][0][0] / 0x0001), 48000 Hz, stereo, s32 (24 bit), 2304 kb/s

我们可以发现 FLAC 已经解编码为 pcm_s24le 格式了

如果学过信号与系统的朋友应该很清楚这是什么了

PCM 音频是未经压缩的原始音频数据,能够准确地还原声音的本来面貌,具有高保真度。它通过对模拟音频信号进行采样、量化和编码来实现数字化。采样频率决定了每秒采集的样本数量,较高的采样频率能捕捉更丰富的音频细节;量化位数则表示每个采样点的精度,位数越高,声音的动态范围和精度就越高。

这里已经比较Hi-Fi了 (相比DSD或者192khz来说)

还记得 之前提到的结尾有一声尖刺吗

我们这时候再对 raw.wav 分析

1
tail -b 100 raw.wav|xxd

查看 raw.wav 结尾的100字节数据,我们直接就看到FLAG了

或者你也可以用 strings 命令直接提取 wav 中的可见字符,一把拿下

这时候就有朋友要说了,不是说好了本题只需要ffmpeg吗,怎么还用了flac命令行

现在讲的是轮椅做法,位深、采样率什么的 flac程序都帮你处理好了

接下来讲如何只用ffmpeg提取该音频

使用ffprobe分别查看视频流和音频流的长度

1
2
3
4
5
6
7
8
x@mac ~ % ffprobe -v error -select_streams a:0 -show_entries stream=duration ~/Downloads/sparkle.mp4
[STREAM]
duration=121.400083
[/STREAM]
x@mac ~ % ffprobe -v error -select_streams v:0 -show_entries stream=duration ~/Downloads/sparkle.mp4
[STREAM]
duration=121.400000
[/STREAM]

可以看到音频流 a:0 比视频流 v:0 多出了0.000083s

而正常视频软件生成会裁剪对齐音视频的,明显音频被动手脚了

之前我们看到音频的元数据是 s32 (24bit)

1
2
3
4
  Stream #0:1[0x2](und): Audio: flac (fLaC / 0x43614C66), 48000 Hz, stereo, s32 (24 bit), 931 kb/s (default)
      Metadata:
        handler_name    : SoundHandler
        vendor_id       : [0][0][0][0]

“S32” 表示有符号的 32 位整数数据类型。在音频处理中,用于存储音频样本值。

“24bit” 通常指音频的量化位数为 24 位。量化位数决定了音频信号的精度和动态范围。

“S32(24bit)” 意味着音频数据以有符号 32 位整数的形式存储,并且实际有效的量化精度为 24 位。

我们查看 ffmpeg 有哪些音频解码器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
ffmpeg -decoders|grep pcm

 A....D pcm_s16be            PCM signed 16-bit big-endian
 A....D pcm_s16be_planar     PCM signed 16-bit big-endian planar
 A....D pcm_s16le            PCM signed 16-bit little-endian
 A....D pcm_s16le_planar     PCM signed 16-bit little-endian planar
 A....D pcm_s24be            PCM signed 24-bit big-endian
 A....D pcm_s24daud          PCM D-Cinema audio signed 24-bit
 A....D pcm_s24le            PCM signed 24-bit little-endian
 A....D pcm_s24le_planar     PCM signed 24-bit little-endian planar
 A....D pcm_s32be            PCM signed 32-bit big-endian
 A....D pcm_s32le            PCM signed 32-bit little-endian
 A....D pcm_s32le_planar     PCM signed 32-bit little-endian planar
 A....D pcm_s64be            PCM signed 64-bit big-endian
 A....D pcm_s64le            PCM signed 64-bit little-endian

在这里,由于音频实际有效精度为 24bit

所以我们选 pcm_s24le 解码器

如果你有一些计算机组成原理知识

就会知道字节在计算机系统里分为大端序存储和小端序存储

而在 x86 以及我们日常生活中通常用小端序,所以这里选little-endian

1
2
3
4
5
ffmpeg -i ~/Downloads/sparkle.mp4 -acodec pcm_s24le -vn r1.wav

Stream mapping:
  Stream #0:1 -> #0:0 (flac (native) -> pcm_s24le (native))
Press [q] to stop, [?] for help

这时视频中的音频就以 24bit PCM 的解码方式,存储至 r1.wav

我们strings一下就出flag了

1
2
3
4
5
6
7
8
9
x@xdeMacBook-Air ~ % strings r1.wav
RIFFv
WAVEfmt (
qLIST
INFOISFT
Lavf61.7.100
data
xujc{https://b23.tv/zPZoAcz}
x@xdeMacBook-Air ~ %

到这里,花斯卡题目就完整解出了

接下来我们开始火斯卡

拿到了一个B站的链接

https://b23.tv/zPZoAcz

我们去访问一下

发现又是一个视频,但我们会注意到访问后地址栏多了一堆东西,貌似还跳了一下

似乎是什么东西消失了

我们把浏览器的地址复制一下

然后使用curl命令访问一下原始链接,看看什么消失了

1
2
浏览器
https://www.bilibili.com/video/BV1cC411W7pH?A-Router=1&bv_session_id=2bdcd921513a531399dbb2e44047ad37902bda7a68268f2d46bd61f73f8d9a2f&plat_id=124&share_from=h5&share_plat=iphone&share_source=COPY&share_source_iphone=&share_tag=s_i&timestamp=1729249354&unique_k=zPZoAcz
1
2
CURL
https://www.bilibili.com/video/BV1cC411W7pH?A-Router=1&bv_session_id=2bdcd921513a531399dbb2e44047ad37902bda7a68268f2d46bd61f73f8d9a2f&p=78756a637be88ab1e781ab5f3565346234307d&plat_id=124&share_from=h5&share_plat=iphone&share_source=COPY&share_source_iphone=&share_tag=s_i&timestamp=1729249354&unique_k=zPZoAcz

我们会发现 有个参数 p 消失了

熟悉叔叔(Bilibili)的朋友就知道了, p 参数指的是合集视频里的索引 (p也就是Part,多Part视频)

而 p 为什么会消失呢?我们来小小逆向一下 叔叔的JavaScript 代码

通过搜索 query 等参数改变信息,我们可以定位到这一段代码

可以看标蓝的第二行

1
+n > 1 ? a.query.p = n : delete a.query.p

这是一句三目运算

判断了 n 是否 大于1,如果大于1,则url中 p 参数为 n

否则调用 delete 删除 url 中 p 参数

而 +n 是 JavaScript里的一种比较奇妙的用法

可以通过 +n 实现其他语言 parseInt 的效果

通过以上测试可以看出,当 p 的值符合数字时,+n > 1 才成立

而消失的 p 是一串字符

这也就是 为什么 p 会消失

拿到了消失的 p 参数

我们就可以提取flag了,常玩MISC的同学应该能看出,这是一段HEX

用你喜欢的工具解编码就可以拿到 火斯卡 的FLAG了

至于如何给叔叔服务器塞FLAG,那就不是本题讨论的范围了,自己研究8

1
2
x@xdeMacBook-Air ~ % echo 78756a637be88ab1e781ab5f3565346234307d|xxd -r -p
xujc{花火_5e4b40}

那么这题全通过音频就能解出来,你让我下个这么大的视频什么意思?

  • 为了欢愉

# 路由器 - 0、路由器 - 1

这两题主要考察信息搜集能力和binwalk使用

下载后会拿到一个bin文件

使用binwalk对其分析,可以看到其结构大体为三段

uboot、kernel、rootfs

再使用binwalk解包固件

1
binwalk -e firmware.bin

# 默认TFTP服务器

通过搜索引擎搜索 bootloader tftp、uboot tftp

可以搜索到几个使用tftp uboot启动内核的文章

https://openwrt.org/docs/guide-user/installation/generic.flashing.tftp

其中会有诸如以下命令

1
2
3
4
5
6
setenv ipaddr 192.168.1.1
setenv serverip 192.168.1.100
tftpboot 0x80000000 openwrt-xxx-generic-xxx-squashfs-factory.bin
erase 0x9f020000 +0x332004
cp.b 0x80000000 0x9f020000 0x332004
boot.m 0x9f020000

这里使用strings直接搜索bootloader字符串

1
2
3
4
x@xdeMacBook-Air /tmp % strings _Rel.25439.bin.extracted/200|grep serverip
serverip=192.168.1.10
serverip
Using serverip from env %s

这里的 serverip 便是默认TFTP服务器

同理搜索netmask获取设备子网掩码

1
2
3
4
5
x@xdeMacBook-Air /tmp % strings ~/binwalk/_TL-7DR7280易展Turbo版\ V1.0_1.0.16_Build\ 230718\ Rel.25439.bin.extracted/200|grep netmask
netmask=255.255.255.0
netmask
\.callbacks:callbacks,\.flags:flags,baudrate:baudrate,bootfile:bootfile,ipaddr:ipaddr,gatewayip:gatewayip,netmask:netmask,serverip:serverip,nvlan:nvlan,vlan:vlan,eth\d?addr:ethaddr,loadaddr:loadaddr,stdin:console,stdout:console,stderr:console,
eth\d?addr:ma,ipaddr:i,gatewayip:i,netmask:i,serverip:i,nvlan:i,vlan:i,dnsip:i,

答案便是 192.168.1.10.255.255.255.0

# 内部代号

这题需要一些经验

大公司研发产品会有一个叫CI/CD的东西

比如某某部门对代码进行了改动,将代码推送至仓库后,例如GitHub Action,Jenkins会自动拉取代码,生成对应版本的产物

在这里,该信息遗留在编译好的内核中

我们可以以 be7200 关键词,搜索内核中的字符串

最后可以定位到这几行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
x@xdeMacBook-Air /tmp % strings _Rel.25439.bin.extracted/602E8|grep be7200
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/init/main.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/init/initramfs.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/arch/arm64/kernel/setup.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/arch/arm64/kernel/traps.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/arch/arm64/kernel/smp.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/arch/arm64/kernel/topology.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/kernel/params.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/kernel/async.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/kernel/irq/manage.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/kernel/irq/irqdomain.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/kernel/irq/cpuhotplug.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/kernel/irq/msi.c
/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/slp-sp-target-src/qca/linux-5.4/kernel/freezer.c

这里爆出的就是该Linux内核编译源文件对应的路径

/var/lib/jenkins/workspace/soho6_tdmp_qca_be7200_release/

内部代号即为

1
soho6_tdmp_qca_be7200

# Chiikawa

解题视频 -> https://www.bilibili.com/video/BV1RN4y127ky

本题可以Misc、Web方式做

也可以Reverse、Crypto方式做

该题考察对浏览器开发工具的熟悉度

打开开发者工具,可以看到向 /flag 请求了数据,但返回的是一团乱码

但首页在该请求后却出现了形如flag的字符串,我们对JS文件进行全局搜索

可以定位到下面的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    const [e,t] = x.useState(!0)
      , [n,r] = x.useState(null)
      , [o,i] = x.useState("是什么呢");
    return x.useEffect( () => {
        e && (fetch("/flag").then(s => s.arrayBuffer()).then(s => {
            const a = new Uint8Array(s);
            r(a)
        }
        ).catch(s => console.error("Error fetching data:", s)),
        t(!1))
    }
    , []),
    x.useEffect( () => {
        if (n) {
            const s = new Uint8Array([162, 62, 141, 197, 128, 113, 130, 47, 27, 147, 187, 37, 159, 48, 46, 136])
              , a = new Uint8Array(n.length);
            for (let d = 0; d < n.length; d++)
                a[d] = n[d] ^ s[d % s.length];
            let l = new TextDecoder().decode(a)
              , u = l.indexOf("{")
              , c = l.indexOf("}");
            if (u !== -1 && c !== -1) {
                let d = l.substring(u + 1, c)
                  , p = l.replace(d, "乌拉呀哈~呀哈乌拉~");
                i(p)
            }
        }
    }

可以看出使用了React的useState和useEffect

flag的初始值为 “是什么呢”

当页面加载时获取 /flag 内容

对内容进行一系列异或,最后将 {} 括号内内容替换为 “乌拉呀哈~呀哈乌拉~”

(这个时候Reverse/Crypto手已经打开Python/CyberChef了)

Reverse/Crypto做法就是把 /flag 保存下来

用逆向出来的 Key 异或解密

Misc/Web的做法就是在这行打几个断点,刷新页面就可以直接出

1
2
3
            let l = new TextDecoder().decode(a)
              , u = l.indexOf("{")
              , c = l.indexOf("}");

# void null()

睡眠排序是一种基于时间的排序算法

解压后可以看到一堆乱七八糟的 .null 文件

但时间不同

这里按时间排序,将文件名拼接到一块,就可以获得 FLAG

Windows可以编写脚本

Linux直接一行命令梭了

1
ls -tr|cut -d "." -f 1|xxd -r -p > 0.raw

ls -tr

是按时间升序排列目录下的所有文件

cut -d “.” -f 1

将获取到的文件名通过 . 分割,获取第一个值,就是把文件名的 .null 去除

xxd -r -p > 0.raw

将获取到的HEX数据通过xxd还原成文件

看一下文件类型

1
2
3
x@xdeMacBook-Air s1eep % file 0.raw
0.raw: RIFF (little-endian) data, Web/P image
x@xdeMacBook-Air s1eep %

Web/P image

改名为 0.webp ,用浏览器打开就直接看到flag