哔哔两句
最近刚做完某个白盒渗透项目,通读了整个项目代码后发现了几个比较明显的 Python 反序列化漏洞的问题。
代码中使用了 Pickle 这个 Python 反序列化模块,除此以外还发现了另外一个比较陌生的模块。
from sklearn.externals import joblib
因为在这个项目中 pickle 模块加载的都是扩展名为 .pkl
的文件,我认为它是 PicKLe 的缩写,经过资料查询也证明了这个猜想。
同时除了 pickle 模块,还有一个 joblib 模块也用它的 load() 函数加载了 .pkl
的文件,这让我很好奇,问了几个朋友也没有认识这个模块的,谷歌也没找出关于这个模块的漏洞文章,python 反序列化漏洞都是在讲 pickle、cPickle 和 PyYaml 的。
于是我尝试了一下用 joblib 这个模块是否能达到反序列化的效果
结论是当然可以
对比生成后的反序列化字节码和跟踪 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))
numpy.loads() 则是直接调用了 pickle.loads()
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())
Pandas (20,109 commits / 21,245 star)
Pandas是一个强大的分析结构化数据的工具集;它的使用基础是Numpy(提供高性能的矩阵运算);用于数据挖掘和数据分析,同时也提供数据清洗功能。
这个库的问题还没有被提出,在 pandas.read_pickle() 函数中,同样是由 pickle.load() 引起
Demo
#!/usr/bin/python
# -*- coding:utf-8 -*-
# @Author : b1u3r
# @Time : 2019/9/10 21:53
import pandas
pandas.read_pickle("blue.pkl")
PyTorch (20,565 commits / 31,489 star)
PyTorch是使用GPU和CPU优化的深度学习张量库。
pytorch 也存在相同的问题,它的 load 函数中,有一个值为 pickle 对象的 pickle_module 参数。
在后面的反序列化的过程直接调用的是 pickle_module.load() 函数,相当于是在调用 pickle.load()
Demo
#!/usr/bin/python
# -*- coding:utf-8 -*-
# @Author : b1u3r
# @Time : 2019/9/10 21:53
import torch
torch.load("blue.pkl")
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 模块,这样就避免了不必要的不安全的反序列化操作。