Python 的另外几个反序列化漏洞检查点

哔哔两句

最近刚做完某个白盒渗透项目,通读了整个项目代码后发现了几个比较明显的 Python 反序列化漏洞的问题。

代码中使用了 Pickle 这个 Python 反序列化模块,除此以外还发现了另外一个比较陌生的模块。

from sklearn.externals import joblib

因为在这个项目中 pickle 模块加载的都是扩展名为 .pkl 的文件,我认为它是 PicKLe 的缩写,经过资料查询也证明了这个猜想。

同时除了 pickle 模块,还有一个 joblib 模块也用它的 load() 函数加载了 .pkl 的文件,这让我很好奇,问了几个朋友也没有认识这个模块的,谷歌也没找出关于这个模块的漏洞文章,python 反序列化漏洞都是在讲 pickle、cPickle 和 PyYaml 的。

1568120346(1).jpg

于是我尝试了一下用 joblib 这个模块是否能达到反序列化的效果

1568120500(1).jpg

结论是当然可以

对比生成后的反序列化字节码和跟踪 joblib.load() 函数发现其实他就是封装了 pickle。

而这个 sklearn 其实是一个用于机器学习的库,一般在大型项目中或者有关机器学习的项目中能找到,所以大家以后在白盒审计 python 项目时除了 pickle 和 cPickle 以外还可以注意一下 sklearn 的 joblib 模块。

举一反三

学习一个东西就要举一反三,所以在看完 sklearn 后又查了一些相关的资料。
sklearn 的反序列化问题是由于它要在机器学习的过程中加载模型,所以会有一些反序列化的操作。但是机器学习的库不止 sklearn 一个,还有很多知名的机器学习第三方库,那么它们是否也存在同样的问题?

NumPy (21,219 commits / 11,711 star)

NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。

这个是被曝存在过漏洞的(CVE-2019-6446),如果 load 方法的 allow_pickle 参数被允许的话,或 loads 方法参数可控,则会引起安全问题

numpy 的 load 函数中判断如果 allow_pickle 为 True,则调用 pickle.load 函数

            if not allow_pickle:
                raise ValueError("Cannot load file containing pickled data "
                                 "when allow_pickle=False")
            try:
                return pickle.load(fid, **pickle_kwargs)
            except Exception:
                raise IOError(
                    "Failed to interpret file %s as a pickle" % repr(file))

1568170847.png

numpy.loads() 则是直接调用了 pickle.loads()

1568172733(1).jpg

Demo

#!/usr/bin/python
# -*- coding:utf-8 -*-
# @Author : b1u3r
# @Time : 2019/9/10 21:53

import numpy

numpy.load("blue.pkl",allow_pickle=True )
with open("blue.pkl","rb") as d:
    numpy.loads(d.read())

1568126432(1).jpg

Pandas (20,109 commits / 21,245 star)

Pandas是一个强大的分析结构化数据的工具集;它的使用基础是Numpy(提供高性能的矩阵运算);用于数据挖掘和数据分析,同时也提供数据清洗功能。

这个库的问题还没有被提出,在 pandas.read_pickle() 函数中,同样是由 pickle.load() 引起

1568171247(1).jpg

Demo

#!/usr/bin/python
# -*- coding:utf-8 -*-
# @Author : b1u3r
# @Time : 2019/9/10 21:53
import pandas

pandas.read_pickle("blue.pkl")

1568126794(1).jpg

PyTorch (20,565 commits / 31,489 star)

PyTorch是使用GPU和CPU优化的深度学习张量库。

pytorch 也存在相同的问题,它的 load 函数中,有一个值为 pickle 对象的 pickle_module 参数。

1568171842.jpg

在后面的反序列化的过程直接调用的是 pickle_module.load() 函数,相当于是在调用 pickle.load()

1568171845.jpg

Demo

#!/usr/bin/python
# -*- coding:utf-8 -*-
# @Author : b1u3r
# @Time : 2019/9/10 21:53
import torch

torch.load("blue.pkl")

1568129589(1).jpg

CheckList

以上三个是我从 python 的机器学习最常用、Commits 次数多的几个库中挑选出来的库,经过测试均存在反序列化的问题。

以下例举几个在 python 代码审计时针对反序列化漏洞的检查点。

CheckPoint 1

import pickle

pickle.load(open("blue.pkl","rb"))
with open("blue.pkl","rb") as d:
    pickle.loads(d.read())

CheckPoint 2

import cPickle

cPickle.load(open("blue.pkl","rb"))
with open("blue.pkl","rb") as d:
    cPickle.loads(d.read())

CheckPoint 3

import _pickle

_pickle.load(open("blue.pkl","rb"))
with open("blue.pkl","rb") as d:
    _pickle.loads(d.read())

CheckPoin 4

from sklearn.externals import joblib
joblib.load("blue.pkl")

CheckPoint 5

from pickle import Unpickler

Unpickler(open("blue.pkl","rb")).load()

CheckPoint 6

import numpy
numpy.load("blue.pkl",allow_pickle=True )
with open("blue.pkl","rb") as d:
    numpy.loads(d.read())

CheckPoint 7

import pandas

pandas.read_pickle("blue.pkl")

CheckPoint 8

import torch

torch.load("blue.pkl")

如何防御

其实让被加载的文件用户不可控,就能解决大部分问题,但这样往往在某些场景中不太容易做到。如果系统中存在任意文件写入的漏洞,即使限制了文件可控问题,也可以配合其提升成为任意代码执行。

过滤关键词?虽然也是个办法,但黑名单的方式总归的会被各种各样的骚操作 bypass。

如果要被反序列化的对象仅仅只需要里面的一些属性数据的话,可以使用 json 模块,这样就避免了不必要的不安全的反序列化操作。

发表留言

如未标注转载则文章均为本人原创,转载前先吱声,未授权转载我就锤爆你狗头。

人生在世,错别字在所难免,无需纠正。