本文档详细讲解 SECSNOW 平台的 Flag 机制,包括静态Flag、动态Flag、多段Flag的配置方法和最佳实践。
1. Flag 类型概述
SECSNOW 平台支持两种 Flag 类型:
| Flag类型 | 说明 | 适用场景 | 安全性 |
|---|---|---|---|
| 静态Flag | 所有用户答案相同 | 签到题、知识点题目 | 低(容易泄露) |
| 动态Flag | 每个用户或者队伍答案唯一 | 比赛题、重要题目 | 高(防止共享) |
2. 多段 Flag 配置(漏洞靶场模块)
漏洞靶场模块支持一道题目配置多个 Flag(1-10个),并为每个 Flag 分配不同分数。
关键机制:平台会为每段Flag生成不同的值,并通过 SNOW_FLAG_1, SNOW_FLAG_2, SNOW_FLAG_3 等环境变量分别注入到容器中。
2.1 配置方法
在后台管理 → 练习题目 → 编辑题目时配置以下字段:
字段1:Flag 值
静态 Flag(手动配置答案):
flag{answer1},flag{answer2},flag{answer3}
- 多个答案用英文逗号分隔
- 系统自动检测逗号分隔的数量
- 顺序提交,第1段给第1个Flag分数,第2段给第2个Flag分数
动态 Flag(自动生成):
3
- 仅填写数量(如:3)
- 系统自动为每个用户生成3个唯一的动态Flag
- 注入方式:通过环境变量
SNOW_FLAG_1,SNOW_FLAG_2,SNOW_FLAG_3分别注入
字段2:Flag 数量
3
- 静态Flag:自动检测逗号数量,无需手动设置
- 动态Flag:必须设置(1-10)
字段3:各 Flag 分数配置
[10, 20, 30]
验证规则: - 数组长度 = Flag数量(如3个Flag必须配3个分数) - 总和 = 题目总分(如总分60,则 [10,20,30] 总和60) - 全部为正整数 - 不符合规则时自动重置为平均分配
示例配置:
题目总分:60分
Flag数量:3个
分数配置:[10, 20, 30]
结果:
- 第1段Flag:10分
- 第2段Flag:20分
- 第3段Flag:30分
总计:60分
2.2 用户提交流程
- 用户创建容器,获得环境
- 解出第1段Flag → 提交 → 获得10分
- 解出第2段Flag → 提交 → 获得20分
- 解出第3段Flag → 提交 → 获得30分
- 全部提交正确 → 总分60分
3. 动态 Flag 注入机制
当题目关联容器时,需要配置动态Flag注入方式,确保Flag正确注入到容器中。
3.1 四种注入方式对比
| 注入方式 | 说明 | 镜像要求 | 平台操作 | 适用引擎 |
|---|---|---|---|---|
| INTERNAL | 镜像已内置 | 读取 SNOW_FLAG 环境变量 |
自动注入环境变量 | Docker/K8s |
| CUSTOM_ENV | 镜像已内置 | 读取自定义环境变量(如 FLAG) |
映射到自定义变量名 | Docker/K8s |
| SCRIPT | 脚本注入 | 无特殊要求 | 执行注入脚本 | Docker/K8s |
| NONE | 不支持动态 | 无特殊要求 | 使用静态Flag | Docker/K8s |
3.2 方式一:INTERNAL(推荐)
适用场景:镜像已内置动态Flag支持,且使用 SNOW_FLAG 环境变量读取
镜像要求示例:
# 启动脚本中读取环境变量
FLAG=${SNOW_FLAG:-"flag{default}"}
echo $FLAG > /flag
平台配置:
动态Flag适配:INTERNAL
Flag环境变量名:留空
Flag注入脚本:留空
工作原理:
- 用户创建容器时,平台生成唯一Flag(如
flag{a1b2c3d4}) - 平台自动注入环境变量:
SNOW_FLAG=flag{a1b2c3d4} - 容器启动时读取
$SNOW_FLAG并使用
优点:
- 最简单,无需额外配置
- 性能最优(启动时注入)
3.3 方式二:CUSTOM_ENV
适用场景:镜像已内置动态Flag支持,但使用自定义环境变量名(如 FLAG, CTF_FLAG, GZCTF_FLAG)
镜像要求示例:
# 启动脚本中读取自定义环境变量
FLAG=${CTF_FLAG:-"flag{default}"}
echo $FLAG > /flag
平台配置:
动态Flag适配:CUSTOM_ENV
Flag环境变量名:CTF_FLAG # 填写镜像中使用的变量名
Flag注入脚本:留空
工作原理:
- 平台生成唯一Flag:
flag{a1b2c3d4} - 平台将
SNOW_FLAG映射为CTF_FLAG=flag{a1b2c3d4} - 容器启动时读取
$CTF_FLAG
优点:
- 兼容第三方镜像(如 GZCTF、CTFd 镜像)
- Docker 和 K8s 通用
- 无需修改镜像
3.4 方式三:SCRIPT(万能方案)
适用场景:镜像未内置动态Flag支持,需要通过脚本注入
镜像要求:无特殊要求
平台配置:
动态Flag适配:SCRIPT
Flag环境变量名:留空
Flag注入脚本:echo "$SNOW_FLAG" > /flag
注意事项:
- 脚本执行失败可能会影响容器启动,请确保脚本中的命令在镜像中存在
- 需要确保脚本中的命令在镜像中存在
- 复杂脚本建议提前在容器中测试
3.5 方式四:NONE
适用场景:题目不支持动态Flag,使用静态Flag
平台配置:
动态Flag适配:NONE
Flag环境变量名:留空
Flag注入脚本:留空
题目Flag值:flag{static_answer}
说明: - 所有用户答案相同 - 适合签到题、公开题目 - 不会注入任何环境变量
4. 如何出动态 Flag 题目
完整流程示例
场景:制作一道Web题,要求用户找到隐藏的Flag文件
步骤1:准备镜像
创建 Dockerfile:
FROM nginx:alpine
# 方式1:使用 INTERNAL(推荐)
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
创建 entrypoint.sh:
#!/bin/sh
# 读取平台注入的 SNOW_FLAG 环境变量
# 单段Flag时:SNOW_FLAG 或 SNOW_FLAG_1 都可以
FLAG=${SNOW_FLAG:-${SNOW_FLAG_1:-"flag{default}"}}
# 将Flag写入隐藏位置
echo $FLAG > /usr/share/nginx/html/.flag.txt
chmod 644 /usr/share/nginx/html/.flag.txt
# 启动Nginx
nginx -g 'daemon off;'
构建镜像:
docker build -t my-web-challenge:v1 .
步骤2:后台配置题目
1. 创建镜像配置(系统容器 → 镜像管理)
- 镜像名称:my-web-challenge:v1
- 动态Flag适配:INTERNAL
- Flag环境变量名:留空
- Flag注入脚本:留空
2. 创建题目(练习题目 → 添加题目)
- 题目名称:Web - 找到隐藏Flag
- Flag类型:动态Flag
- Flag值:1 (生成1个动态Flag)
- Flag数量:1
- 分数配置:[100]
- 关联容器:选择刚创建的镜像配置
步骤3:用户使用流程
- 用户点击"启动环境"
- 平台创建容器,注入动态Flag:
- 单段Flag:
SNOW_FLAG=flag{a1b2c3d4}或SNOW_FLAG_1=flag{a1b2c3d4} - 多段Flag:
SNOW_FLAG_1=flag{xxx},SNOW_FLAG_2=flag{yyy},SNOW_FLAG_3=flag{zzz} - 容器启动,entrypoint.sh 执行:
bash echo "flag{a1b2c3d4}" > /usr/share/nginx/html/.flag.txt - 用户访问
http://容器IP/.flag.txt获得Flag - 用户提交
flag{a1b2c3d4},系统验证通过
环境变量命名规则:
- 单段Flag:SNOW_FLAG 或 SNOW_FLAG_1(两者等价)
- 多段Flag:SNOW_FLAG_1, SNOW_FLAG_2, ... SNOW_FLAG_N(N为Flag数量)
5. 多段动态 Flag 题目示例
场景:制作一道渗透题,分3个阶段,每阶段一个Flag
重要:多段Flag时,平台会为每段Flag生成不同的值,并通过 SNOW_FLAG_1, SNOW_FLAG_2, SNOW_FLAG_3 等环境变量分别注入。
步骤1:准备镜像
entrypoint.sh:
#!/bin/sh
# 从环境变量读取多段Flag(平台自动注入)
FLAG1=${SNOW_FLAG_1:-"flag{default1}"}
FLAG2=${SNOW_FLAG_2:-"flag{default2}"}
FLAG3=${SNOW_FLAG_3:-"flag{default3}"}
# 注入到不同位置
echo $FLAG1 > /flag1.txt # 阶段1:简单文件
echo $FLAG2 | base64 > /var/www/flag2.enc # 阶段2:Base64编码
sqlite3 /db.sqlite "INSERT INTO flags VALUES('$FLAG3');" # 阶段3:数据库
# 启动服务
/start.sh
平台注入机制:
# 平台自动为用户生成3个不同的Flag
SNOW_FLAG_1=flag{a1b2c3d4-段1}
SNOW_FLAG_2=flag{e5f6g7h8-段2}
SNOW_FLAG_3=flag{i9j0k1l2-段3}
步骤2:后台配置
题目总分:300分
Flag类型:动态Flag
Flag值:3 # 告诉平台生成3段Flag
Flag数量:3 # 与Flag值保持一致
分数配置:[50, 100, 150] # 每段Flag对应的分数
镜像配置:
动态Flag适配:INTERNAL
# 平台会自动注入 SNOW_FLAG_1, SNOW_FLAG_2, SNOW_FLAG_3
步骤3:用户答题
- 阶段1:读取
/flag1.txt得到第1段Flag → 提交 → 50分 - 阶段2:Base64解码
/var/www/flag2.enc得到第2段Flag → 提交 → 100分 - 阶段3:从数据库获取第3段Flag → 提交 → 150分
关键说明:
- 平台为每段Flag生成不同的UUID
- 每个用户的3段Flag都是唯一的
- 环境变量命名:SNOW_FLAG_1, SNOW_FLAG_2, ... SNOW_FLAG_N
- 不是只注入一个 SNOW_FLAG 然后自己派生
6. 常见问题
Q1:动态Flag没有注入到容器中?
排查步骤:
# 1. 检查环境变量是否注入
docker exec secsnow-user-<id> env | grep SNOW_FLAG
# 2. 查看容器日志
docker logs secsnow-user-<id>
# 3. 检查镜像配置
# 后台 → 系统容器 → 镜像管理 → 查看配置
常见原因:
- 镜像启动脚本未读取 $SNOW_FLAG
- 脚本注入执行失败(SCRIPT模式)
- 环境变量名配置错误(CUSTOM_ENV模式)
Q2:用户提交正确Flag但验证失败?
排查步骤:
# 1. 查看用户实际Flag值
# 后台 → 用户容器记录 → 查看Flag值
# 2. 检查Flag格式
# 确保格式一致:flag{xxx} 还是 FLAG{xxx}
# 3. 查看提交日志
# 后台 → 答题记录
Q3:多段Flag分数配置不生效?
原因:配置不符合验证规则
正确示例:
题目总分:100
Flag数量:3
分数配置:[20, 30, 50] 总和=100
错误示例:
分数配置:[20, 30, 40] 总和=90,不等于100
分数配置:[20, 30] 长度=2,不等于Flag数量3
分数配置:[20, -30, 110] 包含负数
解决:后台自动重置为平均分配 [33, 33, 34]
Q4:多段Flag只有第1段正确,其他段都验证失败?
原因:镜像只读取了 SNOW_FLAG 或 SNOW_FLAG_1,没有读取 SNOW_FLAG_2, SNOW_FLAG_3
排查:
# 1. 检查容器中的环境变量
docker exec <container_id> env | grep SNOW_FLAG
# 应该看到:
# SNOW_FLAG_1=flag{xxx}
# SNOW_FLAG_2=flag{yyy}
# SNOW_FLAG_3=flag{zzz}
# 2. 检查镜像启动脚本
docker exec <container_id> cat /entrypoint.sh
# 应该包含:
# FLAG1=${SNOW_FLAG_1:-"default"}
# FLAG2=${SNOW_FLAG_2:-"default"}
# FLAG3=${SNOW_FLAG_3:-"default"}
解决:修改镜像启动脚本,确保读取所有环境变量:
# 错误写法(只读取第1段)
FLAG=${SNOW_FLAG:-"default"}
echo $FLAG > /flag1.txt
echo $FLAG > /flag2.txt # 错误:所有段都是同一个值
# 正确写法(分别读取每段)
FLAG1=${SNOW_FLAG_1:-"default1"}
FLAG2=${SNOW_FLAG_2:-"default2"}
FLAG3=${SNOW_FLAG_3:-"default3"}
echo $FLAG1 > /flag1.txt # 正确
echo $FLAG2 > /flag2.txt # 正确
echo $FLAG3 > /flag3.txt # 正确
7. 最佳实践建议
7.1 镜像制作规范
- 优先使用
INTERNAL模式(简单可靠) - 启动脚本中添加默认Flag值(方便本地测试)
- 单段Flag:使用
${SNOW_FLAG:-"flag{default}"}或${SNOW_FLAG_1:-"flag{default}"} - 多段Flag:必须使用
SNOW_FLAG_1,SNOW_FLAG_2,SNOW_FLAG_N - 防止变量为空:
${SNOW_FLAG_1:-"flag{default1}"}
7.2 Flag 格式规范
- 统一格式:
flag{xxx}或FLAG{xxx} - 动态Flag使用UUID4保证唯一性
- 避免特殊字符(如引号、反斜杠)
7.3 分数配置建议
- 难度递增:
[10, 30, 60](后面阶段分数更高) - 总分为整数且易计算
- 避免单个Flag分数过低(<5分)
7.4 测试流程
单段Flag测试
# 1. 本地测试镜像(单段)
docker run -e SNOW_FLAG="flag{test123}" my-image:v1
# 或
docker run -e SNOW_FLAG_1="flag{test123}" my-image:v1
# 2. 验证Flag注入
docker exec <container_id> cat /flag
# 3. 平台测试
# 创建测试用户 → 启动环境 → 提交Flag → 验证
多段Flag测试
# 1. 本地测试镜像(多段)
docker run \
-e SNOW_FLAG_1="flag{test1}" \
-e SNOW_FLAG_2="flag{test2}" \
-e SNOW_FLAG_3="flag{test3}" \
my-image:v1
# 2. 验证每段Flag注入
docker exec <container_id> cat /flag1.txt
docker exec <container_id> cat /flag2.txt
docker exec <container_id> cat /flag3.txt
# 3. 平台测试
# 创建测试用户 → 启动环境 → 依次提交3段Flag → 验证分数累加