The 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 just a potential threat.
It all comes down to input validation, and the good news is: input validation turns out to be remarkably simple, once you know how.
Command line validation, and parameter files as an alternative, are discussed in their own dedicated page.
SET /Pto prompt for input
A convenient way to prompt for user input is Windows' native
SET /P "Input=Please type anything and press Enter: "
will prompt for input (
Please type anything and press Enter: ), accept all keyboard input until Enter is pressed, and store the input in environment variable
No risk so far...
Try it: when prompted, type
abc&ping ::1 and press the Enter key.
Nothing much happened so far...
Enter the command
SET Input to see if your input was stored in the environment variable
However, when you enter the command
ECHO %Input% the output will look like this:
abc Pinging ::1 with 32 bytes of data: Reply from ::1: time<1ms Reply from ::1: time<1ms Reply from ::1: time<1ms Reply from ::1: time<1ms Ping statistics for ::1: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms
The explanation is that the ampersand acts as a "command separator": everything following a (single) ampersand will be interpreted as a new command.
ECHO %Input% is evaluated to
ECHO abc&ping ::1 which is in turn interpreted as:
ECHO abc ping ::1
|Note:||This issue is similar to the unquoted
For the unquoted
%CD% vulnerability, the solution is to put the variable between doublequotes:
"%CD%"; this works because
%CD% will never contain doublequotes itself (unless of course this dynamic variable has been set to a static value).
SET /P however, the user can type anything when prompted, including ampersands and stray doublequotes.
Suppose we were to place
%Input% between doublequotes; try:
and the output will be:
Looking good so far...
But now, repeat the
SET /P command, and enter
abc"&ping ::1&echo "oops and see what happens with
"abc" Pinging ::1 with 32 bytes of data: Reply from ::1: time<1ms Reply from ::1: time<1ms Reply from ::1: time<1ms Reply from ::1: time<1ms Ping statistics for ::1: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms "oops"
Until recently I believed that safely parsing the input is a cat and mouse game we were never going to win.
Then Kang-Che Sung provided an ingenious and remarkably simple solution to the problem: use delayed variable expansion!
I read your page about SET /P validation and info about command injection vulnerability (which I have also discovered during my coding).
My personal solution is to just use Delayed Expansion for that variable, and (according to my test) nothing will be executed or wrongly parsed, period.
I actually believe Delayed Expansion is the ultimate solution to this SET /P problem, and with it you don't need any echo-piped-to-findstr tricks.SETLOCAL EnableDelayedExpansion SET /P var="Type anything here:" ECHO .!var!. ECHO strip quotes: .!var:"=!. REM If you really want to reject the variable with any quotation mark... ECHO !var! | FIND """" >NUL && SET var= REM That's it.
Try it, try to insert code using ampersand, percent signs, exclamation marks, doublequotes... I could not make this code fail, so far.
Before processing the acquired input, it may be wise to reject input that contains "questionable" characters.
The following code will check if
%Input% contains doublequotes, and if so, wipe the input:
SET Input | FIND """" >NUL IF NOT ERRORLEVEL 1 SET Input=
Likewise, you can use
FIND "&" to test for the occurrence of ampersands, or
FINDSTR /R /C:"[&""|()]" to test for all "questionable" characters:
( SET Input | FINDSTR /R /C:"[&""|()]" IF NOT ERRORLEVEL 1 SET Input= ) >NUL
|Notes:||3:||Scott Sumner noted that, when redirecting
That is why, for
|4:||The shorter code
My advice is to keep it simple and safe, and use the
You don't always need "free" input, often you only want the user to select from a list of choices.
In that case, consider using CHOICE instead of
If you do need "free" input, either use the routine above, or a different scripting language, or my InputBox.exe (which removes doublequotes, ampersands and redirection characters from the input).
page last modified: 2016-09-19