2025xyctf wp
一、战队信息
战队名称:Initialization
战队排名:12
二、解题情况

三、解题过程
web-Signin
直接看代码
1 | # -*- encoding: utf-8 -*- |
提示了一个secret文件,/download路由能读取文件内容,但是有一定过滤,/secret路由看上去是生成cookie和检查cookie用的,/根目录
./当前目录
../上一级目录,这里我们穿插使用./.././../secret.txt能够绕过waf,读取到secret的内容
Hell0_H@cker_Y0u_A3r_Sm@r7
然后看/secret路由,我们通过模块bottle看看get_cookie和set_cookie的内容
1 | def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **options): |
能够看到set_cookie对name和value进行了pickle序列化,
1 | def get_cookie(self, key, default=None, secret=None, digestmod=hashlib.sha256): |
而get_cookie对数据进行了pickle反序列化,我们打pickle反序列化的__reduce__
重写漏洞https://blog.51cto.com/u_12205/8710727参考这篇文章

1 | # -*- encoding: utf-8 -*- |
在本地开一个用来生成cookie,将命令内容输出到app.py用/download界面查看


直接读就行

flag{We1c0me_t0_XYCTF_2o25!The_secret_1s_L@men7XU_L0v3_u!}
web-ezsql(手动滑稽)
进入为一个登录界面,由题目知为sql,进行测试,在username处注入,测试得到过滤了空格,union,逗号,于是放弃常规注入,但是查询失败会回显账号或密码错误用的<strong>
表情包裹,选择布尔盲注,使用%09绕过空格,substr(database()%09from%091%09for%091)=“x”
用于判断,下面是爆破脚本
1 | import requests |
1 | import requests |
最终得到的信息有:
库名testdb 表 double_check,user 字段secret,username,password 内容password:zhonghengyisheng username:yudeyoushang secret:dtfrtkcc0czkoua9S
我们拿着这个信息登录
进入到一个无回显的命令执行页面,测试发现过滤了空格,用${IFS}\(9绕过,用>将内容写入新页面`ls\){IFS}$9/>1.txt`
cat${IFS}$9/f*>2.txt
XYCTF{ea54fff6-d3db-4055-95b4-6b13330d5b02}
web-fate
直接看附件,定义了很多函数和路由,我这里的思路是先看看怎么走到db_search(),既然有过滤,说明需要过去,if flask.request.remote_addr == '127.0.0.1':
需要我们以本地IP访问才能够进入/1337路由,这里又看向/proxy路由,
1 |
|
能够get传入url拼接在http://lamentxu.top的后面,有黑名单检测,内容为所有字母,并且过滤了点号,这里考虑SSRF,使用@能重定向为我们传入的url,我们想要ip为本地(127.0.0.1),这里使用连续十进制绕过,也就是2130706433,端口为8080

看到这个返回结果也是确定自己访问成功了,下一步是传入0的参数,值为abcdefghi,但是传入的url不能有字母,我们考虑到SSRF会进行1次url解码,我们用bp传会自动url解码1次,固2次url编码即可绕过,

到了传json数据的那一步,我们要把name为键名的数据进行json序列化再转化为二进制传入,这里我写了一个脚本便于实现(脚本里已经写了payload,下文解释)
1 | import json |
看到源代码用len(name)限制了长度,不让我们直接查询LAMENTXU的FATE值(这些fate值在init_db.py里),但是如果我们构造{"name":{"123":"123"}}字典嵌套字典,会发现len的计数只有1,但是我们传入的LAMENTXU是无法被使用的,因为具有其他字符以及大括号,那我们考虑闭合引号和右括号,由于我们name的值是一个字典,所有对引号和右括号的过滤是无效的,
{"name":{"))))))) or 1=1--+":"1"}}
我用这个字符串进行测试,传入后发现成功执行了SQL语句,用order
by发现列数为1,由此得到payload为{"name":{"))))))) union select group_concat(FATE) FROM FATETABLE--+":"1"}}
/proxy?url=@2130706433:8080/1337?0=%61%62%63%64%65%66%67%68%69&1=01111011001000100110111001100001011011010110010100100010001110100010000001111011001000100010100100101001001010010010100100101001001010010010100100100000011101010110111001101001011011110110111000100000011100110110010101101100011001010110001101110100001000000110011101110010011011110111010101110000010111110110001101101111011011100110001101100001011101000010100001000110010000010101010001000101001010010010000001000110010100100100111101001101001000000100011001000001010101000100010101010100010000010100001001001100010001010010110100101101001010110010001000111010001000000010001000110001001000100111110101111101
(二进制的序列号字符串,图片为url编码后)
flag{Do4t_bElIevE_in_FatE_Y1s_Y0u_2_a_Js0n_ge1nus!}
web-Now you see me 1
审计给的代码,一堆helloworld,但是其中藏了真代码,是base64编码的,解码下来看看
1 | import flask |
可以看到过滤的内容是非常多的,flask,有渲染函数flask.render_template_string('')
,考虑SSTI,这里把request能用的绕过禁了很多,get
post
cookie等都不行了,但是仍然有可用的,request.mimetype能获取Content-Type的内容,request.authorization.type和request.authorization.token能获取Authorization的内容,中间用空格隔开,request.origin能获取Origin的内容,request.referrer能获取Referrer的内容,这样我们可以去构造注入的payload,考虑寻找eval模块(os被删掉了)重新导入os,经过测试,直接传入?My_ins1de_w0r1d=Follow-your-heart-{%print(config)%}
就能成功实现SSTI,所以就在里面构造我们想要的结构,这里对request.mimetype传入abcdefghijklmnopqrstuvwxyz_方便我们提取字符,(request.mimetype)|attr(request.origin)()
,再对request.origin传入__getitem__
括号里填入0即可提取字母a,于是有{'a':0,'b':1,'c':2,'d':3,'e':4,'f':5,'g':6,'h':7,'i':8,'j':9,'k':10,'l':11,'m':12,'n':13,'o':14,'p':15,'q':16,'r':17,'s':18,'t':19,'u':20,'v':21,'w':22,'x':23,'y':24,'z':25,'_':26}
字符之间用~连接,于是我们得到
1 | __class__:(request.mimetype)|attr(request.origin)(26)~(request.mimetype)|attr(request.origin)(26)~(request.mimetype)|attr(request.origin)(2)~(request.mimetype)|attr(request.origin)(11)~(request.mimetype)|attr(request.origin)(0)~(request.mimetype)|attr(request.origin)(18)~(request.mimetype)|attr(request.origin)(18)~(request.mimetype)|attr(request.origin)(26)~(request.mimetype)|attr(request.origin)(26) |
并且我们利用request.authorization.type传入__builtins__
写脚本查找具有eval函数的模块
1 | import requests |

我这里随便选了一个118,然后继续构造eval以及执行内容,用request.authorization.token传入eval,用request.referrer传入内容__import__('os').popen('ls /').read()
这样可以读取到根目录的信息

但是我cat
/f*却报错了,原来是文件太大没法读取,于是想利用服务器开一个端口用来让我下载文件,命令为cd / && python3 -m http.server 8088
1 | import requests |

下载flag_h3r3

得到flag为flag{N0w_y0u_sEEEEEEEEEEEEEEE_m3!!!!!!}
web-ez_puzzle
1.前端这种题找一圈也就只有这个js看起来很不一般的样子(真乱啊),那些解混淆工具也没成功。自己做的时候没找到有用的,还以为要从逻辑里找到恢复解压缩前面那坨c的类base64的东西。不过队里的其它师傅通过搜索if搜索审阅,硬是找到了这里可疑的变量yw4。后来想了想根据这个机制,搜索<或者>也可行
2.控制台里面可以看到其值为2000,猜测就是限制2秒
3.把限制时间改长,改成9999999999999999999999999,慢慢玩就是了
flag值:flag{Y0u__aRe_a_mAsteR_of_PUzZL!!@!!~!}
pwn-Ret2libc's Revenge
这题我应该想复杂了,说是签到题,搞得非常麻烦,感觉是做过来偏难的题。首先最烦的是这个init函数设置标准输出是全缓冲,这直接导致我需要占满整个缓冲区才能在终端看到回显。这里我本地利用程序自带的输出先走53次就能使缓冲区临近溢出,而远程服务器需要214次才能快占满缓冲区。

主要的漏洞函数就是一个无限制的栈溢出,遇到换行符结束。需要注意的是,这里是一个字节一个字节读取,栈上有变量存当前读取下标,覆盖到该下标后就可以跳跃到返回地址进行覆盖。

麻烦的是如何泄露libc基址,这个程序里没有控rdi的gadget,而且动态调试时看到feof之后的寄存器状态也没有可利用的内容。我们需要想办法控制rdi为libc相关地址然后call puts来泄露libc基址。这里我采用的是下面两个gadget的组合。首先第一次溢出先覆盖rbp,然后返回到revenge函数,这样可以让我们读取内容到可控的地址,这里我选择的是覆盖rbp为0x404900,然后就能往可控的地址处读入函数的got表地址或者在bss段的FILE结构体地址。这样之后就可以通过处于0x401274的gadget来控制eax,之后再利用init函数中setvbuf传参控制rdi,之后就能回到puts函数泄露libc地址。这里能成功地关键在于setvbuf函数非法调用结束后不会改变rdi的内容,这里的成功率为50%,调用时会把rdi中相关的内容与0x8000进行按位与提取位,可能会进入别的分支导致程序直接崩溃。
有了libc基址后记得返回时不要回到main函数开头,不知道为什么再次调用init函数后,之后输入就会出现问题。


1 | from pwn import * |

XYCTF{181826fe-77ab-4285-9c46-1c46fdbb93a2}
pwn-web苦手
一个web pwn题目,逻辑比较简单,但其实有点不像是pwn题。首先一开始会解析三个get参数。

然后看这里的主要逻辑,这里的hash函数是通过具体的加密特征得出的,问了ai可能是PBKDF2-HMAC-SHA1算法,然后有个函数会把加密后的内容保存到dk.dat文件,这个文件我们实际可以直接访问对应路由下载下来查看加密结果,这里的passwd_re实际re代表register,不能小于63字节。
后面的passwd_lo就相当于login了,会把读入的内容同样加密后和之前注册的进行比较,不能大于63字节。成功后就能够传出filename参数,读取filename.dat文件内容了。


这里一开始我想的是进行爆破相同的hash值,但后来发现比较用的是strncmp,可以被截断,那我们直接爆破hash后首位为的长短两个密码即可。
1 | import hashlib |

然后可以去读文件,但是服务器上flag.dat里是fake_flag,这里注意到snprintf(file, 0x10uLL, "/%s.dat", filename);
有长度限制,可以通过加长filename来最后截断.dat后缀,最终读../../..//flag
即可。

XYCTF{837f56a3-160b-4fad-94ac-037166e2635d}
pwn-bot
一个经典的protobuf题,首先去手动逆proto结构体,这题里有两个。




逆出来的结果如下,protobuf相关内容可见我博客。
1 | syntax = "proto2"; |
为了美观起见,这里我们在IDA中设置这两个结构体,然后把对应类型的变量改为我们自定义的结构体类型,好看一点。


主要功能一共有3个,其中我们要用到的只有action 1和2,0只是把我们输入的内容打印一遍(这里说不定可以劫持puts_got为system,但我没试)。其中action 1中会复制内容到我们指定id的chunk中,这里我们要先看一下这个堆块申请的逻辑。实际上申请的这个堆块分为管理块和数据块。管理块data域第一个是返回地址,而data域第二个内容就是指向数据块的data域。


由于在实际交互之前也alloc了2次,这里我们可以看前两堆块。

这里我们的1号功能实际上可以进行堆溢出改写。比如我可以指定id为0,然后溢出覆盖id为1的管理块的数据域指针。
2号功能可以打印出指定id管理块数据域指针指向的内容,最多使用2次。
那么我们可以先溢出部分覆盖1号管理块的数据域指针,使其指向0号管理块的返回地址(也就是libc相关地址),这里要覆盖两个字节,1/16概率,本地可以先关aslr进行调试。然后用2号功能得到libc基址。之后可以再次改写1号块的数据域指针为environ来泄露栈相关地址。
最后我们把1号块的数据域指针覆盖成栈上的返回地址,注意是进入cp具体memcpy实现函数的返回地址,因为我们实际上前面覆盖时把1号块的返回地址覆盖了,最后release时会检测到直接退出程序。然后对1号块调用1号功能就可以覆盖返回地址了,执行我们的rop链。
1 | from pwn import * |

XYCTF{eb7711df-94d9-4eb8-b94f-859dfaa638fe}
pwn-奶龙回家
看沙箱,只能进行常规orw。

这题一开始给了栈上相关地址,但进行了随机偏移,不过因为是伪加密,所以可以确定这个偏移量。这里需要注意的是,用pwntools远程连接到服务器到这个程序time的运行会有一定延迟,所以我们本地算随机数种子时需要尝试加减一定的偏移量。

主要利用点在于有任意地址读和任意地址写,前面的v20的次数限制可以通过输入负数来进行type confuse,(choice是int而v20是unsigned int),然后v18的限制可以通过改写栈上变量来绕过。由于这题开了沙箱,所以不能进入调用system的分支。


任意地址读可以读got表泄露libc基址,然后绕过上述两个限制后,就可以往栈上任意写了,直接写orw的rop链。
1 | from pwn import * |

XYCTF{0b6fb9b7-e291-4037-9833-52b7f99a0ec0}
pwn-明日方舟寻访模拟器
前面各种抽卡逻辑没有漏洞,漏洞点主要在于后面分享功能存在栈溢出。但溢出的不多,只能放下3个rop元素。利用的挑战点是执行完一遍后会把标准输出关了。一开始是想着先控制rbp回来到read函数读取到可控地址处,然后再栈迁移回来。后面发现有更简单的方法。

由于程序本身存在pop rdi;ret
的gadget,程序里又有system函数,PIE保护也没开启,所以我们可以想办法让一些全局变量变成我们想要的值然后赋给rdi,之后直接call
system即可。这里选用sum_count这个全局变量,因为每次抽卡都可以累加,而且程序也提供批量抽卡功能(上限10000),那么我们直接把sum_count累加成sh,然后就能获取shell了。最后获取shell后,还需要把标准输出重定向到标准错误,这样才好有回显。

1 | from pwn import * |

XYCTF{6edda96d-aacb-434b-8a42-60f78e5d8e56}
pwn-girlfriend
内置菜单,其中方法3 reply 中能往name全局变量读取大量字节,但name全局变量实际只有0x30的大小,那我们就可以在这个区域内布局后续要用到的rop链。而且这个函数还有格式化字符串漏洞,可以一次性把所有要用的地址以及canary都泄露出来。通过动调可知,canary在偏移15,libc相关地址在偏移17,程序相关地址在偏移19。


然后方法一 girlfriend 中我们刚好可以栈溢出覆盖返回地址,那么显然就是栈迁移到我们可控的name全局变量下面的位置,然后由于这题开了沙箱,所以我选择先用 mprotect 改一段可执行,然后直接写openat+sendfile的shellcode。


1 | from pwn import * |

XYCTF{0a311a59-8dbe-43c8-bc43-eba20569d0ad}
pwn-EZ3.0
一道mips架构的pwn题,以前做过类似的栈溢出+retshellcode,这一题比较简单,主要就是考察对mips架构的基本掌握。用Ghidra打开进行分析(我的IDA分析不了mips (ಥ﹏ಥ)),发现chall函数里面糊脸就是一个栈溢出。具体返回地址在哪个位置可以结合动态调试来看。

用如下方式进行调试,记得-L那指定好,不然会找不到/lib/ld.so.1
。
1 | program = "./EZ3.0" |

IDA搜字符串存在一个可以直接读取flag的命令。程序里也存在system函数的调用。


之后就是要找个可以控$a0的gadget,可以沿用ROPgadget。

这里第一个gadget就可以正确的控制$a0并返回到$sp+4位置处了,在$sp+4布局调用system函数的地址就可以读取到flag了。
1 | from pwn import * |

XYCTF{5e624a31-34f2-47f5-9486-5b8420a39a29}
pwn-heap2
这里给出本地能打通的exp,当时比赛时远程一直打不通,估计是和堆布局有关,毕竟我本地的libc环境是GLIBC 2.39-0ubuntu8.4
,而给出的libc是(Ubuntu GLIBC 2.39-0ubuntu8.3)
,差了一个小版本。我怀疑libseccomp.so.2
版本也有所不同,所以可能在堆上的布局也不同,毕竟我在计算一些chunk的地址时是看的本地的偏移。
题目本身是一个裸的UAF,不过只有16次机会add chunk,而且分配了就收不回来。这里的我们其实可以靠UAF实现劫持tcachebin chunk的fd域,然后分配chunk到**_IO_list_all,将其改成我们可控的chunk地址,然后就是打house_of_apple2**。
由于题目开了沙箱,所以最后要靠svcudp_reply+29和swapcontext的gadget实现rop。
1 | from pwn import * |
re-WARMUP
给了一个vbs文件

可以直接用chatgpt一把梭,我们也可以先把每个chr算好提取出来看看大概逻辑是什么。其中有一些计算结果不在ascii范围之内,我们可以略过。
1 | import re |
得到结果大概长这样,其实已经可以明显看出rc4的加密逻辑了:
1 | MsgBox "Dear CTFER. Have fun in XYCTF 2025!" |
之后直接rc4解密即可。
1 | from Crypto.Cipher import ARC4 |


XYCTF{5f9f46c147645dd1e2c8044325d4f93c}
re-Dragon
.bc
文件是 LLVM 编译生成的 bitcode
文件,也叫“中间表示 IR(Intermediate
Representation)”,不是机器码,也不是源代码,而是一种 LLVM
的中间形式。我们可以通过llvm-dis-17
来将其转换成人类可读的
LLVM IR(文本形式)

反编译出来的程序如下:
1 | ; ModuleID = 'Dragon.bc' |
程序让用户输入一个字符串,然后每两个字符一组,使用
calculate_crc64_direct
函数进行 CRC64
校验,并与内置的 @__const.main.enc
中的 12 个
i64
常量值进行比较。如果全部匹配成功,则输出
Success
,否则输出
Error!
。然后我们就可以结合ai解读逆出解密脚本。
一、核心函数分析:?calculate_crc64_direct@@YA_KPEBE_K@Z
这是一个计算 CRC64 校验值 的函数,签名为:
1 | uint64_t calculate_crc64_direct(const uint8_t* data, uint64_t length); |
算法流程简述:
初始化
crc = -1
(即0xFFFFFFFFFFFFFFFF
)。对每个字节:
将其左移 56 位并与
crc
异或。然后进行 8 次循环(一个字节),每次:
如果最高位为 1,则:
1
crc = (crc << 1) ^ 0x42F0E1EBA9EA3693
否则:
1
crc <<= 1
最后再与
0xFFFFFFFFFFFFFFFF
异或(即按位取反)返回。
这个是标准的 CRC-64-ECMA 算法。
二、主程序分析:main
1 | Input U flag: |
程序从控制台读取一串字符串(最多 65 个字符,保存在 data
数组里)。c
主体校验逻辑:
1 | for (i = 0; i < strlen(data) / 2; ++i) { |
程序以2字节为一组(即两个字符)对输入串进行 CRC64 校验,并和
enc[j]
中的值对比。
所以一共有 12
个 CRC64 值,意味着:
- 一共会计算
12
次 CRC。 - 每次使用输入中的 2 字节作为输入。
所以:输入长度应该正好是 24 字节(即 24 个字符)。
三、目标值 enc
这是 CRC 校验目标值(正确 flag 的 CRC 分组):
1 | enc[12] = { |
四、输入长度与数据块
每次取两个字符,使用 calculate_crc64_direct
校验:
也就是说:
- 输入是一个 24 字节的字符串。
- 每次从
i
开始连续取两个字节,计算出 CRC。
所以我们应当:
- 构造一个长度为 24 的字符串,使得它的每个连续两个字符的 CRC64 校验值对应上表中的目标。
1 | POLY = 0x42F0E1EBA9EA3693 |

flag{LLVM_1s_Fun_Ri9h7?}
re-Moon
有个.py和.pyd文件,py文件里面使用了pyd
1 | import moon |
就要知道pyd在python那个版本下的,在自己的3.12不对,运气好直接在虚拟机的3.11直接成功


然后用ida看了看,找到很多函数


有xor异或的函数,还有targethex(用于异或的16进制),seed种子
然后让ai分析了一下,找了找函数,交叉引用了两个关键函数,check和xor发现
这个check:
1 | __int64 __fastcall sub_180001A00(__int64 a1, __int64 *a2, __int64 a3, __int64 a4) |
和xor:
1 | __int64 __fastcall sub_180001130(__int64 a1, __int64 *a2, __int64 a3, __int64 a4) |
分析了一下,40c0应该就是chek函数,1b70就是xor函数,并且xor函数传参是两个

xor_crypt 的关键逻辑:
- 要求 2 个参数(
a2[0]
,a2[1]
):v9 = *a2
是第一个参数(待加密或解密的数据)*((_QWORD *)&v13 + 1) = a2[1]
是 key
- 调用
sub_180001370(v8, v9, key)
执行 XOR 操作,最后返回结果
所以 xor_crypt(data, key)
=
sub_180001370(data, key)
是我们要重点还原的函数,几乎可以肯定它就是执行字节级异或。
反正ai分析了很多

最后尝试直接用 Python 脚本调用 .pyd,测试 xor_crypt()与 check_flag(),观察行为。

说是moon.xor_crypt()要两个参数,而moon.check_flag()要一个参数,moon.check_flag()这个好理解,直接是输入flag检查,moon.xor_crypt()这个根据上面的分析,应该是有个密钥(也就是看到的seed种子,后面试出来的,嘻嘻)然后就有了接下来的ai对话:
问了一下模型,让他写了个脚本,找出模块内有哪些变量/函数/模块
1 | import sys |

看到有这些玩意儿,让他提取这些的值
1 | import importlib.util |

再根据前面的分析,写了个解密的脚本
1 | import moon |
刚开始根据分析以为先传的是16进制
1 | (b'\x42\x6b\x87\xab\xd0\xce\xaa\x3c\x58\x76\x1b\xbb\x01\x72\x60\x6d\xd8\xab\x06\x44\x91\xa2\xa7\x6a\xf9\xa9\x3e\x1a\xe5\x6f\xa8\x42\x06\xa2\xf7',1131796) |
结果报错了

后面换回来就对了

得到flagflag{but_y0u_l00k3d_up_@t_th3_mOOn}
crypto-Division
1.稍微搜一下就知道,漏洞在于Python的random模块使用的是MT19937伪随机数生成器,这是一个确定性算法,只要获取足够多的随机数样本,也就是624个32位值,就可以完全预测后续的所有随机数输出。只预测下一个32位的话可看https://blog.csdn.net/qq_57235775/article/details/131168939。
2.这里一开始以为预测11000和10000要用32位去拼,因为不是32的倍数,想尽办法想把这个预测大数的函数写好,问ai也是搞不清楚是截高位还是低位,还有各种掩码什么的,两种都试了结果都不行。最后真的是福至心灵,一开始有在本地测猜后续的32位确认没问题,是用的predictor.getrandbits(32),那我不自己去用32位拼了,直接用训练好的来predictor.getrandbits(11000)呢?结果还真对了,想复杂了。
exp:
1 | import time |
flag值:XYCTF{64341c34-1e13-47d5-b97c-bc17f7b7fe98}
crypto-reed
1.看题目。大概就是用户提供一个种子,使用该种子初始化PRNG生成两个参数a和b,然后使用线性同余加密方式加密flag,(a * table.index(m) + b) % 19198111。
2.nc上之后可以看到,虽然每次得到的密文组不同,但是固定的几个位置上的数一定是相同的,比如1,2,5...。说明这几个位置上的明文是相同的,如果可以先猜测这些位置上的明文,就可以列方程组解a,b,由此再去解剩余的密文。(说实话是遍历试的明文,代码总是有点问题改了很久,答案出来之后只能说哎,这怎么能没看出来,这相同的位置,这提示,哎)
3.随便选一组数据来解:
1 | import string |
flag值:XYCTF{114514fixedpointissodangerous1919810}
misc-问卷
填写问卷得flag。

flag{TH@NK_U!WE_H0P3_Y0U_H@VE_FU7!H@PPY_H@CKING!}
misc-XGCTF
1.审题,先去 CTFshow 找一下西瓜杯这场比赛
2.根据题目 "唯一由 LamentXU 出的题", 点进每道题看看,找到这道题是 easy_polluted
3.为了方便找对应的原题,看看官方 wp,没想到直接给了
4.直接搜 CISCN 华东南 WEB 没怎么找到,搜 dragonkeep 什么的也没找到,于是换成搜出题想找到出题人的博客,说不定有更详细的信息。也是很容易就找到了。
5.进入博客页面
6.下翻,左侧可拉开,有搜索功能
7.损友的话有可能会提到对方的,于是在这里搜索 dragonkeep,确实提到了
8.好好好直接给了对方的博客地址
9.访问该地址,找到历史文章 CISCN 的 WEB,翻下源码搜 flag 就看见了,再 base64 解密即可
flag 值:flag{1t_I3_t3E_s@Me_ChAl1eNge_aT_a1L_P1e@se_fOrg1ve_Me}
misc-签个到吧
1.看一眼附件,挺眼熟的,根据题目内容搜一下,Brainfuck 啊
2.直接尝试用网站一键解密!
3.结果一片空白,短一点也是空白。好吧,只能老老实实学一下这个语言规则,稍微了解一下就好
4.这时候大概能发现这个-+-+-+纯没用,可删,但这应该影响不了最后的结果才对。再用调试工具试一下看是哪出了问题 https://ashupk.github.io/Brainfuck/brainfuck-visualizer-master/index.html
5.发现 [] 的乘法做完之后本来都已经到右边的指针又返回来把往左一个的这个数清零。也就是 < [-] 这部分的问题,全局选一下删掉,再运行就能发现每个位置的数都保存好了,按照 ascii 码也大概知道对了。
6.但是这个运行太慢,就写脚本了
1 | def brainfuck_interpreter(code): |
7.得到
flag 值:flag{W3lC0me_t0_XYCTF_2025_Enj07_1t!}
misc-MADer也要当CTFer
是个mkv文件,打开只有十几秒的视频,但是下面时长5小时,肯定有问题

然后看到后缀是mkv,搜一下这个mkv能封装视频、音频、字幕文件,下个小工具箱导出文件

有个字幕.ass文件

后面的text字段是标准的16进制,写个脚本提取
1 | import re |
提取出来直接导入16进制文件

文件头是rifx,搜了一下是个ae的文件,是.aep,但是我的ae打不开。。。。就直接在线预览,搜了一下flag


只有这俩,但是说明flag肯定在,找找又发现

发现他们都在这个左边,我就直接慢慢看左边,没看到,最后我用记事本按这个方法找到了,不知道为什么在线预览没有,在记事本里他们前面的字符都一样我就这样找到的


得到flagflag{l_re@IIy_w@nn@_2_Ie@rn_AE}
misc-会飞的雷克萨斯
看到这个题目就知道答案了,大名鼎鼎的四川小孩,嘻嘻,但是我刚开始以为是找左边三位小数md5加密(我没加群!!!!!!)。下次flag形式发题目描述里吧,求求你。
现在dy搜一下这件事,是四川省内江市资中县的事情
地图直接搜
得到flagflag{四川省内江市资中县春岚北路城市中心内}
misc-曼波曼波曼波
qr码是假的

有一个smn.txt,看起来就像是要base64转图片(赛马娘),但是=在前面说明应该是需要逆序,直接在线逆序!!!!


然后b64转图片

得到图片

然后就是正常的隐写,foremost/binwalk分离

里面是个zip,解压一张老隐写图和一段提示解压zip,密码XYCTF2025

俩看起来一样的图,直接blindwatermark分离!!!!!


得到flagXYCTF{easy_yin_xie_dfbfuj877}
misc-Greedymen
这个写个脚本直接出
1 | from collections import defaultdict |
输出:

整理得到
1 | 47, 49, 35, 39, 26, 46, 33, 45, 38, 44, 34, 50, 30, 28, 42, 40, 32, 24, 36 |
直接输入,连赢三局,得到flag



flag:flag{Greed, is......key of the life.}
- 标题: 2025xyctf wp
- 作者: collectcrop
- 创建于 : 2025-04-08 11:26:17
- 更新于 : 2025-04-08 11:34:03
- 链接: https://collectcrop.github.io/2025/04/08/2025xyctf-wp/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。