# 大小写转换工具
while True:
print("\n=====大小写转换器=====")
print("1. 大写 → 小写")
print("2. 小写 → 大写")
print("3. 退出程序")
choice = input("请输入选项(1/2/3): ")
if choice == "1":
text = input("请输入要转小写的大写文本:")
result = text.lower()
print("转换结果:", result)
elif choice == "2":
text = input("请输入要转大写的小写文本:")
result = text.upper()
print("转换结果:", result)
elif choice == "3":
print("程序退出")
break
else:
print("输入错误,请选择1‑3")
1.备份是个好习惯
漏洞原理(备份文件泄露)
Web 开发人员经常修改index.php,备份时会生成备份文件,常见备份后缀:
index.php.bak
index.php~
index.php.swp
index.php.bak~
.bak 是 Windows 记事本 / 编辑器最经典的备份后缀!
解题步骤
1、访问备份文件地址
在浏览器地址栏输入:
http://160.202.254.160:10418/index.php.bak
回车,浏览器就会下载 index.php.bak 的源码备份文件。
include_once "flag.php";
ini_set("display_errors", 0);
// 取出url ? 后面所有参数
$str = strstr($_SERVER['REQUEST_URI'], '?');
// 去掉问号,拿到参数字符串
$str = substr($str,1);
// 把字符串里面所有的 key 字样,替换成空(删除)
$str = str_replace('key','',$str);
// 将字符串解析成变量
parse_str($str);
echo md5($key1);
echo md5($key2);
// 判断条件
if(md5($key1) == md5($key2) && $key1 !== $key2){
echo $flag."取得flag";
}
必须同时满足 2 个条件
md5($key1) == md5($key2)弱相等,哈希值相同$key1 !== $key2强不等,两个变量本身不能完全一样
关键坑点:str_replace('key','',$str)
它会把参数字符串里面 所有 "key" 文字删掉 举个例子:如果你写
?key1=QNKCDZO&key2=240610708原始字符串:key1=QNKCDZO&key2=240610708经过str_replace('key','')删除 key 之后 变成:1=QNKCDZO&2=240610708👉 parse_str 就识别不出key1 key2,直接失败!
绕过思路:双写 key!
kkeyey → 里面包含 key,str‑replace 删掉中间的key kkeyey 去掉 key → key
kkeyey1→ 删除 key →key1kkeyey2→ 删除 key →key2
最终 Payload(直接复制访问)
?kkeyey1=QNKCDZO&kkeyey2=240610708
原理验证
- 原始参数串:
kkeyey1=QNKCDZO&kkeyey2=240610708 str_replace('key',''):把里面的key删掉kkeyey1→ k + (key) + ey1 → key1kkeyey2→ k + (key) + ey2 → key2- parse_str 解析后得到
$key1 = QNKCDZO$key2 = 240610708 - MD5 弱碰撞: md5 ('QNKCDZO') = 0e830400459932425248873780520603 md5 ('240610708') = 0e462097431906509019562988736854 PHP 中
0e数字==0e数字→ 当作科学计数法,都等于 0,相等成立 $key1 !== $key2:字符串 "QNKCDZO" 和 "240610708" 完全不一样,条件通过 → 输出 flag
备用可用 payload(其他 0e 碰撞字符串)
?kkeyey1=s878926199a&kkeyey2=a155964671a
完整 URL 例子
http://你的ip:端口/?kkeyey1=QNKCDZO&kkeyey2=240610708
#读取output文件将每一个字符加密后的md5解密
import hashlib
生成单个字符→md5的字典表
def get_md5(s):
return hashlib.md5(s.encode()).hexdigest()
枚举所有有可能出现在flag里的字符
charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}_"
lookup = {}
for c in charset:
lookupget_md5(c) = c
读取题目给的output文件
res = ""
with open("output","r") as f:
lines = f.readlines()
for line in lines:
md5_hash = line.strip()
res += lookupmd5_hash
print(res)
2.变量1
完整源码分析
error_reporting(0);
include "flag1.php";
highlight_file(__file__);
if(isset($_GET['args'])){
$args = $_GET['args'];
if(!preg_match("/^\w+$/",$args)){
die("args error!");
}
eval("var_dump($$args);");
}
?>
1. 逐段解析漏洞原理
preg_match("/^\w+$/",$args)正则^\w+$:\w= 只能是字母、数字、下划线 ,不能包含.`
[ ( -等符号。 你传 flag1.php,里面带了 .→ 正则不通过 → 直接输出args error!`。
- 危险代码:
eval("var_dump($$args);");$$args是 PHP可变变量。
如果
args=flag→$$args=$flag,打印$flag变量的值!
- 提示:
flag In the variable !flag 保存在一个变量里面,而不是文件内容,所以不能直接传文件名。
2. 核心绕过思路 ------ GLOBALS 超全局变量
PHP 超全局变量:$GLOBALS $GLOBALS 是一个数组,存放当前脚本所有已经定义的变量(包含 include 进来的flag1.php里面的 flag 变量)
重点!GLOBALS 完全由字母组成,满足正则^\w+$,不会被拦截。
payload:
?args=GLOBALS
完整 URL:
http://160.202.254.160:19275/?args=GLOBALS
3. 为什么这个 Payload 可行
- args=GLOBALS →
$args = "GLOBALS" $$args→${"GLOBALS"}→$GLOBALSvar_dump($GLOBALS)打印出全部全局变量,就能看到 flag1.php 加载进来存 flag 的那个变量。
4. 完整攻击链路复盘
include "flag1.php",执行了 flag1.php,里面定义了存 flag 的变量,存入全局作用域- 传入
args=GLOBALS,正则校验放行(GLOBALS 只有字母) - eval 执行
var_dump($$GLOBALS),输出所有全局变量,flag 直接显示在页面。
❌错误:
?args=flag1.php(带点被正则拦截) ✅正确:?args=GLOBALS
你直接访问上面这条 url 就可以拿到 flag。
3.Linux2
直接使用cat brave | grep -a KEY
grep -a "KEY" brave
4.来自宇宙的信号


nopqrst
5.请攻击这个压缩包
加密 zip 包:file.zip 压缩包内加密文件:flag.png 加密方式:ZipCrypto(传统 zip 加密,支持明文攻击 bkcrack) 最低明文长度:12 字节 PNG 文件头
第 1 部分:完整安装 bkcrack(编译版)
步骤 1:更新软件源
sudo apt update
步骤 2:安装编译依赖包
sudo apt install git cmake build-essential -y
步骤 3:下载源码
git clone https://github.com/kimci86/bkcrack.git
步骤 4:进入源码文件夹,创建编译目录
cd bkcrack
mkdir build
cd build
步骤 5:编译
cmake ..
make
✅编译成功后,可执行程序位置:./src/cli/bkcrack
第 2 部分:把压缩包放到家目录
把下载好的 file.zip 放到 /home/lgr/(你的家目录) 检查文件是否存在
ls ~/file.zip
第 3 部分:查看 zip 包信息(验证是 ZipCrypto 加密)
./src/cli/bkcrack -L ~/file.zip
你之前已经成功看到输出:
0 ZipCrypto Store ... flag.png
ZipCrypto = 可以明文攻击。
第 4 部分:明文攻击【核心命令!直接复制】
⚠️使用
printf输出完整 12 字节 PNG 头部明文 ,解决之前not enough plaintext字节不足报错
./src/cli/bkcrack -C ~/file.zip -c flag.png -p <(printf '\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D')
运行等待几秒,成功会输出类似:
Keys: xxxxxxxx xxxxxxxx xxxxxxxx
三个密钥字符串。
第 5 部分:解密导出图片文件
把下面命令中 key1 key2 key3 替换成你刚刚得到的三组密钥
./src/cli/bkcrack -C ~/file.zip -c flag.png -k key1 key2 key3 -d ~/flag_out.png
第 6 部分:打开图片查看 flag
xdg-open ~/flag_out.png
图片内的内容就是 flag,格式:BugKu{xxxxxx}
备用方案:免编译 pip 安装(更简单,推荐下次用)
如果你不想再编译,可以用这条命令安装,全程一步:
pip install bkcrack
装好之后任意目录直接调用,不用写长路径:
bkcrack -C ~/file.zip -c flag.png -p <(printf '\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D')
常见踩坑清单
- 报错
not enough plaintext→ 不要用 echo,改用 printf,必须 12 字节 - 找不到 ./bkcrack → 编译出来的程序路径是
./src/cli/bkcrack,不是 build 目录下 - 加密类型显示 AES‑256 → bkcrack 明文攻击无效,该题目就做不了
- 跑完攻击没有算出密钥 → 文件不是标准 png,明文头猜错了
如果你运行完明文攻击命令后得到了三组密钥,可以把输出截图发给我,我帮你生成好可以直接复制的解密命令。
6.宽带信息泄露
搜索一下.bin文件可能是什么文件
TP‑LINK 路由器备份配置文件这是最出名的conf.bin场景:路由器网页后台导出的备份配置,内部加密 / 压缩保存 WiFi 密码、宽带 PPPoE 账号密码CSDN博...。
特征:不能直接文本打开,乱码;不是 ELF 可执行。 Windows 工具:RouterPassView,直接加载 conf.bin,搜索username拿 flag。 TP‑LINK 加密 bin,也可以用开源 python 脚本tpconf‑bin‑xml.py解密导出 xml 配置文件。

下载链接:
b4190/routerpassview186.zip-代码预览-RouterPassView-路由密码恢复工具:基于实用工具技术的路由密码恢复项目 - AtomGit
ctrl+f搜索username:
得到flag{053700357621}
7.闪的好快
方案一:Kali ffmpeg 抽帧(最推荐,CTF 标准解法)
原理:ffmpeg 可以把视频,每隔一小段时间导出一张图片。闪得快就设置高帧率抽帧,保证不会漏掉二维码画面。
步骤
-
把下载好的视频文件放到 kali 桌面,打开终端,进入文件所在目录
cd ~/Desktop
-
新建文件夹,存放导出的帧图片
mkdir frames
-
执行抽帧命令,每秒提取 20 帧图片(足够抓住一闪而过的二维码)
ffmpeg -i test.mp4 -vf fps=20 ./frames/frame_%04d.png
参数解释:
-i test.mp4:输入视频,把test.mp4替换成你的附件文件名-vf fps=20:每秒导出 20 张图片;闪的越快 fps 数值就要越大./frames/frame_%04d.png:输出图片保存在 frames 文件夹,自动命名
-
进入 frames 文件夹浏览所有图片
cd frames
xdg-open .
一张张浏览图片,找到二维码画面。 5. 二维码图片保存,扫码。
-
Kali 扫码工具:
zbarimgzbarimg frame_0035.png
批量扫描全部图片
for pic in *.png;do echo "==== pic ===="; zbarimg "pic";done
直接把扫码输出全部保存进日志文件,之后直接搜 SYC 就行,一步到位。
执行这条命令(在 frames 文件夹内运行)
for pic in *.png;do echo "==== $pic ====" >> scan_result.txt; zbarimg -q --qr "$pic" >> scan_result.txt 2>/dev/null;done
解释:
- 所有扫码结果写入
scan_result.txt 2>/dev/null:屏蔽掉那些 "找不到二维码" 的警告垃圾信息
2、扫描完成后,直接搜索 SYC
grep "SYC" scan_result.txt
输出内容就是 flag,格式SYC{xxxx}
方案二:Windows 播放器手动定格(新手最简单)
原理
用播放器逐帧前进,当二维码闪现的时候立刻暂停。
步骤
- 使用播放器 PotPlayer / VLC 打开视频
- 快捷键逐帧播放:
- PotPlayer 快捷键:
→单帧前进
- PotPlayer 快捷键:
- 一点点往后逐帧挪,直到二维码出现,立刻截图
- 微信 / QQ 扫描截图里的二维码,读取 flag
为什么可行:视频是连续画面,逐帧前进不会跳过一闪而过的二维码。
方案三:OpenCV‑Python 脚本自动抽帧(进阶自动化)
适合不想手动翻几百张图片,脚本自动导出所有帧
import cv2
vidcap = cv2.VideoCapture('test.mp4')
success,image = vidcap.read()
count = 0
while success:
cv2.imwrite("frame%d.png" % count, image)
success,image.read()
count += 1
运行之后,视频每一帧都会被保存成图片文件,绝对不会漏掉二维码画面。 然后扫码找到的二维码图片。
方案四:在线视频拆帧网站(零安装,懒人方案)
原理:上传视频到在线帧提取网站,网站自动把视频切成图片包下载。 流程:
- 浏览器打开视频转帧在线工具
- 上传附件视频,设置高帧率提取(15‑25fps)
- 下载打包好的所有帧图片,解压,找到二维码截图扫码
🔍踩坑避坑 & 思考复盘
- 为什么不能直接播放视频? 二维码画面持续时间极短,人眼反应速度跟不上,等你看到的时候画面已经消失,必须定格 / 拆帧。
- fps 参数怎么选?
- 一闪而过:fps=20~30
- 画面停留半秒:fps=5 就足够 fps 越高,生成图片数量越多,越不容易漏掉,但图片文件也越多。
- zbarimg 扫码失败怎么办
- 图片模糊:可以用画图工具放大二维码再扫码
- 二维码不全:回到帧文件夹,找相邻的下一帧图片
零代码懒人终极方案(最省事,不用写任何 python)
关掉 vscode 脚本,直接打开在线 GIF 拆帧网站,上传 masterGO.gif,一键下载全部图片,直接扫码即可。 或者如果你电脑有 ffmpeg,cmd 一行命令搞定:
ffmpeg -i masterGO.gif frame_%02d.png
wps可以一帧一帧看图片,直接微信扫描。
本题最终 flag
S Y C { F 1 a S h _ s o _ f 4 s T }
8.简单加密
末尾AA结束,想到可能是base64中的结尾:== 偏移过来的,先使用ASCII偏移转换找到两个==,再使用base64解码。

key{68743000650173230e4a58ee153c68e8}
9.告诉你个秘密
第1步:所有字符只包含:0‑9、大写字母A‑F
这是十六进制 (hex/base16) 最标志性的特征,属于 CTF 密码题第一优先级检查项。
排除其他可能性
MD5:固定 32 位,这里长度明显更长;
SHA1:40 位,长度也不匹配;
不是直接密文:一堆数字 + A‑F 几乎不会是明文加密结果;
base16解码结果:
cjV5RyBscDlJIEJqTSB0RmhC
VDZ1aCB5N2lKIFFzWiBiaE0g
第 2 步:解码后为什么立刻想到 Base64?
看到的线索
hex 解码出来的文本:cjV5RyBscDlJIEJqTSB0RmhCVDZ1aCB5N2lKIFFzWiBiaE0g 字符全部落在 Base64 合法字符集:A‑Z a‑z 0‑9 + / =
base64解码结果:
r5yG lp9I BjM tFhB
T6uh y7iJ QsZ bhM
键盘包围密码」(键盘围栏密码)固定规则: 每 4 个字符,代表键盘上一个字母的上下左右 4 个方向按键,四个点包围住中间目标字母。 格式永远是:XXXX XXXX XXXX,4 字符 = 1 组,一组解密出1 个明文字母。
TONGYUAN
flag{TONGYUAN}
10.game1
打开网页源代码看到

- 三条箭头分别讲解
var ppp='112.96.211.89';
定义了一个 ip 参数,写死的固定值,提交请求时必须带上。
var sign = Base64.encode(score.toString());
sign 才是经过 Base64 加密的值!!!
score = 你的游戏分数(例如 100000),先转字符串,再 base64 加密,结果存进变量sign
一直把加密后的密文当成num参数,根本就没有 num 这个参数!
xmlhttp.open("GET","score.php?score="+score+"&ip="+ppp+"&sign="+sign, true);
最最关键:它是 GET 请求!!不是 POST!!! 访问的页面不是 / 而是 score.php 携带 3 个参数:
score:明文分数(100000)
ip:固定 ip 112.96.211.89
sign:分数经过 base64 加密之后的值
完整的正确请求 URL
明文分数:100000 base64 加密 100000 → zMMTAwMDAw==

拼接完整 url:
http://160.202.254.160:14156/score.php?score=100000\&ip=112.96.211.89\&sign=zMMTAwMDAw==
直接复制这条链接丢进浏览器地址栏访问,立刻就能拿到 flag!
方法二:抓包更改分数。
