MISC
week1 decompress
题目描述:
1 | 正在失传的技艺之压缩包解压 |
下载附件
查看passwd_regex_hint.txt
根据正则^([a-z]){3}\d[a-z]$,得到这是一个数字+字母的五位密码组合,直接爆破密码
解压压缩包
最后flag为flag{U_R_th3_ma5ter_0f_dec0mpress}
week1 pleasingMusic
题目描述:
1 | 一首歌可以好听到正反都好听(以 flag{} 形式提交,所有英文字母均为小写) |
下载附件
查看音频文件
听到这边很熟悉,摩斯密码
手动提取
. ..- — .-.- -.–.. . … .-. — – -.–.. ..– .
摩斯解码
提示正反音乐都好听,将摩斯编码逆序再解码
最后flag为flag{ez_morse_code}
week1 WhereIsFlag
题目描述:
1 | 才……才不会告诉你我把 flag 藏在哪里了! |
开启环境,找到flag位置
最后flag为flag{77c1140f-79e3-4eca-af3f-0c006c49d4f8}
week1 Labyrinth
题目描述:
1 | 听好了:9 月 23 日,NewStar 2024 |
下载附件
提示LSB加密,直接拿stegsolve秒
扫描二维码
最后flag为flag{e33bb7a1-ac94-4d15-8ff7-fd8c88547b43}
week1 兑换码
题目描述:
1 | 领取 NewStar 前瞻兑换码,明天中午 12 点就失效喽!就在图片下面。什么,你没有看到?原来是 png 的下面啊,那没事了。 |
下载附件
宽高一把梭
最后flag为flag{La_vaguelette}
week2 wireshark_checkin
题目描述:
1 | un 搭建了一个简单的 http 服务器,但是不小心把重要文件删除了,只剩下访问这些文件时的流量,你能帮他找到吗 |
下载附件
tcp追踪流找到flag
最后flag为flag{ez_traffic_analyze_isn’t_it}
week2 wireshark_secret
题目描述:
1 | un 偷看涩图,被抓到流量了 |
下载附件
导出http对象
保存查看文件
最后flag为flag{you_are_gooddddd}
week2 热心助人的小明同学
题目描述:
1 | 小明的邻居小红忘记了电脑的登录密码,好像设置的还挺复杂的,现在小红手里只有一个内存镜像(为什么她会有这个?),小明为了帮助邻居就找到了精通电脑的你…… |
下载附件
查看提示
Volatility 2查看内存镜像版本
lsadump
最后flag为flag{ZDFyVDlfdTNlUl9wNHNTdzByRF9IQUNLRVIh}
week2 用溯流仪见证伏特台风
题目描述:
1 | 漂亮国也干了。照着 2024 年 7 月 8 日央视新闻的方法来看看隐匿在图片下的东西吧. |
访问链接
根据视频,我们获得以下信息:
- 所需报告:The Rise of Dark Power…
- 对应版本:最初 4 月 15 日版本
- 现状:所需信息已经被篡改
搜索报告名称
打开pdf链接
可以看到我们需要的 PDF 文件,但是视频中又提到报告内容已经被篡改
使用网站时光机—— wayback machine.
输入官网链接,启动溯流仪,正好有 4 月 15 日的版本
下载文件,移开封底图片,拿到 Domain 框里的东西,然后 MD5
最后flag为flag{6c3ea51b6f9d4f5e}
week2 你也玩原神吗
题目描述:
1 | 如果你玩原神,那么你看得懂这些提瓦特文字吗?请把得到的内容用 flag{} 包裹 |
下载附件
gif分离帧
原神提瓦特文字对照表
左下角文字解密后是 doyouknowfence
提示是「栅栏密码」,右下角文字就是密文
用栅栏密码解密工具解出来,得到 flag
week2 字里行间的秘密
题目描述:
1 | 我横竖睡不着,仔细看了半夜,才从字缝里看出字来 |
下载附件
零宽解密
解压docx文件
全选修改字体颜色
最后flag为flag{you_h4ve_4nyth1n9}
week2 Herta’s Study
题目描述:
1 | 黑塔女士在进行一项新的研究,她截获了银狼作为新手黑客时的渗透流量,不过这小家伙喜欢整点新花样。「在战场上,如果我牺牲了,我不希望我的代码被敌人轻易使用」 |
下载附件
tcp追踪流
可以搜索一下 create_funtion()
函数,解除混淆后得到加密代码
1 | $ns = base64_encode($ns); |
就是 Base64 后把奇数位 ROT13
解码反过来就行(第38条,f.txt
里的是真 flag,另一个是假 flag)
1 | <?php |
php运行得到
最后flag为flag{sH3_i4_S0_6eAut1fuL.}
week3 BGM坏了吗?
题目描述:
1 | 刚想听一篇推文,但是 bgm 吵死了,我忍了又忍,摈除杂音仔细听,up 难道在拨号吗?(以 flag{} 形式提交) |
下载附件
用 Audacity 打开音频很容易发现结尾处右声道有信息,而左声道是噪音
根据题目描述是拨号音,但是直接放解不出来,需要删掉噪音
选择 分离立体音到单声道 » 关闭左声道 » 导出
按键音(即DTMF)解密
最后flag为flag{2024093020241103}
week3 ez_jail
题目描述:
1 | no {} no cpp? |
本题的原意是只考查 {}
在 C++ 里的(宏)替代运算符这个知识
只要关键词用得对,网上一搜就能搜到,但是被出题人执行坏了,测题时出现了一堆非预期。考虑了一下各个知识点的难度,感觉非预期的难度和预期解相差不大,就索性变成了一道半开放性的题目
我们观察代码的 check
函数
1 | def cpp_code_checker(code): |
这段代码看似过滤了 #include
#define
等,但不知道同学们有没有意识到 #
后加空格就能绕过这里,也就是说可以通过宏定义来做到编译前预处理
找到 C++ 的替代运算符的相关资料,然后使用 <% %>
替换{}
1 | void user_code() <% |
base编码
输入payload得到flag
week3 AmazingGame
题目描述:
1 | 出题人们都好狠啊,题做累了不如来玩玩游戏吧,还有就是,安卓软件私有数据会放在哪?(游戏至少通过一个关卡才会有存档) |
下载附件
使用模拟器打开
打开游戏画面,是个赛车游戏,类似qq飞车
通关很难,尝试使用APK反编译软件
发现base编码,解码得到flag
week3 OSINT-MASTER
题目描述:
1 | 我重生了,这一世我是 osint 带师,我一定要找到之前坐过的那架航班! |
下载附件
给了图片,先看图片的 EXIF 信息,拍摄时间是 2024-8-18 14:30,照片中可以在机翼上看到一个标号 B-2419
直接在 flightaware 中搜索这个标号,应该是飞机的注册号
可以搜到这是一架东航的飞机
在下面可以找到历史航班
可以看到,在 2024 年 8 月 18 日这四架航班中,只有红框中这架符合 14:30 在飞行中,点进去看一下详细信息
找到航班号 MU5156
下面根据照片拍摄时间和航行轨迹来找照片拍摄时飞机经过的地级市,我这里使用航班管家,有了航班号直接搜
14:30 在 14:13 和 14:51 中间偏左的位置!
放大来看,此时飞机大致经过邹城市
邹城市属于济宁市,济宁市是地级市
最后flag为flag{MU5156_济宁市}
week4 Alt
题目描述:
1 | 你真的没在打数字? |
1 | tshark -r keyboard.pcapng -T fields -e usbhid.data > usbdata.txt |
然后得到的数据里有一些空行,可以用文本编辑器批量替换掉。我这里截最前面的一段作为示例进行分析:
1 | 0400000000000000 |
根据中文互联网上能容易找到的、不用充会员的键盘流量分析相关资料可知,第一字节代表控制键,第二字节保留为 0x00
,第三到八字节是我们敲击的键。
有些同学反映,网上的脚本里找不到 0x59
0x62
等等键码对应的按键,原因上面讲过了。其实多读几篇国内的相关文章就会发现它们经常引用一篇名为 Universal Serial Bus (USB) 的文章,把这个文件下载下来,第 55 页就有对应的对照表。
很多同学分析到这里,都会忽略第一字节的 0x04
,根据题目名和网上的资料可以知道是按着 Alt 键。那么整个击键流程就比较清晰了:保持 Alt 键的按下状态,按下几个数字键,然后松开 Alt。
直接搜索「Alt 加数字键」,就能知道这是在按Unicode码值输入字符,写个脚本稍微自动化一下或者直接一个个手动看过去,很容易分析出来上面截取分析的这段流量就是在输入 fla
这三个字符,以此类推,就能得到整个 flag.
还有一些同学对流量里的 backspace 退格键有所疑惑,认为是删除了前一个数字或者认为是删除了整个字符。很遗憾两者都不是。
注意题目描述中指明了,flag 含有非 ASCII 字符且语义较通顺。如果退格键是删除了 Alt 加数字键打出来的整个字符的话,得到的 flag 就不含有非 ASCII 字符。 如果退格键是删除了上一个输入的数字的话,得到的 flag 的非 ASCII 部分没有任何语义。反而是忽略了退格键,能得到正确的结果,比如说第一段非 ASCII 字符是键盘流量。
因为出题人在出题时是用的 Windows 11 自带记的事本,如果要让 Alt 加数字的结果是中文字符的话,经测试需要按下退格键或者是 Enter 键,也说明 Alt 加数字键输入非 ASCII 字符这个特性在不同软件里不一定能完美复现。除了手动复现按下 Alt 键加数字键这个流程以外,也可以直接使用 Python 的 chr
函数进行计算,就能获得十进制码值对应的字符。
week4 扫码领取 flag
题目描述:
1 | Tip: 什么?这可不是图寻,这是货真价实的 hint |
下载附件
解压得到很多压缩包文件,使用winhex查看,发现是8950开头,都将文件修改为后缀名.png
使用b神工具爆破crc宽高
查看爆破结果
ps拼接二维码
保存后用Aztec扫描得到flag
week4 擅长音游的小明同学
题目描述:
1 | 小明是资深的音游玩家,有一天他游玩某知名街机音游后顺利使 rating 上 w5 |
下载附件
使用 FTK imager + 虚拟机进行仿真找出 flag.
首先我们打开 FTK imager 加载拿到的镜像,我们看到所有分区都加载完毕,发现磁盘名称有提示,说明系统是 Windows7 x64.
取证一般都是考虑看桌面有没有什么东西,路径在 C:\Users\[用户名]\AppData\Roaming\Microsoft\Windows\Themes
或 C:\Windows\Web\Wallpaper\Windows
可以看到十分抽象的壁纸,根本没有能明显看见的东西,瞅一眼桌面文件夹,只有一大坨文件,也没有什么直观能看见的,内容倒是有:
要开始了哟~.txt内容
1 | 今天舞萌彩框了好开心啊o(* ̄▽ ̄*)ブ |
真相.txt内容
1 | 真相会不经意间流入日常的点点滴滴…… |
找到一张图片
使用 010 Editor 进行查看的话,可以发现除了正常的照片内容,还有意义不明的文字和一个压缩包(实际上使用 binwalk 梭一下也很正常)
文字内容:
1 | ?????_DIMENSION_1200x800 |
压缩包可以使用 binwalk 提取并解压:
1 | 听好了听好了听好了听好了听好了听好了听好了: |
进行仿真
步骤如下:
1.直接将镜像映射成物理磁盘,方便虚拟机直接使用启动:
2.一定要选择挂载方式位 Writable 不然会因为无法写入而报错,点击 Mount 挂载,下面出现挂载结果表示成功:
3.挂载成功后我们打开虚拟机,这里使用 Vmware,由于使用物理硬盘需要管理员权限,所以我们需要使用管理员启动 Vmware,右击快捷方式,打开文件位置,再次右击选择兼容性,勾选以管理员权限启动:
启动之后新建虚拟机就可以了。
选择 Windows7 x64 配置,一路全选推荐,其中需要注意的如下:
开启虚拟机
当你进入系统后就不得不想起前面的提示:
1 | 但发掘真相的道路被加诸混沌的历练 |
Flag 其实是拿桌面图标堆的,要是不是 1200×800 的分辨率启动就会被重新排列,一旦被重新排列,图标就再也回不去了
需要切换到 Guest 调整窗口到相应分辨率再切换到 Admin 账号,就看到了:
最终flag为flag{wowgoodfzforensics}
week4 擅长加密的小明同学
题目描述:
1 | 小明在学习中对各类文件加密的方式起了浓厚的兴趣,并把自己珍贵资料和 Flag 进行了套娃式加密。然而,他却在某天的凌晨三点选择了重装系统,本来他就记不住自己的密码,还丢失了备份密钥…… |
下载附件
双击 vhd 发现有 BitLocker,BitLocker 怎么解?理论上没有密码和恢复密钥还真解不开,也没有软件能直接破解,但是 dump 内存镜像的机器是成功解密 BitLocker 的,内存中会残留着 BitLocker 的密钥,而借助内存镜像来解密 BitLocker 的软件确实是有的,他是 Elcomsoft Forensic Disk Decryptor
,基本上搜到的博客都用它,使用以上软件,按图示步骤解密:
选择第一项「解密或挂载硬盘」:
由于题目给了 vhd 文件,所以选使用镜像文件的第二项:
数据来源选择被加密的镜像,而内存转储文件就选题目给的 raw 文件:
7z 在密码复杂的情况下基本不可能被解出密码,根据提示,我们得知小明曾经使用画图软件把密码写了下来,我们可以借助内存镜像看到程序运行的样子找回密码。
在这里我们借助 Volatility 和 GIMP 的力量解决问题:
首先按照上一道取证,分析镜像后查看进程:
1 | vol.py -f chal.raw --profile=Win7SP1x64 pslist |
发现 mspaint.exe(画图进程),我们提取出来,使用 memdump:
1 | vol.py -f chal.raw --profile=Win7SP1x64 memdump -p 3744 -D ./ |
提取出的程序对应的 dmp 文件是含有程序运行时的显示内容的,我们只需要寻找运行时图像在 dmp 文件中的位置,然后想办法让他显示出来,这里我们就可以借助 GIMP 通过调整偏移,高,宽的方式达到上面的目的。
在此之前,记得改后缀为 .data
,拉入 GIMP 打开,可以看到:
调节位移、宽度、高度来显现程序运行时显示的内容
压缩包密码:rxnifbeiyomezpplugho
解压得到 flag
week5 PlzLoveMe
题目描述:
1 | 公元 4202 年,一位劳累的工程师正在自己的实验室研究一种古老的设备,当他把存放在时间胶囊里的设备链接上老式物理计算机串口,当设备上古老怀旧的 LED 散发出蓝色微光,他感觉到身后有何种实体靠近,转头的瞬间晕了过去。他醒来了,听见了设备上逐渐变小的歌声,还有被点亮的 LCD 用血红的奇怪符号一行一行书写着某种难以识别的语言,他觉得自己好像失去了什么。歌声暂停,他立刻将实验室传感设备记录下的信息取出分析,只得到采样率 16k 的声音数据。是谁想传达什么吗?你能否帮他解开谜团。(flag 为有意义的全为字母的字符串,请包上 flag{} 提交) |
音频采样数据一般就是 PCM,根据所给采样率,我们可以直接找线上网站播放
直接找个音乐软件,听歌识曲可以知道是 world.execute(me);
(这是歌曲名)
播放到 03:01 的歌词如下
对照 LCD 图片
仔细查看可以发现 LCD 上显示的是歌词(LCD 图片的第一行内容为 Question me
,后面也有明显的 LO-O-OVE
)
其实 LCD 上的符号是特殊字体,感兴趣的同学可以看看 LVDC-Secret-Passage 字体。
对照歌词可以得到符号的对应关系
歌词倒数第四排是 flag:
,后三排仔细对照,可以知道是:
fhwdLd
mnwdOnV
mnwdOnV
由题目可知 flag 是有意义的全英文字符串,我们还剩一个 AXF 文件没有用到
这是包含 ARM 符号信息的二进制文件,可以用 IDA 打开
题目里提到设备连上了电脑串口(其实一般是 USB 来串口通信,这里为了降低难度直接说明是串口了)
使用 file
命令:file SD_MP3_RC.axf
,输出:
1 | SD_MP3_RC.axf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped |
IDA32 函数列表搜索 UART
看到一个 RxCpltCallback
,搜一下是串口回调,用于接收数据后的处理,按 F5 查看伪代码
1 | void __fastcall HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) |
只有个异或 0x1
,CyberChef 解密后包上 flag{}
上交
week5 zipmaster
题目描述:
1 | 我又重生了!这一世我是压缩包带师,发誓要解开所有压缩包! |
下载附件
发现有四个长度仅为 3 字节大小一样的文件,使用 CRC 爆破来得到其中的内容
CRC 值在压缩包中都可以看到,直接上脚本进行爆破
1 | import binascii |
得到结果
1 | -------------Start Crack CRC------------- |
使用 this_is_key!
作为密码来解压 3077.zip
,得到 114514.zip
,发现无密码,可以直接解压
得到 0721.zip
和 hint.txt
1 | hint.txt |
使用 bkcrack 来进行明文攻击。这里要注意 -c
参数后面写的是破解文件中的明文文件的绝对路径,从压缩包一层开始,我们可以看到在压缩包中还有一层名为 0721
的文件夹,所以这里路径要写 0721/hint.txt
爆破出密钥之后我们直接构造一个弱密码加密的压缩包,密码是 123456
,其中的内容和原来的 0721.zip
完全一样
解压 0721.zip
之后得到一个 flag.zip
,这个压缩包是个压缩包炸弹,具体原理可以自行搜索,这里我们直接使用 010 Editor 打开看一下它的文件结构,发现很多 Base64 字符串
提取出来解密,得到一个新的压缩包
同样将 16 进制内容放到 010 Editor 中另存为一个新的压缩包,在末尾找到 hint
这里提到看不到密码,后面给的 f4tj4oGMRuI=
其实是密码的 Base64 加密后的字符串,但是解密之后发现密码是不可见字符(密码就是它),无法直接使用它来解压压缩包,写脚本来进行解压
1 | import base64 |
解压后得到一个 Z1 文件,使用记事本打开得到 flag
最后flag为flag{ecebbd61-2bb9-4eda-b4ca-f24b895be2e3}
week5 pyjail
题目附件:
1 | 咦?居然有一个神秘的 Python 程序跳出来,声称是无敌的「小小魔法牢笼」!它还傲娇地说,谁都别想逃出去!你作为超级聪明的魔法少女,怎么可能被困住嘛!不过,牢笼里面有好多限制,很多魔法(比如那些调用系统的咒语)都被封印了呢。 |
match case
是 Python 3.10 才有的语法,可以用来获取一个对象的属性
1 | class Dog: |
str() 是一个空字符串对象,下面这部分等价于 bfc = ‘’.join([chr(37),chr(99),]),也就是 bfc=%c
1 | match str(): |
后面拿到了 %c
,就可以使用 %
构造字符串
完整的 EXP 如下:
1 | import socket,time |
week5 I wanna be a Rust Master
题目描述:
1 | 你是 Rust 糕手吗?!让我看看你的代码! |
其实禁了不少东西,看附件给的 server 源码,可以看到检测大致分成两个部分,一个明文检测,一个是基于 syn、quote 库检测 TokenStream(指令流)。
先说检测 TokenStream 这块,比如下面这个,就是在检测是否有字面量(字面量是用于表达源代码中一个固定值,比如 123
, "abc"
, true
, 114.514
)。
1 | #[derive(Default)] |
然而由于 Rust 的宏在解析的时候,都是有一套自定义的解析逻辑,而 syn 库本身并不能直接获取宏定义的解析逻辑,所以,如果有尝试去看过 syn 的源码,就会发现在处理 macro 的时候,都是直接返回 TokenStream,也就是没有被解析的原始指令流(就是因为前面说过 syn 本身不知道一个宏是怎么展开的)。
换言之,如果使用者没有人为去解析这些指令流,那么 syn 本身就不会检测宏里面有啥指令,这样就可以把一些恶意的代码塞进宏里面,比如:
1 | vec![println!("Hello")] |
这个毫无疑问是有一个字面量 "Hello"
的,但是由于在宏里面,所以 syn 无法检测。
对于这道题而言,使用 syn 进行检测的其他逻辑,比如我有检测 std
、unsafe
等等,其实都可以利用这一点来绕过。
但是很可惜的,明文匹配的检测,也就是在源码中类似这段的代码:
1 | if input.contains("std") { |
所以 std
、unsafe
这些还是很难能够使用。
那么如果不用标准库的东西,还能怎么读取文件呢?
其实 Rust 本身自带了很多有趣的宏,对于这道题,可以使用 include_str!
或者 include_bytes!
.
以 include_str!
为例,它会在编译期,读取指定路径的文件(如果路径不存在,无法通过编译),然后会把读出来的内容作为字符串进行编译。
比如有一个 a
文件,里面内容是 Hello
,那么 println!("{}", include_str!("a"))
就完全等价于 println!("{}", "Hello")
.
所以我们就可以通过 include_str!("/flag")
来直接读取 flag 文件(在编译期)!
但是,令人难过的是,题目还检测了代码中是否包含 "flag"
这个字符串,可能大家的第一反应是套一层变量去绕过,类似这样:
1 | let a = "/fl".to_string() + "ag"; |
但是这是不行的!
如果运行,应该会看到 error: argument must be a string literal
这样的报错,因为 include_str!
这个宏解析的时候需要接受字符串字面量!
那么这该怎么办呢?别慌,还有办法!这就不得不提 concat!
这个宏了(大家可以多翻翻标准库里自带的那些宏,有很多很有意思的宏),concat!
可以编译期拼接字符串字面量(是的!concat!
也要求提供的值是字面量),所以就可以使用这个来绕过本题的检测读取 flag 了。
1 | let f = include_str!(concat!("/fl", "ag")); |
接下来就是另一个问题了:怎么样输出 flag 呢?
要知道,在 Rust 中,输出都是依赖于 println!
dbg!
panic!
之类的宏,而这些宏本质是对 std::io
中的对象进行的封装,所以想要输出,要么能够使用这些封装好的宏(但是都被我 ban 啦,哇哈哈哈哈),要么能够访问到 std::io
这个模块中的东西(也被我 ban 了,嘻嘻)。
那么还有什么办法呢?
其实有个很常见的思路:使用报错来带出输出!
比如随便构造一个整数溢出?比如数组越界?比如对 None
调用 .unwrap()
?比如对 Ok
对象调用 .unwrap_err()
?等等,非常多的报错,但是我们要让报错信息能够被控制!毕竟我们需要输出我们想要输出的内容。这里我们就很容易想到使用 Option
或者 Result
,下面我就随便给几个例子,大家可以参考一下:
1 | let a: Option<i32> = None; |
综合上述的思路,就可以整理出下面这段 payload 啦!
1 | fn main() { |