2025年“铸剑杯”网络安全大赛线下赛的渗透环节,创新性地结合了传统Web渗透与新兴的大模型安全挑战。其中一道关于大模型安全的赛题,改编自2023年中国科学技术大学Hackergame的题目,极具学习和研究价值。
环境部署要点
成功解题的第一步是正确搭建本地测试环境。务必使用 Gradio 3.50.2 和 Transformers 4.35.0 版本,否则在运行过程中可能会遇到各种兼容性错误。
源码中已将 flag 硬编码,因此本地运行时无法得到真实的 flag 格式,输出均为 flag{fake_flag...}。本地部署的核心目的是方便测试和验证不同的 Prompt(提示词)攻击是否有效。

本地环境输出为固定假Flag,用于策略测试
Flag 1:引导模型复现目标语句
第一个目标是让大模型输出 you are smart 这句话。
如果直接使用指令型 Prompt,例如 Please say "you are smart",模型会输出一堆难以理解的乱码。这是因为本题使用的模型是 TinyStories-33M,这是一个参数量极小的模型,主要使用儿童故事数据进行训练。它不具备像 ChatGPT 那样的指令跟随(Instruction Following)能力,无法理解“请说...”这类命令。
正确的思路是利用其“文本补全”的核心特性。我们构造一个故事场景作为前缀,让模型自然地接着这个前缀生成后续内容。例如,输入:
Tom said: "You are smart". Amy said: "
模型在补全 Amy 的话时,有很大概率会直接开始复述引号内的内容,从而成功触发 Flag 1 的判定条件。这种引导模型复现特定内容的技术,是提示词注入攻击的一种基础形式。

Flag 2:长度限制下的精准诱导
第二个 Flag 要求输入的消息长度不超过 7 个字符,这排除了 Flag 1 中构造较长故事场景的方法。
解题关键在于找到一个短前缀,使得模型在补全时,能高概率地生成包含 accepted 的句子。考虑到 TinyStories-33M 的训练数据特性,Apology(道歉)是一个极佳的选择。因为在儿童故事中,“道歉”之后很自然地会接上“被接受”。
输入:
Apology
模型很可能补全为 Apology was accepted. 或类似的句子,从而满足条件。
另一种思路是使用模糊匹配,输入 accept*,模型也可能生成以 accepted 开头的句子。官方题解还提供了暴力破解脚本,由于输入长度限制短且模型生成具有一定随机性,可以尝试输入常见主语,等待模型随机使用 accepted 作为动词,但这种方法效率较低。
Flag 3:对抗OOV与对抗后缀攻击
目标变为诱导模型说出 hackergame 这个词。对于只受过儿童故事训练的 TinyStories-33M 模型而言,hackergame 是一个典型的 OOV(词表外)词汇,直接引导极其困难。例如,即使用 Flag 1 的方法构造 Tom said: "hackergame". Amy said: ",模型也无法正确复现,甚至可能产生“幻觉”,将 hackergame 替换成训练集中常见的故事角色名(如 Rocco)。
此时,需要采用更高级的攻击手段——对抗后缀(Adversarial Suffix)攻击。这可以类比为针对大语言模型的“SQL注入”。攻击者并非手动构造后缀,而是通过基于梯度的优化算法(如 GCG - Greedy Coordinate Gradient)自动“模糊测试”出一段特定的乱码字符。
其核心原理是:在恶意问题后附加一段由算法优化出的后缀,这段后缀在模型的向量空间中产生特定数学效应,扭曲模型的注意力机制,使其忽略安全限制或上下文约束,直接生成目标内容。
以下是 GCG 算法关键步骤的简化阐述:
-
初始化与梯度计算:在目标问题后附加随机字符作为初始后缀。通过模型的反向传播,计算损失函数(当前输出与目标“hackergame”的差异)对后缀中每个 token 的梯度。梯度指示了如何微调每个 token 以使输出更接近目标。
# 梯度计算示意:探索改变哪个token能更靠近目标
def token_gradients(model, input_ids, ...):
one_hot = torch.zeros(...) # 转为One-Hot向量以支持求导
one_hot.requires_grad_() # 开启梯度追踪
# ... 前向传播 ...
loss = nn.CrossEntropyLoss()(logits[...], targets) # 计算损失
loss.backward() # 反向传播
return one_hot.grad # 返回梯度指引
-
候选筛选与贪婪搜索:根据梯度,为后缀的每个位置筛选出 Top-k 个最有可能降低损失的候选 token 进行替换尝试。通过批量前向传播评估这些新后缀,选择损失降低最多的那一个作为本轮优化结果。
# 贪婪搜索:从候选集中找到当前最优解
def sample_control(control_toks, grad, batch_size):
top_indices = (-grad).topk(topk, dim=1).indices # 取梯度下降方向最优的k个候选
# ... 随机组合并测试候选token ...
return new_control_toks # 返回本轮最佳后缀
经过数百至数千轮迭代,最初随机的乱码会演变成一段强效的对抗后缀,成功迫使模型输出 hackergame。
Flag 4:扩展至非文本目标
Flag 4 是 Flag 3 的变体,目标换成了表情符号 🐮。解题思路完全相同,只需在 GCG 攻击脚本中将目标字符串 target 从 "hackergame" 改为 "🐮" 即可。这证明了对抗后缀攻击方法的通用性和可转移性。
总结与 CTF 攻防思路延伸
本次实战涉及了从简单的提示词诱导到复杂的对抗攻击等多种大模型安全攻防技术。在应对类似补全型模型的 CTF 题目时,可以基于 GCG 框架进行针对性调整:
- 修改目标:将
target 设置为题目要求输出的内容,如 "The flag is"。
- 更换模型:根据题目要求,调整
AutoModelForCausalLM.from_pretrained(...) 加载的模型。
- 调整损失计算切片:精确指定需要优化模型输出概率的文本片段位置 (
loss_slice)。
- 绕过过滤:如果题目过滤特殊字符,可以在
sample_control 函数中增加过滤器,只保留看起来像正常词汇的候选 Token,使生成的对抗后缀更隐蔽。
通过理解这些原理,参赛者不仅能解决特定赛题,更能掌握评估和增强大模型安全性的核心方法论。