如何在Windows上安全地转义cmd.exe shell的命令行参数?

时间:2021-09-17 06:29:38

Ok, I have a some command wich MUST be executed in shell=True mode.

好吧,我有一个必须在shell = True模式下执行的命令。

os.system or subprocess.Popen(..., shell=True)

os.system或subprocess.Popen(...,shell = True)

And this command contain string substitution like: cmd = "some_secret_command {0}".format(string_from_user)

此命令包含字符串替换,如:cmd =“some_secret_command {0}”。format(string_from_user)

I want escape string_from_user variable to prevent ANY injections.

我想要转义string_from_user变量来防止任何注入。

Simple wrong answers:

简单的错误答案:

  1. Use shlex.quote - incorrect
  2. 使用shlex.quote - 不正确

print(shlex.quote('file.txxt; &ls . #')) -> 'file.txxt; &ls . #' (injection)

print(shlex.quote('file.txxt;&ls。#')) - >'file.txxt; &ls。 #'(注射)

Example:

> python -c "import sys; print(sys.argv[1])" 'file.txxt; &ls . #'secret.txtsecret2.txt
  1. Use escape ^ - incorrect
  2. 使用转义^ - 不正确

Example:

import osCMD = '''string with spaces'''.replace('', '^').replace('^"', '')os.system('python -c "import sys; print(sys.argv[1])" {0}'.format(CMD))

Now I can use (space) and inject more then one argument.

现在我可以使用(空格)并注入多个参数。

  1. Use ^ and " or ' - incorrect
  2. 使用^和“或” - 不正确

Example:

import osCMD = '''some arg with spaces'''.replace('', '^').replace('^"', '')os.system('python -c "import sys; print(sys.argv[1])" "{0}"'.format(CMD))

print ^s^o^m^e^ ^a^r^g^ ^w^i^t^h^ ^s^p^a^c^e^s^

打印^ s ^ o ^ m ^ e ^ ^ a ^ r ^ g ^ ^ w ^ i ^ t ^ h ^ ^ s ^ p ^ a ^ c ^ e ^ s ^

and if '

而如果 '

import osCMD = '''some spaces'''.replace('', '^').replace('^\'', '')os.system('python -c "import sys; print(sys.argv[1])" \'{0}\''.format(CMD))

print 'some

I now about shell=False but this is incorrect for me.

我现在关于shell = False,但这对我来说是不正确的。

1 个解决方案

#1


9  

The problem with quoting command lines for windows is that there are two layered parsing engines affected by your quotes. At first, there is the Shell (e.g. cmd.exe) which interprets some special characters. Then, there is the called program parsing the command line. This often happens with the CommandLineToArgvW function provided by Windows, but not always.

引用Windows命令行的问题在于有两个分层的解析引擎受到引号的影响。首先,有一个Shell(例如cmd.exe)可以解释一些特殊字符。然后,有一个被调用的程序解析命令行。这通常发生在Windows提供的CommandLineToArgvW函数中,但并非总是如此。

That said, for the general case, e.g. using cmd.exe with a program parsing its command line with CommandLineToArgvW, you can use the techniques described by Daniel Colascione in Everyone quotes command line arguments the wrong way. I have originally tried to adapt this to Ruby and now try to translate this to python here.

也就是说,对于一般情况,例如,使用cmd.exe和一个用CommandLineToArgvW解析其命令行的程序,你可以使用Daniel Colascione所描述的技术在Everyone中以错误的方式引用命令行参数。我原本试图将其改编为Ruby,现在尝试将其转换为python。

import redef escape_argument(arg):    # Escape the argument for the cmd.exe shell.    # See http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx    #    # First we escape the quote chars to produce a argument suitable for    # CommandLineToArgvW. We don't need to do this for simple arguments.    if not arg or re.search(r'(["\s])', arg):        arg = '"' + arg.replace('"', r'\"') + '"'    return escape_for_cmd_exe(arg)def escape_for_cmd_exe(arg):    # Escape an argument string to be suitable to be passed to    # cmd.exe on Windows    #    # This method takes an argument that is expected to already be properly    # escaped for the receiving program to be properly parsed. This argument    # will be further escaped to pass the interpolation performed by cmd.exe    # unchanged.    #    # Any meta-characters will be escaped, removing the ability to e.g. use    # redirects or variables.    #    # @param arg [String] a single command line argument to escape for cmd.exe    # @return [String] an escaped string suitable to be passed as a program    #   argument to cmd.exe    meta_chars = '()%!^"<>&|'    meta_re = re.compile('(' + '|'.join(re.escape(char) for char in list(meta_chars)) + ')')    meta_map = { char: "^%s" % char for char in meta_chars }    def escape_meta_chars(m):        char = m.group(1)        return meta_map[char]    return meta_re.sub(escape_meta_chars, arg)

Applying this code, you should be able to successfully escape your parameters for the cmd.exe shell.

应用此代码,您应该能够成功转义cmd.exe shell的参数。

print escape_argument('''some arg with spaces''')# ^"some arg with spaces^"

Note that the method is expected to quote a single complete argument. If you are collecting your argument from multiple sources, e.g., by building a string of python code to pass to the python command, you have to assemble this before passing it to escape_argument.

请注意,该方法应引用一个完整的参数。如果你从多个源收集你的参数,例如,通过构建一串python代码传递给python命令,你必须在将它传递给escape_argument之前组装它。

import osCMD = '''string with spaces and &weird^ charcters!'''os.system('python -c "import sys; print(sys.argv[1])" {0}'.format(escape_argument(CMD)))# string with spaces and &weird^ charcters!

#1


9  

The problem with quoting command lines for windows is that there are two layered parsing engines affected by your quotes. At first, there is the Shell (e.g. cmd.exe) which interprets some special characters. Then, there is the called program parsing the command line. This often happens with the CommandLineToArgvW function provided by Windows, but not always.

引用Windows命令行的问题在于有两个分层的解析引擎受到引号的影响。首先,有一个Shell(例如cmd.exe)可以解释一些特殊字符。然后,有一个被调用的程序解析命令行。这通常发生在Windows提供的CommandLineToArgvW函数中,但并非总是如此。

That said, for the general case, e.g. using cmd.exe with a program parsing its command line with CommandLineToArgvW, you can use the techniques described by Daniel Colascione in Everyone quotes command line arguments the wrong way. I have originally tried to adapt this to Ruby and now try to translate this to python here.

也就是说,对于一般情况,例如,使用cmd.exe和一个用CommandLineToArgvW解析其命令行的程序,你可以使用Daniel Colascione所描述的技术在Everyone中以错误的方式引用命令行参数。我原本试图将其改编为Ruby,现在尝试将其转换为python。

import redef escape_argument(arg):    # Escape the argument for the cmd.exe shell.    # See http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx    #    # First we escape the quote chars to produce a argument suitable for    # CommandLineToArgvW. We don't need to do this for simple arguments.    if not arg or re.search(r'(["\s])', arg):        arg = '"' + arg.replace('"', r'\"') + '"'    return escape_for_cmd_exe(arg)def escape_for_cmd_exe(arg):    # Escape an argument string to be suitable to be passed to    # cmd.exe on Windows    #    # This method takes an argument that is expected to already be properly    # escaped for the receiving program to be properly parsed. This argument    # will be further escaped to pass the interpolation performed by cmd.exe    # unchanged.    #    # Any meta-characters will be escaped, removing the ability to e.g. use    # redirects or variables.    #    # @param arg [String] a single command line argument to escape for cmd.exe    # @return [String] an escaped string suitable to be passed as a program    #   argument to cmd.exe    meta_chars = '()%!^"<>&|'    meta_re = re.compile('(' + '|'.join(re.escape(char) for char in list(meta_chars)) + ')')    meta_map = { char: "^%s" % char for char in meta_chars }    def escape_meta_chars(m):        char = m.group(1)        return meta_map[char]    return meta_re.sub(escape_meta_chars, arg)

Applying this code, you should be able to successfully escape your parameters for the cmd.exe shell.

应用此代码,您应该能够成功转义cmd.exe shell的参数。

print escape_argument('''some arg with spaces''')# ^"some arg with spaces^"

Note that the method is expected to quote a single complete argument. If you are collecting your argument from multiple sources, e.g., by building a string of python code to pass to the python command, you have to assemble this before passing it to escape_argument.

请注意,该方法应引用一个完整的参数。如果你从多个源收集你的参数,例如,通过构建一串python代码传递给python命令,你必须在将它传递给escape_argument之前组装它。

import osCMD = '''string with spaces and &weird^ charcters!'''os.system('python -c "import sys; print(sys.argv[1])" {0}'.format(escape_argument(CMD)))# string with spaces and &weird^ charcters!