The recent (September 2014) disclosure of a command insertion vulnerability for command-shell scripts (batch files), by The Security Factory, made it painfully clear that batch files can be vulnerable to exploits.
Batch files are extremely "weakly typed" (to use the understatement of the millennium): everything is a string, and a string can be everything, command as well as data, or even both, and there is no way to distinguish between the two.
This makes batch code insertion more than a potential threat.
It all comes down to input validation, and the bad news is: input validation is a lot of work and in batch files it will never be 100% fool-proof.
Three methods of passing input to batch files will be discussed here:
Though neither of these is 100% fool-proof, parameter files do come close.
Unquoted command line arguments
%*, etcetera can pose a risk, running "unexpected" code, though I cannot imagine any way (yet) to make it run code with elevated privileges.
To start with the bad news: as far as I know, there is no fool-proof command line input validation.
Some generally used input validation methods and their strong and weak points:
|empty string||whitespace||delimiters||ampersands||(double) equal signs||doublequoted||quotes within|
|1||IF /I %1==TEST||➖||➖||➖||➖||➖||➖||➖||0||❌|
|2||IF /I X%1==XTEST||➕||➖||➖||➖||➖||➖||➖||1||❌|
|3||IF /I "%~1"=="TEST"||➕||➕||➕||➕||➕||➖||➖||5||❌|
|4||ECHO %1| FIND /I "TEST"||➖||➖||➖||➖||➖||➖||➖||0||❌|
|5||ECHO.%1| FIND /I "TEST"||➕||➖||➖||➖||➖||➖||➖||1||❌|
|6||ECHO "%1"| FIND /I "TEST"||➕||➕||➕||➕||➕||➖||➖||5||❌|
|7||ECHO "%~1"| FIND /I "TEST"||➕||➕||➕||➕||➕||➕||➖||6||✔️|
|8||ECHO "%~1"| FIND /I """TEST"""||➕||➕||➕||➕||➕||➕||➕➖||6.5||✔️|
|9||ECHO %1| FINDSTR /L /X /I "TEST"||➖||➖||➖||➖||➖||➖||➖||0||❌|
|10||ECHO.%1| FINDSTR /L /X /I "TEST"||➕||➖||➖||➖||➖||➖||➖||1||❌|
|11||ECHO "%1"| FINDSTR /L /X /I "TEST"||➕||➕||➕||➕||➕||➖||➖||5||❌|
|12||ECHO "%~1"| FINDSTR /L /X /I "TEST"||➕||➕||➕||➕||➕||➖||➖||5||❌|
|13||ECHO "%~1"| FINDSTR /L /X /I """TEST"""||➕||➕||➕||➕||➕||➕||➕➖||6.5||✔️|
|Notes:||1:||The validation methods shown in the table are all case insensitive.
To make them case sensitive, remove the
|2:||The examples all use
|3:||FIND returns an ErrorLevel 1 if "test string" isn't found in the input, or 0 if it is (if the test string is equal to the input, but also if the test string is part of the input).
|4:||Characters that may be "misinterpreted" for delimiters are, besides whitespace: commas, semicolons and equal signs.|
As the table above shows, the weak point of input validation is with "quotes within", a.k.a. "randomly" placed unterminated doublequotes in the command line arguments.
Combining these with ampersands is a recipe for disaster.
Though the command line interpreter (CMD.EXE) does its best to try and interpret doublequotes, it can easily be fooled.
While this is not an issue in
%CD% (folder names can be quoted, but cannot contain doublequotes themselves), it could be in command line arguments.
Even if it doesn't allow cross-scripting so far, it may still cause the batch file to terminate with an error.
A simple demonstration: create the following batch file, and run it with command line arguments
@ECHO OFF CLS SETLOCAL ENABLEDELAYEDEXPANSION FOR /L %%A IN (1,1,9) DO ( SET Count=%%A CALL ECHO %%%%!Count!=%%!Count! CALL ECHO %%%%~!Count!=%%~!Count! CALL ECHO "%%%%!Count!"="%%!Count!" CALL ECHO "%%%%~!Count!"="%%~!Count!" ECHO. ) ENDLOCAL
The resulting output will look like this:
%1=test1"&test2 %~1=test1"&test2 %2= %~2= "%2"="" "%~2"="" ...
Try again, with the entire line in doublequotes, i.e.
The resulting output will look like this:
%1="test1" %~1=test1 "%1"=""test1"" "%~1"="test1" %2= %~2= "%2"="" "%~2"="" ... %9= %~9= "%9"="" "%~9"="" 'test2' is not recognized as an internal or external command, operable program or batch file.
'test2' is not recognized as an internal or external command,operable program or batch file indicates successful code insertion — or at least, it might have been successfull...
Try other "randomly" inserted doublequotes, use more arguments, combine them with ampersands, and see if you can successfully predict the results...
test1 "test2; if an opening doublequote is not terminated by a closing doublequote (
"test2 in this example), CMD.EXE will just assume a doublequote at the end of the command line.
It may be clear from the demonstration above that it is hard, if possible at all, to completely prevent code insertion in batch files.
Instead of passing arguments on the command line, you can present them in a parameter file instead.
A parameter file is a plain text file, with each parameter/argument specified on a single line, e.g.:
SourceDir=D:\MyFiles TargetDir=Z:\MyFiles FileSpec=*.mp3
For those of you who have been around for quite a while: yes, like INI files, but without the section headers.
Though not as flexible as command line arguments, text files can be tested for unwanted content much easier.
Unfortunately, the FOR /F loop required to "read" the parameters introduces another vulnerability that has to be addressed first.
Characters that you want to avoid in a FOR /F loop are parenthesis and singlequotes (besides the usual suspects & < > |).
You can escape them if hard coded in the batch file, but for "free" input this is a pain in the ...
Singlequoutes can be used freely in a FOR /F loop by using
usebackq, but in that case backquotes are unwanted in the parameter file.
For parameter file validation FINDSTR usually is the best choice:
FINDSTR /R /C:"[()&'`\"]" "parameterfile" IF ERRORLEVEL 1 ( ECHO Parameter file does not contain unwanted characters )
Of course we want to parse the file if it is safe, instead of showing a message; this is where
FOR /F is put to work.
But we may want to add an extra test if the lines are in
FINDSTR /R "( ) & ' ` \"" "parameterfile" > NUL IF NOT ERRORLEVEL 1 ( ECHO Sorry, the parameter file contains unwanted characters, and cannot be parsed. ECHO Aborting the script... GOTO:EOF ) REM Only parse the file if no unwanted characters were found FOR /F "tokens=* delims==" %%A IN ('FINDSTR /R /X /C:"[^=][^=]*=.*" "parameterfile"') DO ( SET Parameter.%%A="%%~B" ) SET Parameter.
|Note:||I did not use the
By omitting the
Each part is itself still a regular expression, due to the
The reason to choose this notation instead of a character class? When redirecting the first example,
Using a parameter file instead of command line arguments may be feasible for configuration parameters, but it may prove to be rather impractical for "free" user input.
If you really do need those ampersands, singlequotes and doublequotes, or if you must ask for user input, consider using "stronger" scripting languages like VBScript or, even safer (because it has stronger typing), PowerShell.
You could even consider to have the batch file create the (temporary) VBScript or PowerShell scripts.
SET /P to prompt for input
page last modified: 2022-10-23