Python 3 command injection prevention

1, Background

To be honest, I'm safe. I always find fault with other people's code, but sometimes it's my turn to write my own code without paying attention to security. Among them, if the security script is generally small, then the proportion of the code dealing with security problems will greatly exceed that of the business code, or if I'm not skilled enough to add a function to the full-time development. Since security scripts are not generally used to open services, there is generally no problem.

But some time ago, I was informed by the group that a script I wrote had a command injection vulnerability. I was surprised at the beginning. After communication and analysis, I found that it was because the script called a binary file through the system to report and execute the risky command. When a life risk determination rule is constructed and the command of injection syntax is further constructed, command injection can be realized.

 

2, Direct use shlex.quote() repair

We usually recommend filtering shell metacharacters such as "()"; ";"; ";"; ";"; ", but it's more difficult to implement character processing by ourselves. We try to find a simple method, and the best way is to use existing functions or libraries for processing.

Finally, I can see that it can be used shlex.quote() processing: https://python-security.readthedocs.io/security.html#shell-command-injection

The sample code is as follows:

import shlex


filename_para = 'somefile'
command = 'ls -l {}'.format(filename_para)
print("except result command:")
print("{}\n".format(command))

filename_para= 'somefile; cat /etc/passwd'
command = 'ls -l {}'.format(filename_para)
print("be injected result command:")
print("{}\n".format(command))

filename_para = 'somefile; cat /etc/passwd'
command = 'ls -l {}'.format(shlex.quote(filename_para))
print("be injected but escape result command:")
print("{}\n".format(command))

The operation results are as follows:

 

3, Write your own code shlex.quote() repair

shlex.quote() was introduced after Python 3.3, shlex.quote() code is also very simple, if you want to be compatible with Python version without the function, you can implement the function yourself.

shlex.quote() source code link: https://github.com/python/cpython/blob/master/Lib/shlex.py#L325

In principle, shlex.quote() there are two things that () does. One is to add single quotation mark at the outer layer of the string so that the string can only appear as a single entity. The other is to use double quotation mark to cause the single quotation mark in the string to lose its possible closing function.

The sample code is as follows:

import re


def quote(s):
    """Return a shell-escaped version of the string *s*."""
    # return if string in null
    if not s:
        return "''"
    # _find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search
    # return if string have no those char.
    if re.search(r'[^\w@%+=:,./-]', s, re.ASCII) is None:
        return s
    # use single quotes, and put single quotes into double quotes
    # the string $'b is then quoted as '$'"'"'b'
    return "'" + s.replace("'", "'\"'\"'") + "'"


if __name__ == "__main__":
    filename_para = 'somefile'
    command = 'ls -l {}'.format(filename_para)
    print("except result command:")
    print("{}\n".format(command))

    filename_para= 'somefile; cat /etc/passwd'
    command = 'ls -l {}'.format(filename_para)
    print("be injected result command:")
    print("{}\n".format(command))

    filename_para = 'somefile; cat /etc/passwd'
    command = 'ls -l {}'.format(quote(filename_para))
    print("be injected but escape result command:")
    print("{}\n".format(command))

The operation results are as follows:

 

reference resources:

https://pypi.org/project/shellescape/

Tags: Python shell ascii github

Posted on Tue, 26 May 2020 22:52:17 -0700 by bubblybabs