Ok, I have a some command wich MUST be executed in shell=True
mode.
os.system
or subprocess.Popen(..., shell=True)
And this command contain string substitution like: cmd = "some_secret_command {0}".format(string_from_user)
I want escape string_from_user
variable to prevent ANY injections.
Simple wrong answers:
shlex.quote
- incorrectprint(shlex.quote('file.txxt; &ls . #'))
-> 'file.txxt; &ls . #'
(injection)
Example:
> python -c "import sys; print(sys.argv[1])" 'file.txxt; &ls . #'
secret.txt
secret2.txt
^
- incorrect Example:
import os
CMD = '''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.
^
and "
or '
- incorrectExample:
import os
CMD = '''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^
and if '
import os
CMD = '''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.
Three Ways to Escape Spaces on WindowsBy enclosing the path (or parts of it) in double quotation marks ( ” ). By adding a caret character ( ^ ) before each space. (This only works in Command Prompt/CMD, and it doesn't seem to work with every command.) By adding a grave accent character ( ` ) before each space.
The Windows command-line interpreter uses a caret character ( ^ ) to escape reserved characters that have special meanings (in particular: & , | , ( , ) , < , > , ^ ).
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.
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.
import re
def escape_argument(arg):
# Escape the argument for the cmd.exe shell.
# See https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
#
# 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 parsed by it. 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_re = re.compile(r'([()%!^"<>&|])')
return meta_re.sub('^\1', arg)
Applying this code, you should be able to successfully escape your parameters for the 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
.
import os
CMD = '''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!
shorter (no other improvement on accepted):
import re
cmd_meta_re = re.compile(r'([()%!^"<>&|])')
cmd_needs_quote = re.compile(r'["\s]')
def cmdquote(s):
s = cmd_meta_re.sub(r'^\1', s)
if not s or cmd_needs_quote.search(s):
s = '"%s"' % s.replace('"', '\\"')
return s
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With