安全开发之subprocess库若干函数中以数组形式传参的安全性分析

栏目: Python · 发布时间: 6年前

内容简介:眼下,与Python相关的安全问题愈发引起人们的注意,本文以最为常用的外部程序调用库(也可称为子进程库)的subprocess库为例,分析其若干函数中的数组传参时引发的安全问题,详文如下。subprocess库作为Python中最为流行的子进程库,是替代老旧的os.system函数和commands等库的首选,其功能强大,灵活性强的特性愈发受到程序员们的喜爱。在开发的过程中,如果没有对subprocess库中等函数的不当使用可能引发的安全问题有足够的了解,很可能会对业务产生严重后果。

0x00. 引言

眼下,与 Python 相关的安全问题愈发引起人们的注意,本文以最为常用的外部程序调用库(也可称为子进程库)的subprocess库为例,分析其若干函数中的数组传参时引发的安全问题,详文如下。

0x01. 先来看看非数组形式传参时可能引发的命令注入

subprocess库作为Python中最为流行的子进程库,是替代老旧的os.system函数和commands等库的首选,其功能强大,灵活性强的特性愈发受到 程序员 们的喜爱。

在开发的过程中,如果没有对subprocess库中等函数的不当使用可能引发的安全问题有足够的了解,很可能会对业务产生严重后果。

举个例子:

subprocess.popen(args, shell=True),如果args 没有过滤,直接传入业务代码中执行, 可能会引起任意代码执行漏洞 ,比如:

参数args为:ls;id

安全开发之subprocess库若干函数中以数组形式传参的安全性分析

程序员们的期望是让用户执行ls的命令,但是结果却也执行了分号后面的id

这个漏洞的原因是因为用户传入的命令中含有';' 这在 shell 环境下被识别为命令分割符 ,而不是ls的

参数,从而导致执行了两个命令,对于这种类型的漏洞,python 中已经内置了一个过滤库pipes (对于Python3则是shlex 库)

安全的编码应该是如下这样: 按照一定约定将命令与参数分割出来,比如以空格

command = args.split(' ', 1)[0]

argument = pipes.quote(args.split(' ', 1)[1])

s = subprocess.Popen(command + ' ' argument  , shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

安全开发之subprocess库若干函数中以数组形式传参的安全性分析  https://www.secpulse.com/archives/76192.html

这样就不会有命令注入了, 与Popen有类似问题的函数还有call,check_output等函数

 0x02. 数组传参时可能引发的命令注入

这时有人会有疑问,上述例子中';' 被识别为命令分割符,是因为上述命令是在shell环境下执行的,

如果不在Shell环境下执行就没这个问题了,比如直接用subprocess.Popen([xxxx,xxxx,xxx], shell=Flalse),直接执行命令,不通过shell环境调用, 比如:

s=subprocess.Popen(['ping', '-c', '1', host], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

假设这里面 host是可控的,如果传入:

www.baidu.com;echo 1

安全开发之subprocess库若干函数中以数组形式传参的安全性分析

结果是 www.baidu.com;echo 1 不可解析,因为www.baidu.com;echo 1被认为是一个整体,是不会执行echo 1的

诚然,这里面不会有命令注入,也正是因为如此,才会使得许多程序员误认为这种用法很安全。

如果将这里的ping 命令替换成其他命令比如tcpdump,则还是可能引发命令注入,因为tcpdump

支持参数表达式,是可以自行组装命令参数后再次进行解析的,这个过程中就会导致命令注入

且看下面的例子:

s=subprocess.Popen(['tcpdump', '-i', 'ens33', args], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

args是可控的,作为过滤数据包的条件, 如果args 传入的是以下内容,则引起命令注入获取反弹shell

-G1 -w 1.txt -z /root/evil.sh

evil.sh 代码如下

安全开发之subprocess库若干函数中以数组形式传参的安全性分析

执行命令:

安全开发之subprocess库若干函数中以数组形式传参的安全性分析

在另外一侧监听的nc 已经获取shell

安全开发之subprocess库若干函数中以数组形式传参的安全性分析

这里要解释下 为啥能够命令注入

主要是因为tcpdump 的 -z 参数

安全开发之subprocess库若干函数中以数组形式传参的安全性分析

-z 与-G 等参数一起使用的时候,会执行-z所指定的程序,这个程序所需的参数是 -w 参数指定的,

不过上述那个evil.sh是不需要传入参数的。

所以这个例子证明,不要以为以数组形式传参给Popen、call、check_output等函数的时候就是安全的

与此例子相似的还有P牛文章: https://www.leavesongs.com/PENETRATION/escapeshellarg-and-parameter-injection.html 中提到的利用'git grep -i --line-number '--open-files-in-pager=id;' master ' 执行命令注入的成功经验。

那么问题来了,怎么防护呢?

有人说利用pipe.quote()过滤嘛,不是能防止命令注入吗,其实不尽然,对于上面的例子是有效的,但不一定对其他例子有效,pipe.quote的原理是类似于escapeshellarg,就是防止参数值变成参数选项,一般会将参数值用引号括起来,

但是引号并不是区分参数选项的标记,比如上面提到的P牛文章中的git grep -i --line-number '--open-files-in-pager=id;' master  中的payload--open-files-in-pager=id加上引号照样被认为是参数选项,还是能够命令执行。

所以最好的办法是在可控的参数前面加上 -- 或者 -e 将可控参数始终认为是值,这样才不会有命令注入的发生。

更改成如下形式则不会有命令注入:

s=subprocess.Popen(['tcpdump', '-i', 'ens33', ‘--’, args], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)


or

s=subprocess.Popen(['tcpdump', '-i', -e, 'ens33', args], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

补充 --- 和 -e选项的说明:

安全开发之subprocess库若干函数中以数组形式传参的安全性分析

0×03. 参考资料

https://docs.python.org/2/library/subprocess.html

https://www.leavesongs.com/PENETRATION/escapeshellarg-and-parameter-injection.html


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

第三次工业革命

第三次工业革命

[美] 杰里米•里夫金(Jeremy Rifkin) / 张体伟 / 中信出版社 / 2012-5 / 45.00元

第一次工业革命使19世纪的世界发生了翻天覆地的变化 第二次工业革命为20世纪的人们开创了新世界 第三次工业革命同样也将在21世纪从根本上改变人们的生活和工作 在这本书中,作者为我们描绘了一个宏伟的蓝图:数亿计的人们将在自己家里、办公室里、工厂里生产出自己的绿色能源,并在“能源互联网”上与大家分享,这就好像现在我们在网上发布、分享消息一样。能源民主化将从根本上重塑人际关系,它将影响......一起来看看 《第三次工业革命》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换