1. 项目概述从一次内部渗透测试说起前段时间我在对一个内部系统进行安全评估时遇到了一个典型的Tornado Web应用。目标系统看起来功能正常但一个不经意的参数传递引起了我的注意。在排查一个疑似XSS的点时我尝试了各种Payload效果都不理想。然而当我将测试焦点从用户输入转向应用自身的配置信息时事情出现了转机。我构造了一个特殊的请求试图访问应用的一个错误处理页面并在URL参数中尝试了{{这样的符号。起初页面只是返回了模板渲染错误但当我深入挖掘错误信息中暴露的上下文变量时一个名为handler.settings的字典赫然在列。点开一看里面竟然包含了数据库连接字符串、第三方服务的API密钥甚至还有用于签名JWT令牌的密钥。那一刻我就知道我碰到了一个经典的服务器端模板注入漏洞而handler.settings正是泄露敏感信息的“黄金钥匙”。这个漏洞的本质是攻击者能够将恶意代码注入到Web应用使用的模板引擎中并使其在服务器端执行。对于Tornado这类使用Jinja2或类似引擎的应用来说如果开发人员未对用户输入进行严格的过滤和转义或者错误地将用户可控的数据传递给了模板渲染函数攻击者就能突破模板的沙箱限制执行任意代码、读取或修改服务器上的文件而handler.settings作为Tornado请求处理器的一个内置属性存储了应用的全局配置一旦被模板引擎解析并输出就相当于把家底都亮给了攻击者。今天我就结合这次实战经历为你详细拆解Tornado模板注入漏洞的原理、利用手法特别是如何通过handler.settings泄露敏感信息并给出从开发和安全测试双视角的修复与防御建议。无论你是开发者想避免踩坑还是安全研究者想复现和挖掘此类漏洞这篇文章都能给你提供清晰的路径。2. 漏洞原理深度剖析模板引擎的双刃剑2.1 Tornado模板引擎的工作机制要理解漏洞首先得明白Tornado是如何渲染一个页面的。Tornado本身内置了一个简单的模板系统同时也兼容Jinja2、Mako等主流模板引擎。其核心流程可以概括为接收请求 - 处理器处理业务逻辑 - 准备渲染上下文数据 - 调用模板引擎渲染 - 返回HTML给客户端。在处理器中我们通常会这样写class MainHandler(tornado.web.RequestHandler): def get(self): name self.get_argument(name, World) # 将变量传递给模板 self.render(template.html, namename)这里的self.render()方法会加载template.html文件并将一个包含name变量的上下文字典传递给模板引擎。模板文件里可能是h1Hello, {{ name }}!/h1。引擎的工作就是把{{ name }}替换成变量name的值“World”或者用户传入的值。问题的关键就在于这个“上下文数据”的传递和模板表达式的解析能力。模板引擎为了提供灵活性支持表达式、过滤器甚至控制语句。例如Jinja2支持{{ config.items() }}来遍历字典。如果开发者错误地将一个包含敏感数据或危险方法的对象如handler自身、self.settings传递给了模板或者用户输入直接被当成了模板语法的一部分进行解析漏洞就产生了。2.2 模板注入漏洞的两种常见成因根据我的经验导致Tornado应用出现SSTI漏洞的场景主要有两类它们的危险程度和利用方式有所不同。第一类用户输入直接拼接进模板字符串。这是最危险也最典型的情况。例如开发者在构建动态模板内容时直接使用了字符串格式化或拼接# 危险示例直接拼接 template_content h1Welcome, user_input /h1 self.write(template_content) # 或者使用字符串格式化 template_content h1Welcome, %s/h1 % user_input self.write(template_content)如果user_input是用户可控的并且包含了{{ 7*7 }}那么模板引擎在后续的渲染过程中如果应用其他地方开启了自动转义但这里漏了或者根本没开启就会计算这个表达式并输出49。更可怕的是攻击者可以注入诸如{{ .__class__.__mro__[1].__subclasses__() }}这样的Payload来遍历Python的所有子类寻找可以用于执行命令或读取文件的类。第二类不当的上下文变量传递。这种情况更为隐蔽也是本文重点讨论的通过handler.settings泄露信息的典型场景。开发者可能无意中将整个handler对象或者self.settings字典作为上下文变量传递给了模板。# 危险示例传递了整个handler或settings def get(self): # 错误地将handler自身传递给模板 self.render(debug.html, contextself) # 或者直接传递了settings self.render(config.html, configself.settings)在模板文件debug.html中如果攻击者能够控制模板的某部分内容比如通过URL参数影响模板包含的另一个模板或者模板本身就是为了调试而输出了所有上下文变量那么攻击者就可以通过模板语法访问context.settings或直接访问config来获取全部配置信息。注意即使你没有主动传递handler或settings在某些错误处理或调试模式下Tornado框架自身也可能将包含这些信息的上下文暴露给错误页面模板这也是一个需要警惕的风险点。2.3 为什么handler.settings是敏感信息宝库handler.settings实际上是TornadoApplication初始化时传入的配置字典的一个引用。它通常包含了应用运行所需的所有核心配置。在一次真实的渗透测试中我通过利用SSTI读取到的handler.settings里发现了以下信息数据库凭证mysql://user:passwordhost:port/dbname格式的连接字符串。缓存配置Redis的地址、端口和密码。第三方API密钥用于调用短信服务、邮件服务、支付接口等的密钥和Secret。会话与加密密钥cookie_secret、xsrf_cookie_secret这些密钥一旦泄露攻击者可以伪造任意用户的会话或绕过CSRF防护。调试信息debugTrue的设置这会使得更多的错误信息暴露给用户可能包含代码片段。文件路径静态文件路径、上传文件路径等可能为后续的目录遍历或文件上传漏洞利用提供信息。这些信息如果落到攻击者手里意味着整个应用的数据层、业务层乃至服务器本身都可能失守。攻击者可以直接连接数据库进行拖库利用API密钥进行恶意消费或发送垃圾信息甚至结合其他漏洞实现远程代码执行。3. 漏洞复现与利用实战理解了原理我们动手搭建一个存在漏洞的简易环境来复现。这里我使用Docker快速构建方便大家安全地测试和学习。3.1 搭建漏洞演示环境首先创建一个项目目录并编写存在漏洞的Tornado应用代码vuln_app.py#!/usr/bin/env python3 # vuln_app.py - 存在SSTI漏洞的演示应用 import tornado.ioloop import tornado.web import os class MainHandler(tornado.web.RequestHandler): def get(self): # 模拟一个常见的错误将用户输入直接用于模板渲染这里模拟一个“欢迎信息”模板片段 user_input self.get_argument(greeting, Hello) # 危险操作将用户输入直接拼接到模板字符串中并且没有正确转义 # 注意在实际漏洞中可能是通过include、宏或者动态模板加载等方式引入用户输入 template_content f html body h1{{{{ {user_input} }}}}/h1 pThis is a vulnerable page./p /body /html # 为了演示settings泄露我们故意在另一个“调试”端点传递handler.settings # 但在实际漏洞中settings可能通过错误上下文、全局变量等方式暴露 self.write(template_content) class DebugHandler(tornado.web.RequestHandler): def get(self): # 典型的错误将handler.settings传递给调试模板 # 假设这个端点只在开发环境开启但配置错误导致生产环境也可访问 self.render(debug_info.html, settingsself.settings, handlerself) class ConfigHandler(tornado.web.RequestHandler): def get(self): # 另一种常见错误直接输出配置信息未做任何访问控制 self.write(str(self.settings)) def make_app(): # 应用配置其中包含敏感信息 settings { template_path: os.path.join(os.path.dirname(__file__), templates), debug: True, # 生产环境绝对不要开启 cookie_secret: SUPER_SECRET_COOKIE_KEY_1234567890, database_url: mysql://admin:MySuperSecretPasslocalhost:3306/prod_db, redis_url: redis://:RedisPass123redis-host:6379/0, api_key: AKIAIOSFODNN7EXAMPLE, api_secret: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY, } return tornado.web.Application([ (r/, MainHandler), (r/debug, DebugHandler), # 危险的调试端点 (r/config, ConfigHandler), # 直接查看配置的端点极危险 ], **settings) if __name__ __main__: app make_app() app.listen(8888) print([*] Vulnerable Tornado app running on http://0.0.0.0:8888) tornado.ioloop.IOLoop.current().start()同时创建模板目录templates和文件debug_info.html!-- templates/debug_info.html -- !DOCTYPE html html head titleDebug Information/title /head body h2Application Settings (DANGER!)/h2 pre{{ settings }}/pre hr h2Handler Object Attributes (Even more DANGER!)/h2 p尝试访问{{ handler.settings[database_url] }}/p /body /html接着编写一个Dockerfile来容器化应用FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD [python, vuln_app.py]以及requirements.txttornado6.1最后使用docker-compose.yml来管理version: 3 services: vuln-app: build: . ports: - 8888:8888 volumes: - ./templates:/app/templates - ./vuln_app.py:/app/vuln_app.py在项目根目录下执行docker-compose up --build漏洞环境就在本地的8888端口运行起来了。3.2 手工探测与验证SSTI环境启动后我们开始探测。首先访问根路径http://localhost:8888/它是一个存在SSTI漏洞的端点。第一步基础探测。我们尝试传入一个简单的数学表达式Payloadhttp://localhost:8888/?greeting{{7*7}}如果页面返回的h1标签内显示的是49而不是{{7*7}}那么基本可以确定存在服务器端模板注入。因为模板引擎执行了7*7的计算。第二步确认模板引擎类型。不同模板引擎的Payload语法略有不同。Tornado内置模板和Jinja2是常见的。我们可以通过一些特征Payload来探测{{7*7}}在Jinja2中会返回7777777字符串重复在Tornado内置模板中可能报错或原样输出。{{7*7}}同样是字符串重复。 访问http://localhost:8888/?greeting{{7*7}}观察结果。根据返回结果我们可以调整后续的利用Payload。假设我们确认是Jinja2引擎。第三步探索对象模型寻找可利用的类。这是利用SSTI执行命令或读取文件的关键。Jinja2在沙箱环境下但通过Python的对象继承链__class__,__mro__,__subclasses__可以逃逸。我们注入一个Payload来列出所有可用的子类http://localhost:8888/?greeting{{ .__class__.__mro__[1].__subclasses__() }}这个Payload做了以下几件事是一个字符串对象。.__class__获取它的类class str。.__mro__方法解析顺序返回一个元组[1]通常是class object所有类的基类。.__subclasses__()返回object的所有直接子类列表。 如果成功页面会输出一个很长的列表包含像class os._wrap_close,class subprocess.Popen这样的类。我们需要从中找到可以用于执行命令的类比如subprocess.Popen。实操心得在实际测试中页面可能因为输出过长而截断或渲染失败。可以尝试将结果写入一个文件或者使用更精确的索引来定位目标类。例如可以写一个简单的Python脚本先本地计算出subprocess.Popen在列表中的索引。3.3 利用漏洞读取handler.settings现在我们来看如何读取handler.settings。根据我们的漏洞代码有两个直接的入口入口一直接访问调试端点。访问http://localhost:8888/debug。如果这个端点没有做访问控制比如判断IP来源、环境变量等那么它会直接渲染debug_info.html模板并将settings和handler对象传递进去。在页面中你会直接看到完整的settings字典以pre标签形式输出所有敏感信息一览无余。入口二通过SSTI在存在漏洞的端点间接读取。假设/debug端点被禁用了但根路径/的SSTI漏洞依然存在。我们的目标是利用这个SSTI构造Payload来访问当前请求处理器handler的settings属性。但是在/这个请求上下文中我们如何获取到handler对象呢这取决于模板的上下文。在Tornado中默认的模板上下文会包含一些内置变量和传递进去的变量。一个常用的技巧是尝试访问request、handler或者self。在Jinja2中可以通过遍历上下文来寻找。一个相对通用的方法是利用Python的__builtins__或__globals__来寻找当前上下文中的对象。我们可以尝试一个探测Payloadhttp://localhost:8888/?greeting{{ self }}如果返回了类似tornado.web.RequestHandler object at 0x7f8b1c0b5d90的信息那么恭喜self就是当前的RequestHandler对象。接下来就可以直接读取settingshttp://localhost:8888/?greeting{{ self.settings }}或者更精确地获取某个键值http://localhost:8888/?greeting{{ self.settings[database_url] }}如果self不行可以尝试handler、request或者使用{{ ()|attr(__class__)|attr(__mro__)|attr(__getitem__)(1)|attr(__subclasses__)()|attr(__getitem__)(N) }}这样的链式调用其中N是某个包含有用方法的类的索引通过其__globals__或__init__.__globals__来获取sys.modules进而导入tornado.web模块最终获取到当前请求的上下文信息。这个过程较为复杂需要一定的耐心和技巧。入口三直接访问配置泄露端点。在我们的演示代码中还有一个极度危险的/config端点它直接write了self.settings的字符串形式。访问http://localhost:8888/config所有配置明文返回这是最低级的错误但在一些临时添加的“管理接口”或“健康检查”接口中仍有可能出现。3.4 进阶利用从信息泄露到代码执行获取到handler.settings中的敏感信息已经是重大安全事件但攻击者的脚步通常不会停止。结合获取到的信息攻击可以进一步升级数据库接管直接使用泄露的数据库连接字符串用mysql客户端连接进行数据窃取、篡改甚至删除。缓存入侵利用Redis密码连接Redis服务可能清空缓存、写入恶意数据或利用Redis未授权访问漏洞向服务器写入SSH公钥等。横向移动泄露的API密钥可能用于攻击其他关联系统。会话伪造利用cookie_secret可以伪造任意用户的会话Cookie直接以其他用户身份登录系统。结合SSTI实现RCE如果我们通过SSTI找到了subprocess.Popen类索引假设为N就可以构造命令执行Payloadhttp://localhost:8888/?greeting{{ .__class__.__mro__[1].__subclasses__()[N]([whoami], stdout-1).communicate()[0] }}这会在服务器上执行whoami命令并返回结果。通过这种方式攻击者可以在服务器上建立持久化后门进行内网渗透等更危险的操作。注意事项在真实渗透测试或SRC漏洞挖掘中执行系统命令是高风险操作必须获得明确授权。在未授权的情况下读取配置信息已经足以证明漏洞的危害性应立即上报。4. 漏洞挖掘与自动化检测思路对于安全研究人员来说如何高效地发现这类漏洞呢纯靠手工测试效率太低。下面分享我常用的几种挖掘和检测思路。4.1 黑盒扫描与模糊测试黑盒测试下我们不知道后端代码只能通过输入输出进行推断。可以借助一些工具和Payload字典。工具推荐Burp Suite Active Scan / IntruderBurp的主动扫描规则库包含一些基础的SSTI检测规则。但更有效的是使用Intruder加载自定义的SSTI Payload字典对所有参数进行Fuzz。sqlmap的--tamper脚本sqlmap虽然主要针对SQL注入但其强大的引擎和tamper脚本机制可以用于测试SSTI。可以编写或寻找将SSTI Payload嵌入到参数中的tamper脚本。tplmap这是一个专门检测和利用SSTI漏洞的工具支持多种模板引擎Jinja2, Tornado, Mako等。它可以自动识别引擎类型并尝试利用。命令类似python tplmap.py -u http://target/page?name*自定义Python脚本针对特定目标编写脚本批量测试参数并智能判断响应中是否存在表达式执行成功的特征如49、7777777等。有效的Payload字典一个基本的SSTI探测字典应该包含不同模板引擎的语法{{7*7}} {{7*7}} {{7*7}} % 7*7 % ${7*7} ${{7*7}} #{7*7}对于Tornado/Jinja2还需要包含一些探测上下文对象的Payload{{self}} {{handler}} {{request}} {{settings}} {{config}} {{application}} {{.__class__}} {{().__class__}} {{[].__class__}}在发送这些Payload时要密切观察响应内容的变化是否出现了计算结果是否出现了对象的内存地址表示是否出现了错误信息而错误信息中包含了handler、settings等关键词4.2 白盒代码审计关键点如果你有权限查看源代码那么审计效率会高很多。重点关注以下代码模式搜索render、render_string方法调用检查传递给这些方法的第二个参数即上下文变量。看看是否有将用户输入、请求对象、self、self.settings、self.application.settings等直接或间接传递给模板的情况。# 审计时搜索以下模式 self.render(template, user_inputuser_input) # 要看user_input是否过滤 self.render(template, contextlocals()) # 极其危险locals()包含所有局部变量 self.render(template, configself.settings) # 直接传递配置 self.render(template, handlerself) # 传递handler自身搜索模板文件中的危险表达式在.html、.j2等模板文件中搜索{{和}}之间的内容看是否有直接调用方法、访问属性的操作例如{{ config.items() }}、{{ handler.request.uri }}。评估这些变量是否来自可信源。检查模板继承和包含注意{% include %}、{% extends %}和{% import %}语句。如果被包含或导入的模板文件名是用户可控的也可能导致漏洞。审查错误处理逻辑查看自定义的write_error方法或默认错误页面。错误页面模板是否接收并渲染了异常信息异常信息中是否可能包含敏感的上下文数据全局搜索self.settings和self.application.settings除了在render调用中还要看这些配置信息是否被记录到日志、写入到响应头、或通过其他API接口返回。4.3 针对handler.settings泄露的专项检测对于本文的核心漏洞点可以设计专项测试用例端点探测使用目录扫描工具如dirsearch, gobuster或常见路径字典扫描/debug、/config、/admin/config、/settings、/info、/status等可能暴露配置信息的端点。参数污染在所有的GET/POST参数、Cookie、Header中尝试插入SSTI Payload观察是否在某些错误响应或页面渲染中触发了settings信息的泄露。错误触发故意制造一些应用错误如访问不存在的路由、提交畸形数据观察返回的错误页面是否包含堆栈跟踪堆栈跟踪中是否出现了handler.settings的内容。工具辅助使用Burp的“查找注释和引用字符串”功能或者自定义插件在HTTP历史记录中搜索cookie_secret、database、redis、api_key等关键词这可能会发现意外泄露的配置信息。5. 修复方案与安全开发实践漏洞复现和挖掘是为了更好地修复和防御。下面从开发者和架构师的角度给出不同层次的修复建议。5.1 紧急修复代码层面立即行动如果发现线上系统存在此类漏洞应立即采取以下措施移除或保护调试端点立即下线或严格限制访问如通过IP白名单、强认证任何直接输出handler.settings、self.application.settings或包含handler对象的调试接口。严格过滤模板输入对所有传递给模板渲染函数的用户输入进行严格的过滤和转义。Tornado默认使用tornado.escape.xhtml_escape进行转义但仅对{{ ... }}内的变量有效。如果用户输入被用于构建模板字符串本身则必须手动转义。最佳实践是永远不要将用户输入直接拼接到模板字符串中。最小化模板上下文只传递模板渲染所必需的最小数据集合到上下文。使用明确的字典而不是传递整个对象。# 正确做法传递明确的值 self.render(user.html, usernameusername, emailemail) # 错误做法传递整个对象或locals() # self.render(user.html, contextself) # 危险 # self.render(user.html, **locals()) # 极其危险禁用危险的内置函数/属性访问对于Jinja2可以通过配置环境来限制。可以创建自定义的Jinja2环境并覆写getattr和getitem来阻止对危险属性和方法的访问但这可能会影响模板功能需谨慎评估。from jinja2 import Environment, BaseLoader class SandboxedEnvironment(Environment): def is_safe_attribute(self, obj, attr, value): # 禁止访问以_开头的属性通常是内部属性 if attr.startswith(_): return False return super().is_safe_attribute(obj, attr, value)升级和打补丁确保使用的Tornado、Jinja2等库是最新版本以修复已知的安全漏洞。5.2 安全配置加固Tornado应用环境除了代码运行环境配置也至关重要生产环境关闭Debug模式这是铁律debugTrue会禁用模板缓存、输出详细的错误信息可能包含代码和变量值极大增加信息泄露风险。确保生产环境的Application初始化时debugFalse。settings { debug: False, # 生产环境必须为False # ... 其他配置 }安全的Cookie Secret使用足够长且随机的字符串作为cookie_secret并定期更换。不要使用硬编码在代码中的简单字符串。配置信息外部化不要将数据库密码、API密钥等敏感信息直接写在代码里。使用环境变量、配置管理服务或加密的配置文件来管理。import os settings { database_url: os.environ.get(DATABASE_URL), api_key: os.environ.get(API_KEY), # ... 从环境变量读取 }自定义错误页面重写RequestHandler的write_error方法返回一个友好的、不包含任何内部错误信息的页面给用户同时将详细的错误日志记录到服务器的安全日志中。def write_error(self, status_code, **kwargs): if self.settings.get(debug): super().write_error(status_code, **kwargs) else: self.set_status(status_code) self.render(error.html, status_codestatus_code)5.3 架构与流程建议防患于未然从团队和流程上建立安全防线安全编码规范将“禁止向模板传递不可信对象”、“禁止拼接用户输入到模板字符串”等内容写入团队编码规范并通过Code Review强制执行。依赖组件安全扫描在CI/CD流水线中集成软件成分分析工具定期扫描项目依赖如Tornado、Jinja2的已知漏洞。定期安全审计与渗透测试对线上系统定期进行专业的安全审计和渗透测试主动发现包括SSTI在内的各类漏洞。安全监控与告警对应用日志进行监控设置告警规则例如发现大量包含{{、}}、__class__等关键词的请求时及时发出安全告警。开发者安全意识培训让开发者理解SSTI的原理和危害在开发过程中就能主动避免。6. 常见问题与排查技巧实录在实际的漏洞挖掘、修复和应急响应过程中我遇到过不少典型问题这里记录分享。6.1 漏洞复现时Payload不生效可能原因及排查模板引擎类型判断错误你用的Payload语法可能不匹配目标引擎。先用{{7*7}}、{{7*7}}、% 7*7 %等不同语法的Payload进行测试观察响应差异。查看HTTP响应头中的Server字段或错误信息有时会泄露框架信息。上下文变量名不对你以为的self、handler在模板中可能不可用。尝试使用{{ |attr(__class__) }}来探测基础对象或者通过错误信息来推断可用的变量。有时框架会使用context、view等别名。WAF或输入过滤应用前端可能有WAF或者后端代码对输入进行了过滤如删除{、}、_等字符。尝试双写绕过{{{{、编码绕过URL编码、HTML实体编码、使用不同大小写或特殊字符。表达式被转义Tornado默认开启了自动转义。如果用户输入是作为变量值传递给{{ }}会被转义。但如果输入是被拼接到模板语法中比如控制模板文件名则可能不会被转义。你需要仔细分析用户输入最终出现在模板的哪个位置。6.2 拿到了settings但信息不全或加密了情况分析信息不全开发者可能将敏感配置放在了另一个独立的配置对象或环境变量中没有全部存入settings字典。你需要结合其他信息泄露点如环境变量读取、配置文件读取进行横向扩展。配置值被加密这是一种好的安全实践。敏感信息在代码或配置文件中以加密形式存储运行时解密。此时直接读取settings得到的是密文。你需要进一步寻找解密密钥或算法。密钥可能硬编码在代码中、存放在另一个配置文件、或通过特定的密钥管理服务获取。审计代码中解密相关函数被调用的地方。6.3 修复后如何验证有效性修复漏洞后不能简单认为万事大吉必须进行验证。回归测试重新运行之前成功的攻击Payload确保它们不再生效。对于SSTI应返回被转义的原字符串或安全的错误信息。对于配置泄露端点应返回403、404或经过脱敏的信息。代码扫描使用静态代码分析工具对修复后的代码进行扫描确保没有引入新的安全漏洞并且相关的危险模式已被消除。安全工具扫描使用tplmap、Burp Suite等工具对修复后的接口再次进行扫描。人工复审重点审查修复代码的上下文确保修复是彻底的。例如不仅修复了A接口所有类似模式的B、C接口也都得到了修复。6.4 在SRC平台提交漏洞报告时要注意什么如果你在漏洞赏金平台或企业SRC发现了此类漏洞一份高质量的报告能帮助你更快获得认可。清晰描述漏洞标题明确如“Tornado应用XXX处存在服务器端模板注入漏洞可导致handler.settings配置信息泄露”。提供完整复现步骤像本篇文章的“漏洞复现”部分一样提供详细的URL、请求方法、Payload、每一步的截图和响应结果。最好能提供视频。证明危害不仅要证明可以执行{{7*7}}更要证明可以读取到真实的敏感信息如数据库连接字符串、密钥。注意在证明时应对敏感信息进行打码处理只证明其存在性和可访问性切勿泄露真实数据。给出修复建议简要说明修复方向如“避免将用户输入拼接进模板”、“不要将handler.settings传递给模板”、“生产环境关闭debug模式”等体现你的专业性。遵守平台规则绝不进行未授权的破坏性测试如执行rm -rf命令绝不窃取和传播敏感数据。