安全开发之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


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

查看所有标签

猜你喜欢:

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

R Cookbook

R Cookbook

Paul Teetor / O'Reilly Media / 2011-3-22 / USD 39.99

With more than 200 practical recipes, this book helps you perform data analysis with R quickly and efficiently. The R language provides everything you need to do statistical work, but its structure ca......一起来看看 《R Cookbook》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具