内容简介:眼下,与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
程序员们的期望是让用户执行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
结果是 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 代码如下
执行命令:
在另外一侧监听的nc 已经获取shell
这里要解释下 为啥能够命令注入
主要是因为tcpdump 的 -z 参数
-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选项的说明:
0×03. 参考资料
https://docs.python.org/2/library/subprocess.html
https://www.leavesongs.com/PENETRATION/escapeshellarg-and-parameter-injection.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 函数式编程之数组的函数式编程
- 巧用 PHP 数组函数
- C函数中返回字符数组
- javascript数组常用函数与实战总结
- c# – 数组的GetUpperBound()和GetLowerBound()函数
- python – 如何从numpy数组中确定什么是概率分布函数?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。