如何修复Python任意命令执行漏洞

作者:媒体转发 时间:2018-04-02 21:43

字号

今天遇到一个不好做白名单的Python命令执行漏洞修复的问题。由于是shell=True导致的任意命令执行,一开始大胆猜测将True改为False即可。经过测试确实是这样,但是参数需要放在list里,稍微有点麻烦。

后来考虑,还可以做黑名单,过滤掉特殊字符,那就写fuzz脚本跑那些需要过滤的字符。最后觉得黑名单方式可能会被绕过,就看官方文档,发现了一个牛逼的修复方法,利用shlex.quote()在命令的参数两边加上一对单引号。

2  测试环境

CentOS Linux release 7.3.1611 (Core)

Python 2.7.5

本文在没有特殊描述环境下,都是在以上环境测试。

3  shell值为True和False的区别

先来看看造成命令执行的代码

s=subprocess.Popen('id', shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
print(s.communicate()) # 输出结果,并kill产生的新进程

当shell=True,并且第一个参数外部可控,那么就能造成任意命令执行。

3.1 shell为False

改为False,任意命令执行漏洞就会被修复。但确实是这样

>>> s=subprocess.Popen(["ls",";id"], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
>>> s.communicate()
('', 'ls: cannot access ;id: No such file or directory\n')

这样即使;id可控,也不能任意命令执行。

执行cat /etc/passwd,如果命令要跟参数,第一个参数必须是一个list。

>>> import subprocess
>>> s=subprocess.Popen(['cat', '/etc/passwd'], shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

此时,查看python的进程情况:

[root@sec ~]# ps -ef | grep 24593
root     24593 24536  0 11:28 pts/0    00:00:00 python
root     24594 24593  0 11:28 pts/0    00:00:00 [cat] <defunct>

可以看到python有一个子进程叫做(cat)。证明,shell=False是python作为父进程执行了cat这个bin文件,产生一个子进程。测试的时候,如果要kill刚产生的子进程,使用s.communicate(),并查看返回结果。

测试发现,当shell=True,并且subprocess.Popen的第一个参数为一个list时,python进程会被卡死。

3.2 shell为True
import subprocess
s=subprocess.Popen('whoami | wc -l', shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

可以看到,Python新建了一个叫sh的子进程,该进程执行了whoami | wc -l命令。继续执行python命令s.communicate(),刚产生的子进程就被kill了。

[root@sec ~]# ps -ef | grep 16323
root 16323 16256 0 14:20 pts/0 00:00:00 python
root 16379 16323 0 14:26 pts/0 00:00:00 [sh] <defunct>

所以,证明,当shell=True时,Python调用/bin/sh去执行命令。

但是有一个特例,当shell=True,执行一个没有任何参数的命令的情况和shell=False一样。说明,没有任何参数的命令,设置shell=True,并没有生效。

s=subprocess.Popen('whoami', shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

再查看发现,python的子进程并没有sh,而是[[whoami] <defunct>],所以证明了,没有任何参数的命令,设置shell=True,并没有新建一个bash去执行该命令。

[root@sec ~]# ps -ef | grep whoami
root 16200 15484 0 14:13 pts/0 00:00:00 [whoami] <defunct>
root 16203 11641 0 14:14 pts/1 00:00:00 grep --color=auto whoami

[root@sec ~]# ps -ef | grep 15484
root 15484 10092 0 12:24 pts/0 00:00:00 python
root 16200 15484 0 14:13 pts/0 00:00:00 [whoami] <defunct> 3.3 总结二者区别

比较简单粗暴的可以理解为,True用/bin/sh执行,False是Python直接调用命令,而不会通过bash。

具体的细节区别:

当执行的命令没有参数时,无论是否设置shell=True,python直接执行该命令,而不是通过/bin/sh

当shell=True,并且命令存在参数时,python调用/bin/sh执行命令

当shell=True,并且subprocess.Popen的第一个参数为一个list时,python进程会被卡死

如果设置shell为False,并且想执行带参数的命令,第一个参数必须是一个list

4  Linux命令执行绕过
责任编辑:CQITer新闻报料:400-888-8888   本站原创,未经授权不得转载
继续阅读
热新闻
推荐
关于我们联系我们免责声明隐私政策 友情链接