如何用chat_template通杀开源模型?"

admin 2026-10-18 21:31:35 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

如何用chat\_template通杀开源模型?

AugustTheodor
AugustTheodor

银遁安全团队

2026年6月29日 08:15
广东

在小说阅读器读本章

去阅读

前言

本文转自如下公众号:

简单讲讲上下文机制原理、以及chat\_template与系统提示词注入的关系。

也就是上周的直播内容。

由于本系列的顺序现在已经乱七八糟加上我最近想讲这个,放在前面——自然,我也知道很多渗透最关注的是这个。

漏洞原理

我们知道当前主流LLM的原理是“根据文字生文字”,在LLM的工作过程中,用户输入的每个词被当做一个向量来表示,LLM会根据我们输入的所有词来对后续词汇进行预测,直到段落输出结束。

这里就存在一个很大的问题:在LLM训练完成之后,如果我们要给LLM添加功能、设定角色,这些功能和角色也需要和用户的输入放在一起,被一并输入至LLM。而LLM无法判断哪一部分输入是系统的,哪一部分输入是用户的。

听起来是不是很耳熟?在传统安全中,以“用户输入”和“系统指令”的不清为攻击面而形成的攻击类型并非第一次出现。传统安全中一系列以注入为核心的漏洞如SQL注入和SSRF、XSS都算是其中一员。

值得注意的是,上下文机制虽然和提示词注入漏洞直接相关,但提示词注入这个漏洞本身是一个大类,手法比较多,一些本文中没有提到的手法会在之后讲解。

上下文机制

在实际讲解提示词注入手法之前,我们需要先了解一下LLM的上下文机制。

首先,各位读者应该都能隐隐约约把上下文跟“历史对话”关联到一起,并且事实上,这确实是上下文机制的核心原理。

假设我们在和部署在云端的LLM对话,在每轮次对话中:

1.我们输入的内容会以文字的形式被上传到云端

2.本轮次输入会被以特定格式与历史问答内容(请把与LLM交互的过程想象为写一本永不完结的书)拼接到一起,然后输入到LLM里

那么这里就有一个很关键的问题:用户、LLM(和系统)几方的输入之间是怎么拼接的,拼接格式是什么样的?

Chat Template

说到拼接格式,就要了解chat template。大部分LLM在训练阶段都会被规定一个固定的模式规则,或者说,对于同一个LLM的训练数据必定要倾向于有一个固定的格式——如果训练者预期一个足够好的训练结果。

我们拿deepseek-r1举例。它的拼接格式可以在huggingface社区或者ollama里找到。

以下是deepseek-r1的huggingface页面:

https://huggingface.co/deepseek-ai/DeepSeek-R1?chat\_template=default&format=true[1]

我们可以通过查看模型的tokenizer\_config.json文件以寻找template。这是它的tokenizer\_config.json文件位置:

https://huggingface.co/deepseek-ai/DeepSeek-R1/blob/main/tokenizer\_config.json

它的语法template是用Jinja2写的,如下:

{%- if not add_generation_prompt is defined -%}    {%- set add_generation_prompt = false -%}{%- endif -%}{%- set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt=\"\", is_first_sp=true) -%}{%- for message in messages -%}    {%- if message[\"role\"] == \"system\" -%}        {%- if ns.is_first_sp -%}            {%- set ns.system_prompt = ns.system_prompt + message[\"content\"] -%}            {%- set ns.is_first_sp = false -%}        {%- else -%}            {%- set ns.system_prompt = ns.system_prompt + \"\
\
\" + message[\"content\"] -%}        {%- endif -%}    {%- endif -%}{%- endfor -%}{{- bos_token -}}{{- ns.system_prompt -}}{%- for message in messages -%}    {%- if message[\"role\"] == \"user\" -%}        {%- set ns.is_tool = false -%}        {{- \"<|User|>\" + message[\"content\"] -}}    {%- endif -%}    {%- if message[\"role\"] == \"assistant\" and \"tool_calls\" in message -%}        {%- set ns.is_tool = false -%}        {%- for tool in message[\"tool_calls\"] -%}            {%- if not ns.is_first -%}                {%- if message[\"content\"] is none -%}                    {{- \"<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>\" + tool[\"type\"] + \"<|tool▁sep|>\" + tool[\"function\"][\"name\"] + \"\
\" + \"```json\" + \"\
\" + tool[\"function\"][\"arguments\"] + \"\
\" + \"```\" + \"<|tool▁call▁end|>\" -}}                {%- else -%}                    {{- \"<|Assistant|>\" + message[\"content\"] + \"<|tool▁calls▁begin|><|tool▁call▁begin|>\" + tool[\"type\"] + \"<|tool▁sep|>\" + tool[\"function\"][\"name\"] + \"\
\" + \"```json\" + \"\
\" + tool[\"function\"][\"arguments\"] + \"\
\" + \"```\" + \"<|tool▁call▁end|>\" -}}                {%- endif -%}                {%- set ns.is_first = true -%}            {%- else -%}                {{- \"\
\" + \"<|tool▁call▁begin|>\" + tool[\"type\"] + \"<|tool▁sep|>\" + tool[\"function\"][\"name\"] + \"\
\" + \"```json\" + \"\
\" + tool[\"function\"][\"arguments\"] + \"\
\" + \"```\" + \"<|tool▁call▁end|>\" -}}            {%- endif -%}        {%- endfor -%}        {{- \"<|tool▁calls▁end|><|end▁of▁sentence|>\" -}}    {%- endif -%}    {%- if message[\"role\"] == \"assistant\" and \"tool_calls\" not in message -%}        {%- if ns.is_tool -%}            {{- \"<|tool▁outputs▁end|>\" + message[\"content\"] + \"<|end▁of▁sentence|>\" -}}            {%- set ns.is_tool = false -%}        {%- else -%}            {%- set content = message[\"content\"] -%}            {%- if \"</think>\" in content -%}                {%- set content = content.split(\"</think>\")[-1] -%}            {%- endif -%}            {{- \"<|Assistant|>\" + content + \"<|end▁of▁sentence|>\" -}}        {%- endif -%}    {%- endif -%}    {%- if message[\"role\"] == \"tool\" -%}        {%- set ns.is_tool = true -%}        {%- if ns.is_output_first -%}            {{- \"<|tool▁outputs▁begin|><|tool▁output▁begin|>\" + message[\"content\"] + \"<|tool▁output▁end|>\" -}}            {%- set ns.is_output_first = false -%}        {%- else -%}            {{- \"<|tool▁output▁begin|>\" + message[\"content\"] + \"<|tool▁output▁end|>\" -}}        {%- endif -%}    {%- endif -%}{%- endfor -%}{%- if ns.is_tool -%}    {{- \"<|tool▁outputs▁end|>\" -}}{%- endif -%}{%- if add_generation_prompt and not ns.is_tool -%}    {{- \"<|Assistant|><think>\
\" -}}{%- endif -%}

其中变量的值在文件上面被定义:

按照上面的模版,则一条处理完毕后的提示词示例长这样:

<|begin▁of▁sentence|>系统提示
  <|User|>用户输入
<|Assistant|>  <|tool▁calls▁begin|>    <|tool▁call▁begin|>function    <|tool▁sep|>工具名称      ```json      {\"参数\": \"工具参数概括\"}      ```    <|tool▁call▁end|>  <|tool▁calls▁end|><|end▁of▁sentence|>
<|tool▁outputs▁begin|>  <|tool▁output▁begin|>    工具输出  <|tool▁output▁end|><|tool▁outputs▁end|>
<|User|>用户追问
<|Assistant|>助手回复<|end▁of▁sentence|>

利用chat template注入开源模型

由于开源模型的chat\_template基本都已知,所以针对chat\_template制作的POC对开源模型有额外的效果。

还是用deepseek做演示。经过测试,大多数chat template中的内容对商业部署的deepseek有效,也对以deepseek为基座二次训练的模型有效。

1 系统提示词注入

虽然deepseek中没有专门的系统提示词标签(这也导致大部分系统提示词标签类型的提示词注入对deepseek没什么用),但看chat template中可以看到规定的系统提示词在bos\_token也就是<|begin▁of▁sentence|>后:

所以我们可以把payload放在这个标志后进行注入:

2 其它标签利用

这里的利用范围绝对比我写的要广,比如说——tool调用也是可以注入的。

可以用think标签伪装LLM思考过程。

这个payload我称之为天地同豆,欧耶。

同时,由于用户的对话会在上下文被压缩之前持续存在,即使LLM在第一段对话中识破了用户的意图——在后文中,用户曾使用的payload仍然会对LLM产生影响,比如这里:

References

[1] https://huggingface.co/deepseek-ai/DeepSeek-R1?chat\_template=default&format=true: https://huggingface.co/deepseek-ai/DeepSeek-R1?chat\_template=default&format=tru


如何用chat_template通杀开源模型?" 网络安全文章

如何用chat_template通杀开源模型?"

该文档揭示了利用开源大语言模型公开的chat_template进行提示词注入的攻击方法。核心原理是针对已知的对话模板结构制作有效载荷,文章以DeepSeek-R1为例,演示了通过bos_token后注入系统指令、滥用think标签伪造思考过程等多种手法。关键发现在于此类攻击具有持久性,即使初始意图被识别,注入的载荷仍会在后续对话中持续影响模型行为。
个人武器的“第二次革命”:韩国“全员无人机兵”计划的技术逻辑与供应链大考" 网络安全文章

个人武器的“第二次革命”:韩国“全员无人机兵”计划的技术逻辑与供应链大考"

韩国国防部于2026年6月宣布推行全军50万现役军人配备无人机的计划,旨在将无人机作为单兵第二武器。该计划面临三大挑战:技术层面依赖低成本无人机实现非对称打击和侦察能力下沉;供应链层面因强制要求100%国产化导致单机成本从500美元飙升至3000-5000美元;战术层面存在硬件配给不足(6万架对应50万人)及专业训练缺失问题。分析指出若不能解决工业产能、成本控制及专业化训练等核心矛盾,该计划可能沦为形式主义。
汽车网络安全与功能安全活动之间的关系" 网络安全文章

汽车网络安全与功能安全活动之间的关系"

该文档为汽车网络安全活动宣传内容,主要介绍2026年6月29日在上海举办的汽车网络安全与功能安全相关活动。文档包含大量图片占位符和推广信息,重点引导用户加入技术交流群、下载行业白皮书及PPT资料,并附有多篇汽车功能安全与信息安全相关的解决方案文档链接。
实战复盘|从弱口令到Furion定时任务RCE的漏洞链" 网络安全文章

实战复盘|从弱口令到Furion定时任务RCE的漏洞链"

该文档详细记录了作者在一次微信小程序渗透测试中发现后端Swagger页面存在弱口令漏洞,通过横向扩展发现同一CMS系统其他子域名存在相同弱口令,进而利用Furion框架定时任务功能实现远程代码执行的全过程。文档展示了从信息收集、漏洞验证到利用AI辅助编写攻击代码、通过HTTP外带数据的关键步骤,并总结了弱口令横向测试、出网验证等实战经验。
评论:0   参与:  0