Valid HTML 4.01 Transitional Valid CSS Valid SVG 1.0

Me, myself & IT

Bugs, Faults, Quirks, Undocumented Behaviour and Vulnerabilities in the Command Shell of Windows NT

Purpose
Reference
Fault № 0
Demonstration
Fault № 1
Demonstration
Background Information
Remediation
Fault № 2
Demonstration
Security Impact
Fault № 3
Demonstration
Fault № 4
Demonstration
Fault № 5
Falsification
Fault № 6
Demonstration
Fault № 7
Falsification
Fault № 8
Demonstration
Fault № 9
Demonstration
Fault № 10
Demonstration
Fault № 11
Demonstration
Work-Around
Fault № 12
Falsification
Fault № 13
Demonstration
Work-Around
Fault № 14
Falsification
Fault № 15
Demonstration
Security Impact
Work-Around
Fault № 16
Demonstration
Fault № 17
Falsification
Fault № 18
Falsification
Fault № 19
Demonstration
Fault № 20
Falsification
Fault № 21
Demonstration
Fault № 22
Demonstration
Fault № 23
Falsification
Fault № 24
Falsification
Fault № 24
Falsification
Security Impact
MSRC Case 71680
Fault № 26
Falsification
Security Impact
MSRC Case 59749
Remediation
Fault № 27
Falsification

Purpose

Present bugs, faults, quirks and vulnerabilities in the Command Processor Cmd.exe of Windows NT.

Reference

Windows Commands, 4/13/2018, https://download.microsoft.com/download/5/8/9/58911986-D4AD-4695-BF63-F734CD4DF8F2/ws-commands.pdf

Fault № 0

The documentation for the Command Processor Cmd.exe states:
Parameter Description
/a Formats internal command output to a pipe or a file as American National Standards Institute (ANSI).
/u Formats internal command output to a pipe or a file as Unicode.
Unicode here means of course Windows NT’s native UTF-16LE encoding.

Demonstration

Perform the following 3 simple steps to show the undocumented misbehaviour of the Command Processor Cmd.exe.
  1. Start the Command Processor Cmd.exe in an arbitrary, preferable empty directory, then execute the following command lines:

    1>comspec.bat "%COMSPEC%" /U /D /C ECHO PAUSE
    DIR comspec.bat
    TYPE comspec.bat
    CALL .\comspec.bat
    Note: the command lines can be copied and pasted as block into a Command Processor window.
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    04/27/2018  08:15 PM                14 comspec.bat
                   1 File(s)             14 bytes
                   0 Dir(s)    9,876,543,210 bytes free
    
      A U S E 
    
    
    'P' is not recognized as an internal or external command,
    operable program or batch file.
    OOPS: contrary to Microsoft’s own recommendation, the Command Processor writes no Byte Order Mark – the file comspec.bat contains 14 bytes in 7 UTF-16LE code units, the string PAUSE plus a CR/LF pair!

    OUCH¹: the internal Type command fails to display UTF-16LE encoded files without Byte Order Mark properly!

    OUCH²: (the internal Call command of) the Command Processor fails to execute batch scripts in Windows NT’s native UTF-16LE encoding!

  2. Open the batch script comspec.bat with NotePad.exe, press the Ctrl S keyboard shortcut to save it unchanged and exit the text editor:

    NOTEPAD.EXE comspec.bat
  3. Repeat the last 3 commands from step 1.:

    DIR comspec.bat
    TYPE comspec.bat
    CALL .\comspec.bat
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    04/27/2018  08:15 PM                16 comspec.bat
                   1 File(s)             16 bytes
                   0 Dir(s)    9,876,543,210 bytes free
    
    PAUSE
    
    '■P' is not recognized as an internal or external command,
    operable program or batch file.
    Note: NotePad.exe silently added a Byte Order Mark, the batch script comspec.bat now contains 16 bytes in 8 UTF-16LE code units.

    OUCH³: while the internal Type command displays the file comspec.bat properly now, (the internal Call command of) the Command Processor still fails to execute it!

Fault № 1

The documentation for the Command Processor Cmd.exe states:
Parameter Description
/d Disables execution of AutoRun commands.

[…]

Note: this feature enables (unprivileged) users to tamper with Logon batch scripts configured by their (privileged) administrators who are unaware of the pitfalls demonstrated below!

Demonstration

Perform the following 3 simple steps to show that AutoRun commands are executed whenever a batch script is started via one of the Win32 functions ShellExecute(). ShellExecuteEx() or WinExec(), for example from Windows graphical shell Explorer.exe per double-click or the Run entry of its Start Menu:
  1. Create the text file comspec.bat with the following content in an arbitrary, preferable empty directory:

    @REM Copyleft © 1997-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    @SET /P =%CMDCMDLINE%
    @EXIT /B
  2. Execute the batch script comspec.bat created in step 1. per double-click:

    C:\Windows\system32\cmd.exe /c ""C:\Users\Stefan\Desktop\comspec.bat" "
    Oops: Explorer.exe fails to provide the parameter /d to disable the execution of AutoRun commands when starting batch scripts!
  3. Display the file types associated with the file name extensions .bat and .cmd and their command line templates:

    ASSOC .bat
    FTYPE batfile
    ASSOC .cmd
    FTYPE cmdfile
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    .bat=batfile
    batfile="%1" %*
    .cmd=cmdfile
    cmdfile="%1" %*

Background Information

The Win32 functions ShellExecute() and ShellExecuteEx() retrieve these command line templates, replace the various tokens %‹digit›, %‹letter› and %* with file or path names and arguments, then feed the completed command line to one of the CreateProcess(), CreateProcessAsUser(), CreateProcessWithLogonW() or CreateProcessWithTokenW() functions.

The documentation for the CreateProcess() function states:

To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.
Due to the security vulnerability CVE-2014-0315 alias MS14-019 I discovered and reported at the MSRC about 11 years ago, which was fixed with security update 2922229 some months later, the following note was added several years later:

Important

The MSRC engineering team advises against this. See MS14-019 – Fixing a binary hijacking via .cmd or .bat file for more details.

CAVEAT: unfortunately the documentation but fails to show how to execute batch scripts in a safe and secure manner!

Remediation

To prevent the execution of AutoRun commands when starting batch scripts, change the command line template registered with the file types alias Programmatic Identifiers batfile and cmdfile associated with the file name extensions .bat and .cmd from its default value "%1" %* to C:\Windows\System32\Cmd.exe /D /C "%1" %*:
FTYPE batfile=%COMSPEC% /D /C "%1" %*
FTYPE cmdfile=%COMSPEC% /D /C "%1" %*
Note: the internal Ftype command must be executed with administrative access rights.

If you still have not abandonded the bad habit of using the Protected Administrator account created during Windows Setup you also need to change the command line templates registered for the RunAs verb:

REGEDIT4

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\batfile\Shell\Open\Command]
@="C:\\Windows\\System32\\Cmd.exe /D /C \"%L\" %*"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\batfile\Shell\RunAs\Command]
@="C:\\Windows\\System32\\Cmd.exe /D /C \"%L\" %*"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\cmdfile\Shell\Open\Command]
@="C:\\Windows\\System32\\Cmd.exe /D /C \"%L\" %*"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\cmdfile\Shell\RunAs\Command]
@="C:\\Windows\\System32\\Cmd.exe /D /C \"%L\" %*"
CAVEAT: thanks to the Merged View of HKEY_CLASSES_ROOT introduced with Windows 2000 this remediation but fails if the unnamed default registry entry with the default command line template is present in the corresponding registry key HKEY_CURRENT_USER\Software\Classes\‹file type›\Shell\‹verb›\Command of a user account!

Fault № 2

The TechNet article The Windows NT Command Shell tells:
When the command shell processes a pipe (|) symbol, it actually runs both commands specified simultaneously.
Note: internal commands except Rem with an argument other than /?, i.e. Assoc, Break, Call, Cd, Chdir, Cls, Color, Copy, Date, Del, Dir, Dpath (undocumented), Echo, Endlocal, Erase, Exit, For, Ftype, Goto, If, Keys (undocumented), Md, Mkdir, Mklink, Move, Path, Pause, Popd, Prompt, Pushd, Rd, Ren, Rename, Rmdir, Set, Setlocal, Shift, Start, Time, Title, Type, Ver, Verify and Vol, are executed in a child Cmd.exe process!

The TechNet article Using command redirection operators states too:

Using the pipe operator (|)

The pipe operator (|) takes the output (by default, STDOUT) of one command and directs it into the input (by default, STDIN) of another command. For example, the following command sorts a directory:

dir | sort

In this example, both commands start simultaneously, but then the sort command pauses until it receives the dir command's output. The sort command uses the dir command's output as its input, and then sends its output to handle 1 (that is, STDOUT).

The documentation for the internal Rem command of the Command Processor states:

Demonstration

Perform the following 6 (plus 1 optional) simple steps to show undocumented (mis)behaviour and a more than 31 (in words: thirty-one) year old bug that crashes the Command Processor.
  1. Start the Command Processor, then execute the following command lines to show proper but undocumented behaviour:

    ECHO %CMDCMDLINE^% | MORE.COM
    1>&2 ECHO %CMDCMDLINE^% | ECHO %CMDCMDLINE^%
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    C:\windows\system32\cmd.exe  /S /D /c" ECHO %CMDCMDLINE% "
    C:\windows\system32\cmd.exe  /S /D /c" ECHO %CMDCMDLINE% 1>&2"
    C:\windows\system32\cmd.exe  /S /D /c" ECHO %CMDCMDLINE%"
    Note: the Command Processor executes each internal command of these pipelines in a separate child Cmd.exe process – fortunately with the parameter /D to disable the execution of AutoRun commands!
  2. Execute the following command lines to show more proper behaviour:

    1>&2 ECHO %CMDCMDLINE^% | REM
    ECHO %CMDCMDLINE^% | REM
    ECHO %CMDCMDLINE^% | REM /?
    C:\Windows\system32\cmd.exe  /S /D /c" ECHO %CMDCMDLINE% 1>&2"
    The process tried to write to a nonexistent pipe.
    Records comments (remarks) in a batch file or CONFIG.SYS.
    
    REM [comment]
    Note: output redirection works with the internal Rem command on the right hand side of a pipeline.
  3. Execute the following command lines to show undocumented behaviour:

    REM /? | ECHO %CMDCMDLINE^%
    REM | ECHO 1>&2 %CMDCMDLINE^%
    C:\Windows\system32\cmd.exe  /S /D /c" ECHO %CMDCMDLINE%"
    Note: redirection characters can’t be used with the internal Rem command and an argument other than /? outside of batch scripts too – REM | … is not recognised as a pipeline.
  4. Execute the following command lines to show the first undocumented misbehaviour:

    SET COMSPEC=%SystemRoot%\System32\Reg.exe
    DPATH | KEYS
    SET COMSPEC=.
    BREAK | EXIT
    SET COMSPEC=NUL:
    ECHO | REM
    Note: the Registry Console Tool Reg.exe is only (ab)used as canary here.
    ERROR: Invalid Argument/Option - '/S'.
    Type "REG /?" for usage.
    ERROR: Invalid Argument/Option - '/S'.
    Type "REG /?" for usage.
    '.' is not recognized as an internal or external command,
    operable program or batch file.
    'NUL:' is not recognized as an internal or external command,
    operable program or batch file.
    OUCH¹: the Command Processor evaluates the (user-controlled) environment variable COMSPEC and tries to execute the file, directory or device it points to as child process of itself!

    CAVEAT: (ab)using an environment variable to locate an executable file is a well-known weakness, documented as CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') and CWE-73: External Control of File Name or Path in the CWE; it allows well-known attacks like CAPEC-13: Subverting Environment Variable Values documented in the CAPEC.

    NOTE: properly implemented, the Command Processor would fetch its own path name with the Win32 function GetModuleFileName() instead to (ab)use the value of a user-controlled environment variable!

  5. Execute the following command lines to show the second undocumented misbehaviour:

    SET COMSPEC=%RANDOM%
    SET PATHEXT
    DPATH | KEYS
    SET PATHEXT=NUL:
    BREAK | EXIT
    SET PATHEXT=%SystemRoot%\System32\Reg.exe
    ECHO | REM
    SET PATHEXT=
    SHIFT | VER
    PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    '.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC' is not recognized as an internal or external command,
    operable program or batch file.
    'NUL:' is not recognized as an internal or external command,
    operable program or batch file.
    'C:\Windows\System32\Reg.exe' is not recognized as an internal or external command,
    operable program or batch file.
    '' is not recognized as an internal or external command,
    operable program or batch file.
    OUCH²: if the environment variable COMSPEC points to a non-existent path, the Command Processor tries to execute the file, directory or device the (user-controlled) environment variable PATHEXT points to as child process of itself, even if this environment variable is missing – and fails always, independent of its value!
  6. Finally execute the following command lines to show the bug:

    SET COMSPEC=
    DPATH | KEYS
    OUCH³: the Command Processor crashes with an access violation exception 0xC0000005 reading address NULL when an arbitrary internal command is executed in a pipeline while the environment variable COMSPEC is missing!

    CAVEAT: the crash induced by the absence of the environment variable COMSPEC is yet another well-known weakness, documented as CWE-248: Uncaught Exception and CWE-476: NULL Pointer Dereference in the CWE.

  7. (Optional) If you have the Debugging Tools for Windows installed, execute the Command Processor under the debugger:

    CDB.EXE /C g;q /G /g "%COMSPEC%" /D /C SET COMSPEC=^& EXIT ^| EXIT
    Note: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
    Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    CommandLine: "C:\Windows\system32\cmd.exe" /D /C SET COMSPEC=& EXIT | EXIT
    Symbol search path is: srv*
    Executable search path is: 
    ModLoad: 4ac70000 4acbc000   cmd.exe
    ModLoad: 779c0000 77b40000   ntdll.dll
    ModLoad: 75610000 75720000   C:\Windows\syswow64\kernel32.dll
    ModLoad: 752d0000 75317000   C:\Windows\syswow64\KERNELBASE.dll
    ModLoad: 75540000 755e1000   C:\Windows\syswow64\ADVAPI32.DLL
    ModLoad: 757a0000 7584c000   C:\Windows\syswow64\msvcrt.dll
    ModLoad: 77420000 77439000   C:\Windows\SysWOW64\sechost.dll
    ModLoad: 77300000 773f0000   C:\Windows\syswow64\RPCRT4.dll
    ModLoad: 750e0000 75140000   C:\Windows\syswow64\SspiCli.dll
    ModLoad: 750d0000 750dc000   C:\Windows\syswow64\CRYPTBASE.dll
    ModLoad: 73d60000 73d67000   C:\Windows\SysWOW64\WINBRAND.dll
    ModLoad: 76f60000 77060000   C:\Windows\syswow64\USER32.dll
    ModLoad: 764f0000 76580000   C:\Windows\syswow64\GDI32.dll
    ModLoad: 76580000 7658a000   C:\Windows\syswow64\LPK.dll
    ModLoad: 76690000 7672d000   C:\Windows\syswow64\USP10.dll
    ModLoad: 76630000 76690000   C:\Windows\SysWOW64\IMM32.DLL
    ModLoad: 75470000 7553e000   C:\Windows\syswow64\MSCTF.dll
    (2f60.32bc): Access violation - code c0000005 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=00000000 ebx=00002000 ecx=00551210 edx=00000002 esi=005511a8 edi=005511a8
    eip=4ac7247d esp=002dfa00 ebp=002dfa0c iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
    *** ERROR: Module load completed but symbols could not be loaded for cmd.exe
    cmd+0x247d:
    4ac7247d 668b08          mov     cx,word ptr [eax]        ds:002b:00000000=????
    0:000:x86> cdb: Reading initial command 'g;q'
    (2f60.32bc): Access violation - code c0000005 (!!! second chance !!!)
    quit:

Security Impact

The (unintended) execution of a (bogus) application determined by the value of a (user-controlled) environment variable like COMSPEC is a well-known weakness, documented as CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') and CWE-73: External Control of File Name or Path in the CWE; it allows well-known attacks like CAPEC-13: Subverting Environment Variable Values documented in the CAPEC.

The crash induced by the absence of the (user-controlled) environment variable COMSPEC is yet another well-known weakness, documented as CWE-248: Uncaught Exception and CWE-476: NULL Pointer Dereference in the CWE.

This at least 31 year old bug (really: absolute beginner’s programming error) can trivially be (ab)used to conduct a denial of service attack against the Command Processor and crash it upon launch, thus disabling its use for single or all users of a machine:

REGEDIT4

[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"AutoRun"="SET COMSPEC=& EXIT | EXIT"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
"AutoRun"="SET COMSPEC=& EXIT | EXIT"

Fault № 3

Pairs of the characters % and ! are used for (delayed) expansion of environment variables at the (interactive) command prompt as well as in batch scripts – where only a single % is used for parameter expansion.

Demonstration

Perform the following 2 simple steps to show a bug.
  1. Create the text file com!spec.bat with the following content in an arbitrary, preferable empty directory:

    REM Copyleft © 1997-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    SETLOCAL DISABLEDELAYEDEXPANSION
    CALL :com%spec
    SETLOCAL ENABLEDELAYEDEXPANSION
    :comspec
    DIR "%~f0"
    ECHO comspec%
    ECHO com%spec
    ECHO %comspec
    ECHO !%!
    ECHO comspec!
    %ECHO com!spec
    ECHO !comspec
    MKDIR !!
    ECHO% !
    RMDIR !!
    !PAUSE
    EXIT /B
    Note: the single exclamation mark (or a single percent sign) can be placed anywhere in the filename!
  2. Start the batch script com!spec.bat created in step 1. per double-click:

    REM Copyleft © 1997-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de>
    SETLOCAL DISABLEDELAYEDEXPANSION
    CALL :comspec
    DIR "C:\Users\Stefan\Desktop\com!spec.bat"
    
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    04/27/2018  08:15 PM               320 com!spec.bat
                   1 File(s)            320 bytes
                   0 Dir(s)    9,876,543,210 bytes free
    
    ECHO comspec
    comspec
    ECHO comspec
    comspec
    ECHO comspec
    comspec
    ECHO !!
    !!
    ECHO comspec!
    comspec!
    ECHO com!spec
    com!spec
    ECHO !comspec
    !comspec
    MKDIR !!
    ECHO !
    !
    RMDIR !!
    !PAUSE
    '!PAUSE' is not recognized as an internal or external command,
    operable program or batch file.
    EXIT /B
    SETLOCAL ENABLEDELAYEDEXPANSION
    DIR "C:\Users\Stefan\Desktop\com!spec.bat"
    
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    File Not Found
    
    ECHO comspec
    comspec
    ECHO comspec
    comspec
    ECHO comspec
    comspec
    ECHO !!
    ECHO is on.
    ECHO comspec!
    comspec
    ECHO com!spec
    comspec
    ECHO !comspec
    comspec
    MKDIR !!
    The syntax of the command is incorrect.
    ECHO !
    ECHO is on.
    RMDIR !!
    The syntax of the command is incorrect.
    !PAUSE
    Press any key to continue . . .
    EXIT /B
    OUCH¹: in batch scripts, the Command Processor strips single percent signs from command lines before they are echoed and executed!

    OUCH²: when delayed environment variable expansion is enabled in batch scripts, the Command Processor strips all exclamation marks which don’t enclose the name of an environment variable from command lines after they are echoed and before they are executed!

Fault № 4

The TechNet article The Windows NT Command Shell states under the heading Command Search Sequence:
When a command is submitted for execution (either by typing or as part of a script), the shell performs the following actions:
  1. All parameter and environment variable references are resolved (see chapter 3).

  2. Compound commands are split into individual commands and each is then individually processed according to the following steps (see the section "Running Multiple Commands" for details of compound commands). Continuation lines are also processed at this step.

  3. The command is split into the command name and any arguments.

[…]
Note: unfortunately there’s no explicit statement when input and output redirection are processed.

Demonstration

  1. Create the text file comspec.bat with the following content in an arbitrary, preferable empty directory:

    @REM Copyleft © 1997-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    @SETLOCAL ENABLEDELAYEDEXPANSION
    @ECHO %%0 com %~0 spec
    @ECHO %%1 com %~1 spec
    @ECHO %%2 com %~2 spec
    @ECHO %%3 com %~3 spec
    @ECHO %%4 com %~4 spec
    @ECHO %%5 com %~5 spec
    @ECHO %%6 com %~6 spec
    @ECHO %%7 com %~7 spec
    @ECHO %%8 com %~8 spec
    @ECHO %%9 com %~9 spec
    @EXIT /B
  2. Start the Command Processor Cmd.exe, then execute the batch script comspec.bat created in step 1. via the following command line with 9 properly quoted arguments which contain special characters:

    .\comspec.bat "!!" "&" "&&" "0<." "1>." "^" "^^" "|" "||"
    %0 com .\comspec.bat spec
    %1 com  spec
    %2 com
    'spec' is not recognized as an internal or external command,
    operable program or batch file.
    %3 com
    'spec' is not recognized as an internal or external command,
    operable program or batch file.
    Access denied
    Access denied
    %6 com  spec
    %7 com ^ spec
    'spec' is not recognized as an internal or external command,
    operable program or batch file.
    OOPS: input and output redirection are processed after parameter and environment variable expansion!

    OUCH¹: the Command Processor fails to execute the last ECHO command – its output %9 com is missing!

  3. Execute the batch script comspec.bat created in step 1. a second time with the same 9 arguments, now unquoted and their special characters properly escaped:

    .\comspec.bat ^!^! ^& ^&^& 0^<. 1^>. ^^ ^^^^ ^| ^|^|
    %0 com .\comspec.bat spec
    %1 com  spec
    %2 com
    'spec' is not recognized as an internal or external command,
    operable program or batch file.
    %3 com
    'spec' is not recognized as an internal or external command,
    operable program or batch file.
    Access denied
    Access denied
    %6 com  spec
    'spec' is not recognized as an internal or external command,
    operable program or batch file.
    OUCH²: now the eighth and tenth ECHO fail execution!

Fault № 5

The documentation for the internal Assoc command of the Command Processor states:
Displays or modifies file name extension associations. […]
assoc [<.ext>[=[<FileType>]]]

[…]

Remarks

Falsification

Perform the following 2 simple steps to prove the highlighted statement of the documentation cited above wrong and also show undocumented behaviour.
  1. Start the Command Processor Cmd.exe, then execute the following command lines to display some non-associations:

    ASSOC batfile
    ASSOC batfile\DefaultIcon
    ASSOC batfile\Shell\Open\Command
    ASSOC CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32
    batfile=Windows Batch File
    batfile\DefaultIcon=%SystemRoot%\System32\imageres.dll,-68
    batfile\Shell\Open\Command="%1" %*
    CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32=%ProgramFiles%\Windows Defender\MpOav.dll
    Oops¹: the internal Assoc command reads not just associations, but also the values of unnamed default registry entries in arbitrary registry keys of the HKEY_LOCAL_MACHINE\SOFTWARE\Classes registry branch.
  2. Start the Command Processor Cmd.exe with administrative access rights, then execute the following command lines to write, display and remove some non-associations, also prove the highlighted statement of the documentation cited above wrong:

    ASSOC CLSID
    ASSOC CLSID=%RANDOM%
    ASSOC CLSID
    ASSOC CLSID=
    ASSOC CLSID
    File association not found for extension CLSID
    CLSID=12345
    CLSID=12345
    File association not found for extension CLSID
    Oops²: the internal Assoc command reads and writes not just associations, but also the values of unnamed default registry entries in arbitrary registry keys in the HKEY_LOCAL_MACHINE\SOFTWARE\Classes registry branch.

    Oops³: contrary to the highlighted statement of its documentation cited above, the internal Assoc command requires no white space after the equal sign to remove a value!

Fault № 6

The documentations for the internal Call, Del alias Erase, Move, Ren alias Rename and Type commands of the Command Processor state:
Calls one batch program from another without stopping the parent batch program. […]
Deletes one or more files. This command is the same as the erase command.
Moves one or more files from one directory to another directory.
Renames files or directories. This command is the same as the rename command.
Displays the contents of a text file. Use the type command to view a text file without modifying it.

Demonstration

Perform the following 7 simple steps to show their braindead misbehaviour:
  1. Start the Command Processor Cmd.exe, then execute the following command lines to create the subdirectory ComSpec in the public TMP directory of the system and remove it first, and to create a file ComSpec there and (try to) erase it too:

    MKDIR "%SystemRoot%\Temp\ComSpec"
    RMDIR "%SystemRoot%\Temp\ComSpec"
    COPY "%COMSPEC%" "%SystemRoot%\Temp\ComSpec"
    ERASE "%SystemRoot%\Temp\ComSpec"
    Note: the command lines can be copied and pasted as block into a Command Processor window.
            1 file(s) copied.
    The system cannot find the file C:\Windows\Temp\ComSpec.
    OOPS: WTF?
  2. Verify that the file %SystemRoot%\Temp\ComSpec created in step 1. really exists, then overwrite it and (try to) display its content, (try to) execute it, (try to) rename it and (try to) move it into your current (working) directory:

    MKDIR "%SystemRoot%\Temp\ComSpec"
    COPY /Y NUL: "%SystemRoot%\Temp\ComSpec"
    TYPE "%SystemRoot%\Temp\ComSpec"
    CALL "%SystemRoot%\Temp\ComSpec"
    RENAME "%SystemRoot%\Temp\ComSpec" COMSPEC
    MOVE "%SystemRoot%\Temp\ComSpec"
    A subdirectory or file C:\Windows\Temp\ComSpec already exists.
            1 file(s) copied.
    Access denied
    Access denied
    Access denied
    Access denied
    OUCH: most obviously the Command Processor performs a (superfluous) access check on files!
  3. Display the access permissions of the file %SystemRoot%\Temp\ComSpec created in step 1.:

    ICACLS.EXE "%SystemRoot%\Temp\ComSpec"
    C:\Windows\Temp\ComSpec NT AUTHORITY\SYSTEM:(I)(F)
                            AMNESIAC\Stefan:(I)(F)
                            BUILTIN\Administrators:(I)(F)
    
    Successfully processed 1 files; Failed processing 0 files
    Note: your user account %COMPUTERNAME%\%USERNAME% has full access to this file, inherited from the parent directory!
  4. Create the text file comspec.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	HANDLE	hComSpec;
    	WCHAR	szComSpec[MAX_PATH];
    	DWORD	dwComSpec = GetWindowsDirectory(szComSpec, sizeof(szComSpec) / sizeof(*szComSpec));
    	DWORD	dwError = ERROR_SUCCESS;
    
    	if (dwComSpec == 0UL)
    		dwError = GetLastError();
    	else
    	{
    		memcpy(szComSpec + dwComSpec, L"\\Temp\\ComSpec", sizeof(L"\\Temp\\ComSpec"));
    #ifndef COMSPEC
    		if (!DeleteFile(szComSpec))
    			dwError = GetLastError();
    #else
    		hComSpec = CreateFile(szComSpec,
    		                      DELETE,
    		                      FILE_SHARE_DELETE,
    		                      (LPSECURITY_ATTRIBUTES) NULL,
    		                      OPEN_EXISTING,
    		                      FILE_FLAG_DELETE_ON_CLOSE,
    		                      (HANDLE) NULL);
    
    		if (hComSpec == INVALID_HANDLE_VALUE)
    			dwError = GetLastError();
    		else
    			if (!CloseHandle(hComSpec))
    				dwError = GetLastError();
    #endif
    	}
    
    	ExitProcess(dwError);
    }
  5. Compile and link the source file comspec.c created in step 4.:

    SET CL=/Oi /W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE comspec.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    comspec.c
    comspec.c(12) : warning C4101: 'hComSpec' : unreferenced local variable
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:comspec.exe
    comspec.obj
    kernel32.lib
  6. Execute the console application comspec.exe built in step 5. and evaluate its exit code:

    .\comspec.exe
    CERTUTIL.EXE /ERROR %ERRORLEVEL%
    0x0 (WIN32: 0 ERROR_SUCCESS) -- 0 (0)
    Error message text: The operation completed successfully.
    CertUtil: -error command completed successfully.
  7. Finally verify that the file %SystemRoot%\Temp\ComSpec created in step 1. doesn’t exist any more:

    MKDIR "%SystemRoot%\Temp\ComSpec"
    RMDIR "%SystemRoot%\Temp\ComSpec"

Fault № 7

The documentation for the internal Call command of the Command Processor states:
Calls one batch program from another without stopping the parent batch program. The call command accepts labels as the target of the call.

Note

Call has no effect at the command prompt when it is used outside of a script or batch file.

[…]

Remarks

[…]

The documentation for the internal Goto command of the Command Processor states:
Directs cmd.exe to a labeled line in a batch program. […]

Remarks

Falsification

Perform the following 7 simple steps to prove both highlighted statements of the documentation cited above wrong and show the true undocumented (mis)behaviour.
  1. Create the text file comspec.txt with the following content in an arbitrary, preferable empty directory:

    MZ
  2. Create the text file comspec.cmd with the following content in the same directory:

    CALL(
    CALL:EOF
    CALL;
    CALL=
  3. Start the Command Processor Cmd.exe in the directory where you created the text files comspec.txt and comspec.cmd, then execute the following single command line:

    CALL comspec.cmd
    CALL(
    CALL:EOF
    The system cannot find the batch label specified - EOF
    CALL;
    CALL=
    OOPS¹: the internal Call command has no effect in batch scripts when followed by an opening (left) parenthesis, a semicolon or an equal sign – the Command Processor even fails to display an error message!

    OOPS²: unlike the internal Goto command, the internal Call command fails to support the (virtual) label :EOF!

  4. Execute the following single command line:

    CALL :EOF
    Invalid attempt to call batch label outside of batch script.
    OOPS³: the internal Call command yields at least an error message when used at the (interactive) command prompt.
  5. Execute another single command line: [Screen shot of message box from 'Windows Script Host' on Windows 7]

    CALL winrm.vbs

    OUCH¹: contrary to both highlighted statements of its documentation cited above, the internal Call command has effect at the (interactive) command prompt outside of a batch script and calls not just batch programs – it acts similar to the internal Start command and evaluates the association of file types to file name extensions as well as the environment variable PATH!
  6. Display the file type associated with the file name extension .vbs and its command line template:

    ASSOC .vbs
    FTYPE VBSFile
    .vbs=VBSFile
    VBSFile="%SystemRoot%\System32\WScript.exe" "%1" %*
  7. Execute the following command lines, then close the windows of Editor and WordPad as well as the message box:

    [Screen shot of message box from module loader on Windows 7]

    CALL(winrm.vbs
    ASSOC .ini
    FTYPE inifile
    CALL;system.ini
    ASSOC .rtf
    FTYPE rtffile
    CALL=license.rtf
    ASSOC .txt
    FTYPE txtfile
    CALL comspec.txt

    Note: the command lines can be copied and pasted as block into a Command Processor window.
    .ini=inifile
    inifile=%SystemRoot%\system32\NOTEPAD.EXE %1
    .rtf=rtffile
    rtffile="%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE" "%1"
    .txt=txtfile
    txtfile=%SystemRoot%\system32\NOTEPAD.EXE %1
    This version of C:\Users\Stefan\Desktop\comspec.txt is not compatible with the version of Windows you're running. Check your computer's system information to see whether you need an x86 (32-bit) or x64 (64-bit) version of the program, and then contact the software publisher.
    OOPS⁴: while CALL(‹path name›.‹extension› has no effect at all, both CALL;‹path name›.‹extension› and CALL=‹path name›.‹extension› are equivalent to CALL ‹path name›.‹extension› – semicolon and equal sign are valid separators too!

    OUCH²: CALL ‹path name›.‹extension› executes tries to execute a file that contains the character string MZ at offset 0 as DOS or Windows application!

Fault № 8

The documentation for the internal Dir command of the Command Processor states:
Displays a list of a directory's files and subdirectories. […]
dir [<Drive>:][<Path>][<FileName>] [...] [/p] [/q] [/w] [/d] [/a[[:]<Attributes>]][/o[[:]<SortOrder>]] [/t[[:]<TimeField>]] [/s] [/b] [/l] [/n] [/x] [/c] [/4]

[…]

Parameter Description
/s Lists every occurrence of the specified file name within the specified directory and all subdirectories.

Demonstration

Perform the following 3 simple steps to show the (mis)behaviour.
  1. Start the Command Processor Cmd.exe, then execute the following command line to display the help text of its internal Dir command:

    DIR /?
    Displays a list of files and subdirectories in a directory.
    
    DIR [drive:][path][filename] [/A[[:]attributes]] [/B] [/C] [/D] [/L] [/N]
      [/O[[:]sortorder]] [/P] [/Q] [/S] [/T[[:]timefield]] [/W] [/X] [/4]
    
      [drive:][path][filename]
                  Specifies drive, directory, and/or files to list.
    
      /A          Displays files with specified attributes.
      attributes   D  Directories                R  Read-only files
                   H  Hidden files               A  Files ready for archiving
                   S  System files               -  Prefix meaning not
      /B          Uses bare format (no heading information or summary).
      /C          Display the thousand separator in file sizes.  This is the
                  default.  Use /-C to disable display of separator.
      /D          Same as wide but files are list sorted by column.
      /L          Uses lowercase.
      /N          New long list format where filenames are on the far right.
      /O          List by files in sorted order.
      sortorder    N  By name (alphabetic)       S  By size (smallest first)
                   E  By extension (alphabetic)  D  By date/time (oldest first)
                   G  Group directories first    -  Prefix to reverse order
      /P          Pauses after each screenful of information.
      /Q          Display the owner of the file.
      /R          Display alternate data streams of the file.
      /S          Displays files in specified directory and all subdirectories.
      /T          Controls which time field displayed or used for sorting
      timefield   C  Creation
                  A  Last Access
                  W  Last Written
      /W          Uses wide list format.
      /X          This displays the short names generated for non-8dot3 file
                  names.  The format is that of /N with the short name inserted
                  before the long name. If no short name is present, blanks are
                  displayed in its place.
      /4          Displays four-digit years
    
    Switches may be preset in the DIRCMD environment variable.  Override
    preset switches by prefixing any switch with - (hyphen)--for example, /-W.
    OUCH¹: the /R switch and its description is missing in the documentation cited above!
  2. Execute the following command lines to create a subdirectory ComSpec.one\ComSpec.two in an arbitrary, preferable empty directory, append the Alternate Data Stream :comspec to the first subdirectory and list both subdirectories using the /R switch:

    MKDIR ComSpec.one\ComSpec.two
    1>ComSpec.one:comspec ECHO
    DIR /R ComSpec.one
    DIR /R ComSpec.one\ComSpec.two
    Note: the command lines can be copied and pasted as block into a Command Processor window.
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop\ComSpec.one
    
    04/27/2018  08:15 PM    <DIR>          .
                                        13 .:comspec:$DATA
    04/27/2018  08:15 PM    <DIR>          ..
    04/27/2018  08:15 PM    <DIR>          ComSpec.two
                   0 File(s)              0 bytes
                   3 Dir(s)    9,876,543,210 bytes free
    
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop\ComSpec.one\ComSpec.two
    
    04/27/2018  08:15 PM    <DIR>          .
    04/27/2018  08:15 PM    <DIR>          ..
                                        13 ..:comspec:$DATA
                   0 File(s)              0 bytes
                   2 Dir(s)    9,876,543,210 bytes free
    OUCH²: with its /R switch, the internal Dir command enumerates Alternate Data Streams not only on regular directories, but also on the pseudo directories . and .. synthesised by the Win32 functions FindFirstFile() and FindNextFile()!
  3. Finally list the current directory using the switches /R and /S together and clean up:

    DIR /R /S
    RMDIR ComSpec.one\ComSpec.two
    RMDIR ComSpec.one
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    04/27/2018  08:15 PM    <DIR>          .
    04/27/2018  08:15 PM    <DIR>          ..
    04/27/2018  08:15 PM    <DIR>          ComSpec.one
                                        13 ComSpec.one:comspec:$DATA
                   0 File(s)              0 bytes
    
     Directory of C:\Users\Stefan\Desktop\ComSpec.one
    
    04/27/2018  08:15 PM    <DIR>          .
                                        13 .:comspec:$DATA
    04/27/2018  08:15 PM    <DIR>          ..
    04/27/2018  08:15 PM    <DIR>          ComSpec.two
                   0 File(s)              0 bytes
    
     Directory of C:\Users\Stefan\Desktop\ComSpec.one\ComSpec.two
    
    04/27/2018  08:15 PM    <DIR>          .
    04/27/2018  08:15 PM    <DIR>          ..
                                        13 ..:comspec:$DATA
                   0 File(s)              0 bytes
    
         Total Files Listed:
                   0 File(s)              0 bytes
                   2 Dir(s)    9,876,543,210 bytes free
    OUCH³: with its switches /R and /S used together, the internal Dir command enumerates Alternate Data Streams on directories twice or even thrice!

Fault № 9

Like the environment variable PATH is used to locate executable files, standard input redirection and the internal Type command of the Command Processor use the undocumented environment variable DPATH to locate (data) files – it is queried and set with the also undocumented internal Dpath command, similar to the internal Path command.

Demonstration

Perform the following 2 simple steps to show the (mis)behaviour.
  1. Start the Command Processor Cmd.exe, then execute the following command line to display the help text of its undocumented internal DPATH command:

    DPATH /?
    Allows programs to open data files in specified directories as if they were
    in the current directory.
    
    APPEND [[drive:]path[;...]] [/X[:ON | :OFF]] [/PATH:ON | /PATH:OFF] [/E]
    APPEND ;
    
      [drive:]path Specifies a drive and directory to append.
      /X:ON        Applies appended directories to file searches and
                   application execution.
      /X:OFF       Applies appended directories only to requests to open files.
                   /X:OFF is the default setting.
      /PATH:ON     Applies the appended directories to file requests that already
                   specify a path.  /PATH:ON is the default setting.
      /PATH:OFF    Turns off the effect of /PATH:ON.
      /E           Stores a copy of the appended directory list in an environment
                   variable named APPEND.  /E may be used only the first time
                   you use APPEND after starting up your system.
    
    Type APPEND ; to clear the appended directory list.
    Type APPEND without parameters to display the appended directory list.
    OOPS: the Command Processor is confused – an (external) APPEND.EXE command is not shipped with 64-bit editions of Windows!

    OUCH: the internal Dpath command has a single argument, either a single semicolon or a list of semicolon-separated directory names!

  2. Execute the following command lines to show the true behaviour:

    DPATH
    SET DPATH
    TYPE system.ini
    CLIP.EXE 0<system.ini
    DPATH %SystemRoot%
    SET DPATH
    TYPE system.ini
    CLIP.EXE 0<system.ini
    MORE.COM system.ini
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    DPATH=(null)
    
    Environment variable DPATH not defined
    
    The system cannot find the file system.ini.
    
    The system cannot find the file specified.
    
    DPATH=C:\Windows
    
    ; for 16-bit app support
    [386Enh]
    woafont=dosapp.fon
    EGA80WOA.FON=EGA80WOA.FON
    EGA40WOA.FON=EGA40WOA.FON
    CGA80WOA.FON=CGA80WOA.FON
    CGA40WOA.FON=CGA40WOA.FON
    
    [drivers]
    wave=mmdrv.dll
    timer=timer.drv
    
    [mci]
    
    Cannot access file C:\Users\Stefan\Desktop\system.ini

Fault № 10

The documentation for the internal Echo command of the Command Processor specifies:
Remarks

[…]

[…]

Examples

[…]

To echo a blank line on the screen, type:

echo.

Demonstration

Perform the following 2 simple steps to show undocumented (mis)behaviour.
  1. Start the Command Processor Cmd.exe, then execute the following command lines to echo two blank lines:

    ECHO.
    ECHO;
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    
    
    
  2. Execute the following command lines to echo six more blank lines:

    ECHO(
    ECHO+
    ECHO,
    ECHO/
    ECHO;
    ECHO=
    
    
    
    
    
    
    

Fault № 11

The documentation for the internal Echo command of the Command Processor states:
Remarks

[…]

The documentation for the internal If command of the Command Processor states:
Remarks

[…]

Examples

[…]

To delete the file Product.dat from the current directory or display a message if Product.dat is not found, type the following lines in a batch file:

IF EXIST Product.dat (
del Product.dat
) ELSE (
echo The Product.dat file is missing.
)

Note

These lines can be combined into a single line as follows:

IF EXIST Product.dat (del Product.dat) ELSE (echo The Product.dat file is missing.)
OUCH⁰: the example proves the highlighted statement wrong – the ELSE clause is on its own line, separate from the IF!

Demonstration

Perform the following 5 simple steps to prove the highlighted part of the documentation cited above wrong and show a stupid, about 30 (in words: thirty) year old bug in the Command Processor.
  1. Start the Command Processor Cmd.exe, then execute the ECHO command line from the documentation cited above with only the inner closing parenthesis escaped:

    (ECHO This is (also^) correct)
    This is (also) correct
    Oops: contrary to the highlighted statement of the first documentation cited above, the inner opening parenthesis doesn’t need to be escaped!
  2. Execute the following ECHO command lines referencing an environment variable with both, one or no inner parentheses escaped:

    (ECHO %CommonProgramFiles^(x86^)% but fails to evaluate the environment variable!)
    (ECHO %CommonProgramFiles(x86^)% also fails to evaluate the environment variable!)
    (ECHO %CommonProgramFiles(x86)% fails miserably!)
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    %CommonProgramFiles(x86)% but fails to evaluate the environment variable!
    %CommonProgramFiles(x86)% also fails to evaluate the environment variable!
    "\Common" cannot be processed syntactically at this point.
    OUCH¹: contrary to the highlighted statement of the first documentation cited above, this example but fails!

    OUCH²: the last command line fails due to the parentheses in the expanded environment variable – the Command Processor expands environment variables first, before parsing the command line!

  3. Execute an IF … ELSE … command block that evaluates the (system) environment variable ProgramFiles(x86) in its then clause:

    IF DEFINED ProgramFiles(x86) (
    ECHO %ProgramFiles(x86)% comspec
    ) ELSE (
    ECHO %ProgramFiles%
    )
    "comspec)" cannot be processed syntactically at this point.
  4. Combine the command block from step 3. into a single command line:

    IF DEFINED ProgramFiles(x86) (ECHO %ProgramFiles(x86)% comspec) ELSE (ECHO %ProgramFiles%)
    "comspec)" cannot be processed syntactically at this point.
  5. Invert the condition of the IF clause and swap the then clause with the ELSE clause:

    IF NOT DEFINED ProgramFiles(x86) (ECHO %ProgramFiles%) ELSE (ECHO %ProgramFiles(x86)% comspec)
    "comspec)" cannot be processed syntactically at this point.

Work-Around

  1. Enable delayed (environment variable) expansion and repeat step 2. with exclamation marks instead of the percent signs:

    SETLOCAL ENABLEDELAYEDEXPANSION
    (ECHO !CommonProgramFiles^(x86^)! is evaluated now!)
    (ECHO !CommonProgramFiles(x86^)! is evaluated now!)
    (ECHO !CommonProgramFiles(x86)! fails but miserably too!)
    C:\Program Files (x86)\Common Files is evaluated now!
    C:\Program Files (x86)\Common Files is evaluated now!
    "!" cannot be processed syntactically at this point.
    OUCH³: the parser is severely broken – parentheses inside a pair of exclamation marks which surround the name of an environment variable don’t terminate an outer block!
  2. Drop the (here superfluous) parentheses around the single command of the ELSE clause from step 5.:

    IF NOT DEFINED ProgramFiles(x86) (ECHO %ProgramFiles%) ELSE ECHO %ProgramFiles(x86)%
    C:\Program Files (x86)
  3. Enable delayed (environment variable) expansion and escape the closing parenthesis:

    SETLOCAL ENABLEDELAYEDEXPANSION
    IF DEFINED ProgramFiles(x86) (ECHO !ProgramFiles(x86^)!) ELSE (ECHO %ProgramFiles%)
    C:\Program Files (x86)

Fault № 12

The documentation for the internal For command of the Command Processor states:
Runs a specified command for each file in a set of files.

[…]

for {%%|%}<Variable> in (<Set>) do <Command> [<CommandLineOptions>]

[…]

Parameter Description
{%%|%}<Variable> Required. Represents a replaceable parameter. Use a single percent sign (%) to carry out the for command at the command prompt. Use double percent signs (%%) to carry out the for command within a batch file. Variables are case sensitive, and they must be represented with an alphabetical value such as %A, %B, or %C.

[…]

Remarks

[…]

Ouch¹: the syntaxes fail to specify how to substitute <Variable> in <Command> or <CommandLineOptions>!

Ouch²: the first two highlighted parts contradict each other – any character allows more than just an alphabetical value alias (upper or lower case) letter!

Ouch³: the last four highlighted parts also contradict each other – with usebackq, <LiteralString> must be enclosed single quotes instead of double quotes, and <Command> must be enclosed in back quotes instead of single quotes!

Falsification

Perform the following 3 simple steps to prove the documentation cited above misleading and wrong.
  1. Start the Command Processor Cmd.exe, then execute the following command lines which use non-alphabetical ASCII characters for the variable:

    FOR /F "Tokens=1-15" %! IN ("Exclamation Quote Hash Dollar Percent Ampersand Apostrophe Left_Parenthesis Right_Parenthesis Asterisk Plus Comma Minus Dot Slash") DO @ECHO %! %^" %# %$ %^% %^& %' %( %) %* %+ %, %- %. %/
    FOR /F "Tokens=1-10" %0 IN ("Zero One Two Three Four Five Six Seven Eight Nine") DO @ECHO %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
    FOR /F "Tokens=1-7"  %: IN ("Colon Semicolon Less Equal Greater Question At") DO @ECHO %: %; %^< %= %^> %? %@
    FOR /F "Tokens=1-6"  %[ IN ("Left_Square_Bracket Backslash Right_Square_Bracket Caret Underline BackQuote") DO @ECHO %[ %\ %] %^^ %_ %`
    FOR /F "Tokens=1-4"  %{ IN ("Left_Curly_Bracket Vertical_Bar Right_Curly_Bracket Tilde") DO @ECHO %{ %^| %} %~
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    Exclamation Quote Hash Dollar %% Ampersand Apostrophe Left_Parenthesis Right_Parenthesis Asterisk Plus Comma Minus Dot Slash
    Zero One Two Three Four Five Six Seven Eight Nine
    Colon Semicolon Less Equal Greater Question
    Left_Square_Bracket Backslash Right_Square_Bracket Caret Underline BackQuote
    Left_Curly_Bracket Vertical_Bar Right_Curly_Bracket Tilde
    Oops¹: with exception of the percent sign, all printable ASCII characters can be used for the variable!
  2. Use (arbitrary) non-ASCII characters for the variable:

    FOR /F "UseBackQ" %€ IN ('Euro') DO @ECHO %€
    FOR /F "Tokens=1-11 UseBackQ" %¢ IN ('Cent Pound Currency Yen Broken_Bar Section Diaeresis Copyright Feminine_Ordinal Left_Angle Not') DO @ECHO %¢ %£ %¤ %¥ %¦ %§ %¨ %© %ª %« %¬
    FOR /F "UseBackQ" %® IN ('Registered') DO @ECHO %®
    FOR /F "UseBackQ" %¶ IN ('Paragraph') DO @ECHO %¶
    Euro
    Cent Pound Currency Yen Broken_Bar Section Diaeresis Copyright Feminine_Ordinal Left_Angle Not
    Registered
    Paragraph
    Note: a demonstration with (for example) Arabic, Cyrillic, Greek or Hebrew letters is left as an exercise to the reader!
  3. Execute the following command lines to show surprising behaviour:

    FOR /F "Delims=" %? IN ("! !! "" & && <NUL: >NUL: ^ ^^ | ||") DO ECHO %?
    FOR /F "Tokens=1-10" %0 IN ("! !! "" & && <NUL: >NUL: ^ ^^ | ||") DO ECHO %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
    ECHO ! !! "" & && <NUL: >NUL:  ^ | ||
    ! !! "" & && <NUL: >NUL:   | ||
    ECHO ! !! "" & && <NUL: >NUL: ^ | ||
    ! !! "" & && <NUL: >NUL:  | ||
    Oops²: with exception of the caret, which keeps its function as escape character, unescaped special characters forwarded by the internal For command via %‹character› are treated as literals!

    Caveat: with delayed expansion enabled, exclamation marks are but removed in batch scripts!

Fault № 13

The documentation for the internal For command of the Command Processor states:
Runs a specified command for each file in a set of files.

[…]

for {%%|%}<Variable> in (<Set>) do <Command> [<CommandLineOptions>]
[…]

Remarks

[…]

Demonstration

Perform the following simple step to show the misbehaviour.
  1. Start the Command Processor Cmd.exe, then execute the following command lines:

    FOR %? IN (CommonProgramFiles(x86) ProgramFiles(x86)) DO ECHO %?
    FOR %? IN (%CommonProgramFiles(x86)% %ProgramFiles(x86)%) DO ECHO %?
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    "ProgramFiles(x86))" cannot be processed syntactically at this point.
    "\Common" cannot be processed syntactically at this point.
    OUCH¹: the Command Processor fails to handle nested parentheses not just in command blocks!

Work-Around

  1. Escape the closing parentheses inside the <Set> or quote its elements and use %~<Variable> to remove them in the substitution:

    FOR %? IN (CommonProgramFiles(x86^) ProgramFiles(x86^)) DO @ECHO %?
    FOR %? IN ("CommonProgramFiles(x86)" "ProgramFiles(x86)") DO @ECHO %~?
    FOR %? IN ("%CommonProgramFiles(x86)%" "%ProgramFiles(x86)%") DO @ECHO %~?
    CommonProgramFiles(x86)
    ProgramFiles(x86)
    
    CommonProgramFiles(x86)
    ProgramFiles(x86)
    
    C:\Program Files (x86)\Common Files
    C:\Program Files (x86)

Fault № 14

The documentation for the internal For command of the Command Processor states:

Falsification

Perform the following 4 simple steps to prove the documentation cited above wrong, to show undocumented (mis)behaviour and bugs.
  1. Start the Command Processor, then execute the following command lines:

    FOR /L %? IN (1 1 1) DO @ECHO %?
    FOR /L %? IN (0 -1 0) DO @ECHO %?
    1
    0
    OUCH¹: contrary to the highlighted statement of the documentation cited above, the command is executed if Start# is equal to End#!
  2. Execute the following equivalent command lines to show undocumented behaviour:

    FOR /L %? IN (1 2 3) DO @ECHO %?
    FOR /L %? IN (1;2;3) DO @ECHO %?
    FOR /L %? IN (1=2=3) DO @ECHO %?
    FOR /L %? IN (1 2,3) DO @ECHO %?
    FOR /L %? IN (1;2=3) DO @ECHO %?
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    1 3
    1 3
    1 3
    1 3
    1 3
    Oops¹: space, semicolon and equal sign are valid separators too!
  3. Show more undocumented behaviour:

    FOR /L %? IN (0xA 0x1 0xB) DO @ECHO %?
    FOR /L %? IN (0XFFFFFFFF -1 0xFFFFFFFF) DO @ECHO %?
    FOR /L %? IN (0X80000000 -1 0x80000000) DO @ECHO %?
    FOR /L %? IN (020000000000 -1 020000000000) DO @ECHO %?
    FOR /L %? IN (2147483648 -1 2147483648) DO @ECHO %?
    FOR /L %? IN (-2147483649 1 -2147483649) DO @ECHO %?
    FOR /L %? IN (-020000000001 1 -020000000001) DO @ECHO %?
    FOR /L %? IN (-0X80000001 1 -0x80000001) DO @ECHO %?
    FOR /L %? IN (-0XFFFFFFFF 1 -0xFFFFFFFF) DO @ECHO %?
    
    10
    11
    2147483647
    2147483647
    2147483647
    2147483647
    -2147483648
    -2147483648
    -2147483648
    -2147483648
    Oops²: Start#, Step# and End# can be hexadecimal or octal numbers too!

    OUCH²: 0xFFFFFFFF, 0x80000000, 020000000000 and 2147483648 are evaluated as 2147483647 = 017777777777 = 0x7FFFFFFF, the greatest signed 32-bit integer, while −21474836489, −020000000000, −0x80000000 and −0xFFFFFFFF are evaluated as −2147483648, the smallest signed 32-bit integer – numbers greater than 2147483647 or smaller than −2147483648 are clamped!

  4. Finally show misbehaviour bugs:

    FOR /L %? IN (A 1 Z) DO @ECHO %?
    FOR /L %? IN (X Y Z) DO @ECHO %?
    0
    0
    0
    0
    ^C
    OUCH³: while the first command line echoes just a single 0, i.e. its loop terminates, the second command line echoes an arbitrary number of 0’s, i.e. its loop does not terminate – non-numeric values for Start#, Step# and End# are evaluated as 0 instead to yield syntax error!

Fault № 15

The documentation for the internal For command of the Command Processor states:
The documentation for the Command Processor Cmd.exe states:
Parameter Description
/d Disables execution of AutoRun commands.

[…]

Note: this feature enables (unprivileged) users to tamper with Logon batch scripts configured by their (privileged) administrators who are unaware of the pitfalls demonstrated below!

Demonstration

Perform the following 4 simple steps to show undocumented (mis)behaviour plus important details missing in the documentation cited above.
  1. Start the Command Processor, then execute the following command lines to set the Windows directory as CWD, remove all environment variables, verify that none are left and list the environment variables of a child Cmd.exe process:

    REM Copyright © 2004-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    CHDIR /D "%SystemRoot%"
    FOR /F "Delims==" %? IN ('SET') DO @SET "%?="
    SET
    FOR /F "Delims=" %? IN ('SET') DO @ECHO %?
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    COMSPEC=C:\Windows\system32\cmd.exe
    PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC
    PROMPT=$P$G
    Oops: if the Command Processor does not inherit the environment variables COMSPEC, PATHEXT and PROMPT from its parent process it sets them with default values.
  2. Execute the following (equivalent) command lines to display the command line of the child Cmd.exe process:

    FOR /F "Delims=" %? IN ('ECHO %CMDCMDLINE^%') DO @ECHO %?
    FOR /F "Delims= UseBackQ" %? IN (`ECHO %CMDCMDLINE^%`) DO @ECHO %?
    C:\Windows\system32\cmd.exe /c ECHO %CMDCMDLINE%
    C:\Windows\system32\cmd.exe /c ECHO %CMDCMDLINE%
    OUCH¹: both FOR /F … IN ('…') DO … and FOR /F "UseBackQ" … IN (`…`) DO … execute their child Cmd.exe process only with the parameter /c, i.e. AutoRun commands are executed!
  3. Execute the following command lines to set an AutoRun command in the user’s Registry, start a child Cmd.exe process and remove the AutoRun command afterwards:

    REG.EXE ADD "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /V "AutoRun" /T REG_SZ /D "VERIFY" /F
    FOR /F "Delims=" %? IN ('ECHO') DO @ECHO %?
    REG.EXE DELETE "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /V "AutoRun" /F
    FOR /F "Delims=" %? IN ('ECHO') DO @ECHO %?
    The operation completed successfully.
    VERIFY is off.
    ECHO is on.
    The operation completed successfully.
    ECHO is on.
    OUCH²: due to the missing parameter /d the child Cmd.exe process executes the AutoRun command(s) set in the Registry which (can) create additional and typically unwanted output or perform arbitrary nefarious atrocities!
  4. Finally execute the following command line to quit the Command Processor:

    EXIT

Security Impact

The (unintended) execution of a (bogus) command specified by a (user-controlled) Registry entry like AutoRun is a well-known weakness, documented as CWE-??: and CWE-??: in the CWE; it allows well-known attacks like CAPEC-??: documented in the CAPEC.

Work-Around

Use
1>"‹file›" (‹command›)
FOR /F "UseBackQ" … IN ("‹file›") DO …
ERASE "‹file›"
instead of
FOR /F "UseBackQ" … IN (`‹command›`) DO …
or
FOR /F … IN ('‹command›') DO …
to prevent the execution of AutoRun commands.

Note: this also saves the execution of a (hidden) instance of the Command Processor!

Fault № 16

The documentation for the internal If command of the Command Processor states:
Performs conditional processing in batch programs.

[…]

Remarks

The documentation for the internal Set command of the Command Processor states:
Displays, sets, or removes CMD.EXE environment variables. If used without parameters, set displays the current environment variable settings.

[…]

Remarks

Demonstration

Perform the following 9 simple steps to prove the highlighted statements of the documentation cited above incomplete as well as misleading and show a more than 25 (in words: twenty-five) year old bug.
  1. Start the Command Processor Cmd.exe, then execute the following command line to display the help text of its internal Set command:

    SET /?
    NOTE: evaluation of substrings and dynamic variables are missing from the documentation of the internal Set command since the last millennium!
    Environment variable substitution has been enhanced as follows:
    […]
    May also specify substrings for an expansion.
    
        %PATH:~10,5%
    
    would expand the PATH environment variable, and then use only the 5
    characters that begin at the 11th (offset 10) character of the expanded
    result.  If the length is not specified, then it defaults to the
    remainder of the variable value.  If either number (offset or length) is
    negative, then the number used is the length of the environment variable
    value added to the offset or length specified.
    
        %PATH:~-10%
    
    would extract the last 10 characters of the PATH variable.
    
        %PATH:~0,-2%
    
    would extract all but the last 2 characters of the PATH variable.
    […]
    If Command Extensions are enabled, then there are several dynamic
    environment variables that can be expanded but which don't show up in
    the list of variables displayed by SET.  These variable values are
    computed dynamically each time the value of the variable is expanded.
    If the user explicitly defines a variable with one of these names, then
    that definition will override the dynamic one described below:
    
    %CD% - expands to the current directory string.
    
    %DATE% - expands to current date using same format as DATE command.
    
    %TIME% - expands to current time using same format as TIME command.
    
    %RANDOM% - expands to a random decimal number between 0 and 32767.
    
    %ERRORLEVEL% - expands to the current ERRORLEVEL value
    
    %CMDEXTVERSION% - expands to the current Command Processor Extensions
        version number.
    
    %CMDCMDLINE% - expands to the original command line that invoked the
        Command Processor.
    
    %HIGHESTNUMANODENUMBER% - expands to the highest NUMA node number on
        this machine.
  2. Execute the following single command line:

    FOR %? IN (CD DATE TIME RANDOM ERRORLEVEL CMDEXTVERSION CMDCMDLINE cmdcmdline HIGHESTNUMANODENUMBER) DO IF DEFINED %? SET %?
    IF DEFINED CD SET CD
    Environment variable CD not defined
    IF DEFINED DATE SET DATE
    Environment variable DATE not defined
    IF DEFINED TIME SET TIME
    Environment variable TIME not defined
    IF DEFINED RANDOM SET RANDOM
    Environment variable RANDOM not defined
    IF DEFINED ERRORLEVEL SET ERRORLEVEL
    Environment variable ERRORLEVEL not defined
    IF DEFINED CMDEXTVERSION SET CMDEXTVERSION
    Environment variable CMDEXTVERSION not defined
    IF DEFINED CMDCMDLINE SET CMDCMDLINE
    Environment variable CMDCMDLINE not defined
    IF DEFINED cmdcmdline SET cmdcmdline
    Environment variable cmdcmdline not defined
    IF DEFINED HIGHESTNUMANODENUMBER SET HIGHESTNUMANODENUMBER
    Environment variable HIGHESTNUMANODENUMBER not defined
    OOPS¹: contrary to the first highlighted statement of its documentation cited above, the internal If command works outside of batch scripts too!

    OOPS²: in addition to the dynamic environment variables ERRORLEVEL, CMDCMDLINE and CMDEXTVERSION listed by the second highlighted statement of its documentation cited above, the internal If command also supports the dynamic environment variables CD, DATE, TIME, RANDOM and HIGHESTNUMANODENUMBER – the latter was introduced with update 2028687 for Windows 7 and Windows Server 2008 R2!

    OOPS³: the internal Set command disagrees with the internal If command – the dynamic environment variables are both defined and undefined!

    OUCH¹: contrary to the second highlighted statement of its documentation cited above, IF DEFINED adds neither ERRORLEVEL nor CMDEXTVERSION nor CMDCMDLINE nor any other dynamic variable to the environment!

  3. Create the text file comspec.c with the following content in an arbitrary, preferable empty directory:

    // Copyleft © 2004-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    const	STARTUPINFO	si = {sizeof(si)};
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	PROCESS_INFORMATION pi;
    
    	WCHAR	szComSpec[MAX_PATH];
    	DWORD	dwComSpec = GetSystemDirectory(szComSpec, sizeof(szComSpec) / sizeof(*szComSpec));
    	DWORD	dwError = ERROR_SUCCESS;
    
    	if (dwComSpec == 0UL)
    		dwError = GetLastError();
    	else
    	{
    		memcpy(szComSpec + dwComSpec, L"\\Cmd.exe", sizeof(L"\\Cmd.exe"));
    
    		if (!CreateProcess(szComSpec,
    		                   L"%COMSPEC% /V:ON /D /C SET & ECHO cd=!CD! & ECHO date=!DATE! & ECHO time=!TIME! & ECHO random=!RANDOM! & ECHO errorlevel=!ERRORLEVEL! & ECHO cmdextversion=!CMDEXTVERSION! & ECHO cmdcmdline=!CMDCMDLINE! & ECHO highestnumanodenumber=!HIGHESTNUMANODENUMBER! & EXIT",
    		                   (LPSECURITY_ATTRIBUTES) NULL,
    		                   (LPSECURITY_ATTRIBUTES) NULL,
    		                   FALSE,
    		                   CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
    		                   L"CD=\0DATE=\0TIME=\0RANDOM=\0ERRORLEVEL=\0CMDEXTVERSION=\0CMDCMDLINE=\0HIGHESTNUMANODENUMBER=\0",
    		                   (LPCWSTR) NULL,
    		                   &si,
    		                   &pi))
    			dwError = GetLastError();
    		else
    		{
    			if (WaitForSingleObject(pi.hThread, INFINITE) == WAIT_FAILED)
    				dwError = GetLastError();
    
    			if (!CloseHandle(pi.hThread))
    				dwError = GetLastError();
    
    			if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED)
    				dwError = GetLastError();
    
    			if (!CloseHandle(pi.hProcess))
    				dwError = GetLastError();
    	}
    
    	ExitProcess(dwError);
    }
  4. Compile and link the source file comspec.c created in step 3.:

    SET CL=/Oi /W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE comspec.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    comspec.c
    comspec.c(34) : warning C4090: 'function' : different 'const' qualifiers
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:comspec.exe
    comspec.obj
    kernel32.lib
  5. Execute the console application comspec.exe built in step 4.:

    .\comspec.exe
    CD=
    COMSPEC=C:\Windows\SysWOW64\Cmd.exe
    DATE=
    PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC
    PROMPT=$P$G
    TIME=
    RANDOM=
    ERRORLEVEL=
    CMDEXTVERSION=
    CMDCMDLINE=
    HIGHESTNUMANODENUMBER=
    cd=C:\Users\Stefan\Desktop
    date=04/27/2012
    time=12:34:56,78
    random=12345
    errorlevel=0
    cmdextversion=2
    cmdcmdline=%COMSPEC% /V:ON /D /C SET & ECHO cd=!CD! & ECHO date=!DATE! & ECHO time=!TIME! & ECHO random=!RANDOM! & ECHO errorlevel=!ERRORLEVEL! & ECHO cmdextversion=!CMDEXTVERSION! & ECHO cmdcmdline=!CMDCMDLINE! & ECHO highestnumanodenumber=!HIGHESTNUMANODENUMBER! & EXIT
    highestnumanodenumber=0
    OOPS⁴: if any of the environment variables COMSPEC, PATHEXT or PROMPT is not inherited from the parent process, the Command Processor sets it with the default value!

    OUCH²: contrary to the last three highlighted statements of the first documentation cited above as well as the first highlighted statement of the help text displayed in step 1., an explicitly defined empty environment variable CD, DATE, TIME, RANDOM, ERRORLEVEL, CMDEXTVERSION, CMDCMDLINE or HIGHESTNUMANODENUMBER but does not override the dynamic one!

  6. Create the text file comspec.bat with the following content in an arbitrary, preferable empty directory:

    REM Copyleft © 2001-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    SET CMDCMDLINE=
    IF NOT DEFINED CMDCMDLINE EXIT /B
    SET CMDCMDLINE
    REM '%CMDCMDLINE%'
    :comspec
    IF NOT "%CMDCMDLINE:~33,-3%" == "" GOTO :comspec
    SET CMDCMDLINE
    IF DEFINED CMDCMDLINE ECHO %CMDCMDLINE%
    PAUSE
    EXIT /B
  7. Start the batch script comspec.bat created in step 6. per double-click:

    REM Copyleft © 2001-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de>
    SET CMDCMDLINE=
    IF NOT DEFINED CMDCMDLINE EXIT /B
    SET CMDCMDLINE
    Environment variable CMDCMDLINE not defined
    REM 'C:\Windows\System32\cmd.exe /c ""C:\Users\Stefan\Desktop\comspec.bat" "'
    IF NOT "C:\Users\Stefan\Desktop\comspec.bat" == "" GOTO :comspec
    IF NOT "" == "" GOTO :comspec
    SET CMDCMDLINE
    Environment variable CMDCMDLINE not defined
    IF DEFINED CMDCMDLINE ECHO 
    ECHO is on.
    PAUSE
    Press any key to continue . . .
    EXIT /B

    OUCH³: extracting a substring from the variable CMDCMDLINE sets it to the resulting substring, i.e. modifies the variable!

  8. Overwrite the text file comspec.bat created in step 6. with the following content:

    REM Copyleft © 2001-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    SET CMDCMDLINE=
    IF NOT DEFINED CMDCMDLINE EXIT /B
    SET CMDCMDLINE
    REM '%CMDCMDLINE%'
    SETLOCAL ENABLEDELAYEDEXPANSION
    :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    SET CMDCMDLINE
    IF DEFINED CMDCMDLINE ECHO %CMDCMDLINE%
    PAUSE
    EXIT /B
  9. Start the batch script comspec.bat modified in step 8. per double-click:

    REM Copyleft © 2001-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de>
    SET CMDCMDLINE=
    IF NOT DEFINED CMDCMDLINE EXIT /B
    SET CMDCMDLINE
    Environment variable CMDCMDLINE not defined
    REM 'C:\Windows\System32\cmd.exe /c ""C:\Users\Stefan\Desktop\comspec.bat" "'
    SETLOCAL ENABLEDELAYEDEXPANSION
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    IF NOT "!CMDCMDLINE:~1,-1!" == "" GOTO :comspec
    SET CMDCMDLINE
    Environment variable CMDCMDLINE not defined
    IF DEFINED CMDCMDLINE ECHO 
    ECHO is on.
    PAUSE
    Press any key to continue . . .
    EXIT /B
    OUCH⁴: with delayed expansion enabled the Command Processor exhibits this bug too!

Fault № 17

When executed with the argument ON or OFF, the undocumented internal Keys command sets the also undocumented environment variable KEYS to this value. When executed without argument, it displays its setting like the internal Verify command, but does not evaluate the environment variable.

Falsification

  1. KEYS /?
    KEYS
    SET KEYS
    KEYS ON
    SET KEYS
    KEYS OFF
    SET KEYS
    SET KEYS=ON
    KEYS
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    Enables or disables command line editing on DOS system
    
    This is present for Compatibility with DOS systems. It has no effect
    under Windows, as command line editing is always enabled.
    
    KEYS is off.
    Environment variable KEYS not defined
    KEYS=ON
    KEYS=OFF
    KEYS is off.
    OOPS: contrary to its help text, the internal KEYS command has effect under Windows – it sets an environment variable!

Fault № 18

The documentation for the internal Mkdir alias Md command of the Command Processor states:
Creates a directory or subdirectory.

Note

This command is the same as the mkdir command.

[…]

md [<Drive>:]<Path>
mkdir [<Drive>:]<Path>
The documentation for the internal Rmdir alias Rd command of the Command Processor states:
Deletes a directory. This command is the same as the rmdir command.

[…]

rd [<Drive>:]<Path> [/s [/q]]
rmdir [<Drive>:]<Path> [/s [/q]]

Falsification

Perform the following 2 simple steps to prove the documentation cited above as well as the help texts of both internal commands wrong.
  1. Start the Command Processor Cmd.exe, then execute the following command lines to display the help texts of both internal commands:

    MKDIR /?
    RMDIR /?
    Creates a directory.
    
    MKDIR [drive:]path
    MD [drive:]path
    
    […]
    
    Removes (deletes) a directory.
    
    RMDIR [/S] [/Q] [drive:]path
    RD [/S] [/Q] [drive:]path
    
        /S      Removes all directories and files in the specified directory
                in addition to the directory itself. Used to remove a directory
                tree.
    
        /Q      Quiet mode, do not ask if ok to remove a directory tree with /S
  2. Create and delete more than a single directory with both internal commands:

    MKDIR COMSPEC comspec
    RMDIR COMSPEC comspec
    A subdirectory or file COMSPEC already exists.
    Error occurred while processing: COMSPEC.
    
    The system cannot find the file COMSPEC.
    Oops: contrary to their documentation cited above as well as their help texts, both internal commands accept more than a single directory pathname!

Fault № 19

This fault complements and supplements Fault № 6.

The TechNet article How Permissions Work specifies:

Folder permissions include Full Control, Modify, Read & Execute, List Folder Contents, Read, and Write. Each of these permissions consists of a logical group of special permissions that are listed and defined in the following table.

Permissions for Files and Folders

Permission Description
Delete Subfolders and Files Allows or denies deleting subfolders and files, even if the Delete permission has not been granted on the subfolder or file. (Applies to folders.)
Delete Allows or denies deleting the file or folder. If you do not have Delete permission on a file or folder, you can still delete it if you have been granted Delete Subfolders and Files on the parent folder.

[…]

You should also be aware of the following:

Demonstration

Perform the following 6 simple steps to show the braindead misbehaviour.
  1. Start the Command Processor Cmd.exe in an arbitrary, preferable empty directory, then execute the following single command line to display its access permissions:

    CACLS.EXE .
    C:\Users\Stefan\Desktop NT AUTHORITY\SYSTEM:(OI)(CI)F
                            BUILTIN\Administrators:(OI)(CI)F
                            AMNESIAC\Stefan:(OI)(CI)F
  2. Execute the following command lines to create the subdirectory ComSpec, remove its (inherited) access permissions and (try to) erase it:

    MKDIR ComSpec
    ICACLS.EXE ComSpec /INHERITANCE:R /Q
    RMDIR ComSpec
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    Successfully processed 1 files; Failed processing 0 files
    Access denied
    OUCH: contrary to the highlighted statements of the documentation cited above and despite the Full Control access permission on its parent directory, the Command Processor fails to remove the subdirectory ComSpec – most obviously it performs a superfluous access check on (sub)directories too!
  3. Create the text file comspec.c with the following content next to the subdirectory ComSpec created in step 2.:

    // Copyleft © 2004-2025, Stefan Kanthak <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>
    
    #define STRICT
    #define UNICODE
    #define WIN32_LEAN_AND_MEAN
    
    #include <windows.h>
    
    __declspec(noreturn)
    VOID	CDECL	wmainCRTStartup(VOID)
    {
    	DWORD	dwError = ERROR_SUCCESS;
    #ifdef COMSPEC
    	HANDLE	hComSpec = CreateFile(L"ComSpec",
    		                      DELETE,
    		                      FILE_SHARE_DELETE,
    		                      (LPSECURITY_ATTRIBUTES) NULL,
    		                      OPEN_EXISTING,
    		                      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE,
    		                      (HANDLE) NULL);
    
    	if (hComSpec == INVALID_HANDLE_VALUE)
    		dwError = GetLastError();
    	else
    		if (!CloseHandle(hComSpec))
    			dwError = GetLastError();
    #else
    	if (!RemoveDirectory(L"ComSpec"))
    		dwError = GetLastError();
    #endif
    	ExitProcess(dwError);
    }
  4. Compile and link the source file comspec.c created in step 3.:

    SET CL=/W4 /Zl
    SET LINK=/ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    CL.EXE comspec.c kernel32.lib
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    comspec.c
    
    Microsoft (R) Incremental Linker Version 10.00.40219.386
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /ENTRY:wmainCRTStartup /NODEFAULTLIB /SUBSYSTEM:CONSOLE
    /out:comspec.exe
    comspec.obj
    kernel32.lib
  5. Execute the console application comspec.exe built in step 4. and evaluate its exit code:

    .\comspec.exe
    ECHO %ERRORLEVEL%
    0
  6. Finally verify that the subdirectory ComSpec created in step 1. doesn’t exist any more:

    MKDIR ComSpec
    RMDIR ComSpec

Fault № 20

The documentation for the internal Mklink command of the Command Processor states:
Creates a symbolic link.

[…]

mklink [[/d] | [/h] | [/j]] <Link> <Target>

[…]

Parameter Description
/d Creates a directory symbolic link. By default, mklink creates a file symbolic link.
/h Creates a hard link instead of a symbolic link.
/j Creates a Directory Junction.
<Link> Specifies the name of the symbolic link that is being created.
<Target> Specifies the path (relative or absolute) that the new symbolic link refers to.
/h Displays help at the command prompt.

Falsification

Perform the following 2 simple steps to show the bugs.
  1. Start the Command Processor Cmd.exe with elevated access rights, then execute the following command lines:

    MKLINK ComSpec "%SystemRoot%"
    DIR ComSpec
    CHDIR /D ComSpec
    ERASE ComSpec
    MKLINK /D ComSpec "%COMSPEC%"
    DIR ComSpec
    CHDIR /D ComSpec
    RMDIR ComSpec
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    Symbolic link created for ComSpec <<===>> C:\Windows
    
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    04/27/2012  08:15 PM <SYMLINK>         ComSpec [C:\Windows]
                   1 File(s)              0 bytes
                   0 Dir(s)    9,876,543,210 bytes free
    
    The directory name is invalid.
    
    Symbolic link created for ComSpec <<===>> C:\Windows\system32\cmd.exe
    
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    File Not Found
    
    The directory name is invalid.
    OUCH¹: the internal Mklink command fails to check that the destination of a symbolic link matches its type and creates invalid reparse points!
  2. Start the Command Processor Cmd.exe again, then execute the following command lines:

    MKLINK /J ComSpec "%COMSPEC%"
    DIR ComSpec
    CHDIR /D ComSpec
    RMDIR ComSpec
    Junction created for ComSpec <<===>> C:\Windows\system32\cmd.exe
    
     Volume in drive C has no label.
     Volume Serial Number is 1957-0427
    
     Directory of C:\Users\Stefan\Desktop
    
    File Not Found
    
    The directory name is invalid.
    OUCH²: the internal Mklink command fails to check that the destination of a directory junction is a directory and creates an invalid reparse point!

Fault № 21

The documentation for the internal Prompt command of the Command Processor states:
Changes the Cmd.exe command prompt. If used without parameters, prompt resets the command prompt to the default setting, which is the current drive letter and directory followed by the greater than symbol (>).
Note: the documentation but fails to mention that the current setting is stored in the environment variable PROMPT.

Demonstration

Perform the following 4 simple steps to show the true behaviour.
  1. Start the Command Processor Cmd.exe, then execute the following command lines:

    SET PROMPT
    PROMPT
    SET PROMPT
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    PROMPT=$P$G
    Environment variable PROMPT not defined
    Oops¹: when the internal Prompt command is executed without parameters the environment variable PROMPT is unset and thus removed from the process environment block.
  2. Start another instance of the Command Processor and display its (default) value of the environment variable PROMPT:

    "%COMSPEC%" /D /C SET PROMPT
    PROMPT=$P$G
    Oops²: when a new instance of the Command Processor is started and the environment variable PROMPT is not inherited from the parent process it is preset with the default value $P$G of the prompt.
  3. Set the environment variable PROMPT to an (arbitrary valid) value:

    SET PROMPT=$$$A$B$C$D$E$F$G$L$M$N$P$Q$S$T$V$+$_
    $&|(04/27/2012←)><CC:\Users\Stefan\Desktop= 12:34:56,78Microsoft Windows [Version 6.1.7601]
    Oops³: setting the environment variable PROMPT changes the prompt immediately.
  4. Finally reset the prompt using comma, semicolon or equal sign as parameter:

    PROMPT ,
    SET PROMPT
    PROMPT ;
    SET PROMPT
    PROMPT =
    SET PROMPT
    Environment variable PROMPT not defined
    Environment variable PROMPT not defined
    Environment variable PROMPT not defined
    Oops⁴: the highlighted statement of the documentation cited above is wrong – comma, semicolon and equal sign are equivalent to no parameter!

Fault № 21

The documentation for the internal Rem command of the Command Processor states:
Remarks

[…]

Demonstration

Perform the following 3 simple steps to prove the documentation cited above incomplete and show surprising behaviour as well as a bug.
  1. Start the Command Processor Cmd.exe, then execute the following command lines to show undocumented behaviour at the (interactive) command prompt:

    0<. REM /?
    1>. REM /?
    2>>.. REM /?
    REM 0<. /?
    REM 1>. /?
    REM 2>>.. /?
    REM /? 0<.
    REM /? 1>.
    REM /? 2>..
    Note: the command lines can be copied and pasted as block into a Command Processor window.

    Oops¹: a redirection operator <, > or >> following or preceeding the internal Rem command with parameter /? is interpreted – and vice versa!

  2. Execute the following command lines to show surprising behaviour:

    REM /? & VER
    REM /? %CMDEXTVERSION%
    REM /?,%ERRORLEVEL%
    REM /?;%RANDOM%
    REM com/?=spec
    REM http://www.microsoft.com/?q=comspec
    Records comments (remarks) in a batch file or CONFIG.SYS.
    
    REM [comment]
    
    Microsoft Windows [Version 6.1.7601]
    
    2 was unexpected at this time.
    1 was unexpected at this time.
    12345 was unexpected at this time.
    spec was unexpected at this time.
    comspec was unexpected at this time.
    Oops²: a pipe operator | or an ampersand operator & following the internal Rem command with parameter /? is interpreted – and vice versa!

    Oops³: the internal Rem command fails when /? is followed by arbitrary characters which include one of the separators space, comma, semicolon and the undocumented equal sign!

  3. Execute the following command lines which use the special characters not covered in the documentation cited above:

    ECHO
    REM & ECHO &
    REM && ECHO &&
    REM || ECHO ||
    0<NUL: 1>NUL: REM ^
    comspec
    (REM ^)
    )
    ECHO is on.
    More?
    More?
    Oops⁴: an ampersand (&) is also not interpreted as special character!

    Oops⁵: neither the double ampersand (&&) nor the double pipe (||) are interpreted as conditional operators!

    Oops⁶: the caret (^) is but interpreted as special character – it escapes both the newline and the closing parenthesis here!

    Oops⁷: neither input nor output redirection are honored, both are ignored completely!

  4. Finally show the bug:

    (REM ^))
    )
    (REM ^))))
    )
    More?
    More?
    OUCH: inside a block started by an opening parenthesis any number of closing parentheses after an escaped one are discarded!

Fault № 23

The documentation for the internal Set command of the Command Processor states:
Displays, sets, or removes CMD.EXE environment variables. If used without parameters, set displays the current environment variable settings.

[…]

set [<Variable>=[<String>]]
set [/p] <Variable>=[<PromptString>]
set /a <Variable>=<Expression>

[…]

Parameter Description
<Variable> Specifies the environment variable to set or modify.
<String> Specifies the string to associate with the specified environment variable.

[…]

Remarks

Falsification

Perform the following 6 simple steps to prove the documentation cited above wrong and show the true (mis)behaviour.
  1. Start the Command Processor, then execute the following command lines to remove all environment variables, set the exit code to 123 and display all remaining environment variables:

    CHDIR /D "%ALLUSERSPROFILE%"
    FOR /F "Delims==" %? IN ('SET') DO @SET "%?="
    CMD.EXE /C EXIT 123
    SET ;
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    =C:=C:\ProgramData
    =ExitCode=0000007B
    =ExitCodeAscii={
    Oops¹: with the undocumented argument comma (,), semicolon (;), quotation mark ("), empty string ("") or blank string ("‹any number of spaces›"), the internal Set command displays all (regular) environment variables, including the undocumented internal environment variables whose name starts with an equal sign (=)!
  2. Add some environment variables and display them all:

    SET !=exclamation mark
    SET "=quotation mark
    SET #=hash sign
    SET $=dollar sign
    SET %=percent sign
    SET ^&=ampersand
    SET '=apostrophe
    SET (=left parenthesis
    SET )=right parenthesis
    SET *=asterisk
    SET +=plus sign
    SET ,=comma
    SET -=minus sign
    SET .=dot
    SET /=slash
    SET 0=zero
    SET 1=one
    SET 2=two
    SET 3=three
    SET 4=four
    SET 5=five
    SET 6=six
    SET 7=seven
    SET 8=eight
    SET 9=nine
    SET :=colon
    SET ;=semicolon
    SET ^<=less than
    SET ==equal sign
    SET ^>=greater than
    SET ?=question mark
    SET @=at sign
    SET [=left bracket
    SET \=backslash
    SET ]=right bracket
    SET ^^=caret
    SET _=underscore
    SET `=backtick
    SET {=left brace
    SET ^|=vertical bar
    SET }=right brace
    SET ~=tilde
    SET
    !=exclamation mark
    Syntax error
    #=hash sign
    $=dollar sign
    %=percent sign
    &=ampersand
    '=apostrophe
    (=left parenthesis
    )=right parenthesis
    *=asterisk
    +=plus sign
    ,=comma
    -=minus sign
    .=dot
    Syntax error
    0=zero
    1=one
    2=two
    3=three
    4=four
    5=five
    6=six
    7=seven
    8=eight
    9=nine
    :=colon
    ;=semicolon
    <=less than
    Syntax error
    >=greater than
    ?=question mark
    @=at sign
    [=left bracket
    \=backslash
    ]=right bracket
    ^=caret
    _=underscore
    `=backtick
    {=left brace
    |=vertical bar
    }=right brace
    ~=tilde
    Oops²: with exception of quotation mark ("), slash (/) and equal sign (=) – /=… is an invalid switch and = terminates a variable name – which are rejected with an error message, all printable non-alphabetical ASCII characters are accepted as valid environment variable name!
  3. (Try to) evaluate the just set environment variables with a special command shell character name:

    ECHO %%%
    ECHO %^&%
    ECHO %^<%
    ECHO %^>%
    ECHO %^^%
    ECHO %^|%
    %%%
    %&%
    %<%
    %>%
    %^%
    %|%
    OUCH: expansion of environment variables named %, &, <, >, ^ and | fails when enclosed in percent signs!
  4. Repeat the previous step 3. with the environment variable names enclosed in percent signs and quotation marks:

    ECHO "%%%"
    ECHO "%&%"
    ECHO "%<%"
    ECHO "%>%"
    ECHO "%^%"
    ECHO "%|%"
    "%%%"
    "ampersand"
    "less than"
    "greater than"
    "caret"
    "vertical bar"
    Oops³: now only expansion of the environment variable % fails!
  5. Start another instance of the Command Processor with delayed variable expansion enabled and evaluate the inherited environment variables with a special command shell character name:

    CMD.EXE /V:ON /C "ECHO !%! & ECHO !^&! & ECHO !^<! & ECHO !^>! & ECHO !^^! & ECHO !^|! & EXIT"
    percent sign 
    ampersand 
    less than 
    greater than 
    caret 
    vertical bar 
  6. Unset the environment variables a second time, then set some named " and /, display and evaluate them:

    FOR /F "Delims==" %? IN ('SET') DO @SET "%?="
    SET " =space"
    SET ""=quotation mark"
    SET "/=slash"
    SET "/ =slash, space"
    SET " / =space, slash, space"
    SET
    ECHO %"%
    ECHO %/%
    ECHO %/ %
    ECHO % / %
    Syntax error
    "=quotation mark
    /=slash
    / =space, slash, space
    ;=semicolon
    quotation mark
    slash
    space, slash, space
    % / %
    Note: enclosed in quotation marks, " and / are finally accepted as environment variable name!

    Oops⁴: inside quotation marks the internal Set command accepts variable names with trailing spaces, but strips leading spaces!

Note: an exploration of the (mis)behaviour for single character environment variable names beyond the 7-bit ASCII code is left as an exercise to the reader.

Fault № 24

The documentation for the internal Set command of the Command Processor states:
Displays, sets, or removes CMD.EXE environment variables. If used without parameters, set displays the current environment variable settings.

[…]

set [<Variable>=[<String>]]
set [/p] <Variable>=[<PromptString>]
set /a <Variable>=<Expression>

[…]

Parameter Description
/p Sets the value of Variable to a line of input entered by the user.
<PromptString> Optional. Specifies a message to prompt the user for input. This parameter is used with the /p command-line option

Falsification

Perform the following simple step to prove the highlighted part of the documentation cited above wrong and show the true behaviour.
  1. Start the Command Processor Cmd.exe, then execute the following single command line:

    SET /P =comspec? 0<NUL:
    comspec?
    Oops: contrary to the highlighted parts of the documentation cited above, the variable name is but optional!

Fault № 25

The documentation for the internal Set command of the Command Processor states:
Displays, sets, or removes CMD.EXE environment variables. If used without parameters, set displays the current environment variable settings.

[…]

set [<Variable>=[<String>]]
set [/p] <Variable>=[<PromptString>]
set /a <Variable>=<Expression>

[…]

Parameter Description
/a Sets String to a numerical expression that is evaluated.
<Expression> Specifies a numerical expression. […]
/? Displays help at the command prompt.

[…]

Remarks

[…]

OUCH⁰: there are no && OR || (logical) operators!

Falsification

Perform the following 5 (plus 1 optional) simple steps to show the true (mis)behaviour and a really stupid, at least 28 (in words: twenty-eight) year old bug which crashes the Command Processor.
  1. Start the Command Processor Cmd.exe, then assign some numerical constants to an environment variable:

    SET /A comspec=07777777777
    SET /A comspec=-07777777777
    SET /A comspec=020000000000
    SET /A comspec=0x123456789ABCDEF
    SET /A comspec=-0x123456789ABCDEF
    SET /A comspec=-2147483648
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    1073741823
    -1073741823
    Invalid number. Numbers are limited to 32-bits of precision.
    -1
    1
    Invalid number. Numbers are limited to 32-bits of precision.
    Oops: when <Variable> is present it is set to the signed decimal string form of the result, with hexadecimal values greater than 0xFFFFFFFF clamped to 0xFFFFFFFF before the (optional) sign is applied, but (decimal and octal) values greater than 2147483647 or less than −2147483647 rejected.
  2. Evaluate some numerical constants from an environment variable:

    SET comspec=077777777777
    SET /A comspec
    SET comspec=0xFFFFFFFF
    SET /A comspec
    SET comspec=0x80000000
    SET /A comspec
    SET comspec=4294967295
    SET /A comspec
    SET comspec=2147483647
    SET /A comspec
    SET comspec=-2147483648
    SET /A comspec
    SET comspec=-2147483649
    SET /A comspec
    SET comspec=-4294967296
    SET /A comspec
    SET comspec=-0xFFFFFFFF
    SET /A comspec
    SET comspec=-077777777777
    SET /A comspec
    2147483647
    2147483647
    2147483647
    2147483647
    2147483647
    -2147483648
    -2147483648
    -2147483648
    -2147483648
    -2147483648
    OOPS¹: when read from an environment variable, positive values greater than 2147483647, the largest signed 32-bit integer, are clamped to 2147483647, and negative values smaller than −2147483648, the smallest signed 32-bit integer, are clamped to −2147483648!
  3. Evaluate the numerical dynamic variables CMDEXTVERSION, ERRORLEVEL and RANDOM:

    ECHO %CMDEXTVERSION%
    SET /A CMDEXTVERSION
    CMD.EXE /C EXIT 123
    ECHO %ERRORLEVEL%
    SET /A ERRORLEVEL
    ECHO %RANDOM%
    SET /A RANDOM
    2
    0
    123
    0
    12345
    0
    OUCH¹: contrary to the last highlighted statement of its documentation cited above, SET /A fails to evaluate dynamic variables!
  4. Evaluate some constants and expressions:

    SET /A 0xFFFFFFFF
    SET /A ~0
    SET /A ~0 ^<^< 32
    SET /A 0x80000000
    SET /A 0x80000000 ^>^> 99
    SET /A -65536 * 65536
    SET /A -020000000000
    SET /A -2147483648
    SET /A ~2147483647
    SET /A ~2147483647 * 2
    SET /A ~2147483647 / ~0
    -1
    -1
    0
    -2147483648
    -1
    0
    Invalid number. Numbers are limited to 32-bits of precision.
    Invalid number. Numbers are limited to 32-bits of precision.
    -2147483648
    0
    Invalid number. Numbers are limited to 32-bits of precision.
    OUCH²: contrary to the first highlighted part of its documentation cited above, SET /A works without variable name and assignment operator on the left side of an expression!

    Note: shifts are not limited to the size of the internal (32-bit) number width.

    OUCH³: contrary to the last highlighted part of the documentation cited above, shifts are performed arithmetical, i.e. right shifts propagate the sign!

    OUCH⁴: although valid, a literal −2147483648 alias −020000000000, the smallest signed 32-bit integer, is erroneously reported as invalid number!

    OOPS²: (signed) integer overflow is detected only for division, other operations wrap around.

  5. Finally demonstrate the bug:

    SET /A ~2147483647 % ~0
    OUCH⁵: on machines with i386 or AMD64 processor, the Command Processor crashes with an integer overflow exception 0xC0000095 when computing the modulus of −2147483648 ÷ −1 – which happens to be 0, the only number smaller in magnitude than the divisor −1!

    Note: division of −2147483648, the smallest signed 32-bit integer, by −1 yields the quotient 2147483648, which is but not representable as signed 32-bit integer and therefore produces an overflow (really: raises a divide error exception alias #DE) that the Command Processor fails to handle, i.e. neither prevents nor catches for the modulus operator, while it does so for the division operator!

    Note: integer overflow and failure to catch the eventually resulting exception are well-known weaknesses, documented as CWE-190: Integer Overflow or Wraparound and CWE-248: Uncaught Exception in the CWE; they allow well-known attacks like CAPEC-92: Forced Integer Overflow documented in the CAPEC.

  6. (Optional) If you have the Debugging Tools for Windows installed, execute the Command Processor under the debugger:

    CDB.EXE /C g;q /G /g "%COMSPEC%" /D /C SET /A ~2147483647 % ~0
    Note: if necessary, see the MSDN articles Debugging Using CDB and NTSD and CDB Command-Line Options for an introduction.
    Microsoft (R) Windows Debugger Version 6.11.0001.404 X86
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    CommandLine: "C:\Windows\system32\cmd.exe" /D /C SET /A ~2147483647 % ~0
    Symbol search path is: srv*
    Executable search path is: 
    ModLoad: 4a6d0000 4a71c000   cmd.exe
    ModLoad: 779c0000 77b40000   ntdll.dll
    ModLoad: 75610000 75720000   C:\Windows\syswow64\kernel32.dll
    ModLoad: 752d0000 75317000   C:\Windows\syswow64\KERNELBASE.dll
    ModLoad: 75540000 755e1000   C:\Windows\syswow64\ADVAPI32.DLL
    ModLoad: 757a0000 7584c000   C:\Windows\syswow64\msvcrt.dll
    ModLoad: 77420000 77439000   C:\Windows\SysWOW64\sechost.dll
    ModLoad: 77300000 773f0000   C:\Windows\syswow64\RPCRT4.dll
    ModLoad: 750e0000 75140000   C:\Windows\syswow64\SspiCli.dll
    ModLoad: 750d0000 750dc000   C:\Windows\syswow64\CRYPTBASE.dll
    ModLoad: 73d10000 73d17000   C:\Windows\SysWOW64\WINBRAND.dll
    ModLoad: 76f60000 77060000   C:\Windows\syswow64\USER32.dll
    ModLoad: 764f0000 76580000   C:\Windows\syswow64\GDI32.dll
    ModLoad: 76580000 7658a000   C:\Windows\syswow64\LPK.dll
    ModLoad: 76690000 7672d000   C:\Windows\syswow64\USP10.dll
    ModLoad: 76630000 76690000   C:\Windows\SysWOW64\IMM32.DLL
    ModLoad: 75470000 7553e000   C:\Windows\syswow64\MSCTF.dll
    (3314.4098): Integer overflow - code c0000095 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    eax=80000000 ebx=00000025 ecx=00000025 edx=ffffffff esi=003ef680 edi=003ef69c
    eip=4a6e5176 esp=003ef650 ebp=003ef650 iopl=0         nv up ei ng nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010286
    *** ERROR: Module load completed but symbols could not be loaded for cmd.exe
    cmd+0x15176:
    4a6e5176 f77d14          idiv    eax,dword ptr [ebp+14h] ss:002b:003ef664=ffffffff
    0:000:x86> cdb: Reading initial command 'g;q'
    (3314.4098): Integer overflow - code c0000095 (!!! second chance !!!)
    quit:

Security Impact

The documentation for the Command Processor Cmd.exe states:
Parameter Description
/d Disables execution of AutoRun commands.
/a Formats internal command output to a pipe or a file as American National Standards Institute (ANSI).
/u Formats internal command output to a pipe or a file as Unicode.

[…]

By setting one or both of these registry entries to (for example) the value SET /A ~2147483647 % ~0 or SET /A (1 ^<^< 31) % -1, this 28 (in words: twenty-eight) year old bug (really: absolute beginner’s programming error) can trivially be (ab)used to conduct a denial of service attack against the Command Processor and crash it upon launch, thus disabling its use for single or all users of a machine:
REGEDIT4

[HKEY_CURRENT_USER\Software\Microsoft\Command Processor]
"AutoRun"="SET /A ~2147483647 % ~0"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]
"AutoRun"="SET /A (1 ^<^< 31) % -1"

MSRC Case 71680

Due to its security impact I reported this bug at the MSRC where case number 71680 was assigned.

They replied with the following bullshit statement:

Though engineering confirmed the crash in this case, it was assessed as a Low severity DoS.
Their reasoning centers around the requirement to have admin privileges to pull off the attack.
OUCH¹: on standard installations of Windows, where the user typically abuses the so-called Protected Administrator account created during setup, there is no security boundary to prevent the user or any (rogue) program running under this account from acquiring administrative privileges!

OUCH²: unprivileged users can write the AutoRun registry entry in their HKEY_CURRENT_USER\Software\Microsoft\Command Processor registry key, and they can insert the command SET /A (1 ^<^< 31) % ~0 or SET /A ~2147483647 % -1 in every batch script they can (over)write!

Will engineering ever learn to use their Windows PCs as unprivileged users?

Fault № 26

The documentation for the internal Start command of the Command Processor states:
Starts a separate Command Prompt window to run a specified program or command.

[…]

Remarks

OUCH: the default value for the PATHEXT environment variable is but .COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC!

Falsification

Perform the following 9 simple steps to prove the 3 highlighted statements of the documentation cited above wrong and show undocumented (mis)behaviour that causes a security vulnerability.
  1. Start the Command Processor Cmd.exe, then execute the following command lines to display the current value of the environment variable PATHEXT and determine its origin:

    SET PATHEXT
    REG.EXE QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V PATHEXT
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
        PATHEXT    REG_SZ    .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    OOPS¹: in the installation images distributed by Microsoft, the environment variable PATHEXT is preset with a non-default value!
  2. Unset the environment variable PATHEXT to remove it from the process environment block and start another instance of the Command Processor to display its (default) value of this environment variable:

    SET PATHEXT=
    START /B CMD /D /C SET PATHEXT
    PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC
    OOPS²: the default value of the environment variable PATHEXT differs from the value given in the documentation cited above!
  3. Set the environment variable COMSPEC to the path name of an arbitrary application and start another instance of the Command Processor:

    SET COMSPEC=%SystemRoot%\System32\Reg.exe
    START /B CMD /D /C ECHO
    Note: the Registry Console Tool Reg.exe is only (ab)used as canary here.
    ECHO is on.
    OUCH¹: contrary to the first highlighted sentence of the documentation cited above, the environment variable COMSPEC is not evaluated by the internal START […] CMD […] command!
  4. Create an empty file CMD.EXE in the current directory and start another instance of the Command Processor to prove the first (highlighted) remark of the documentation cited above completely wrong:

    [Screen shot of message box from module loader on Windows 7]

    COPY NUL: CMD.EXE
    START CMD
            1 file(s) copied.
    Access Denied

    OUCH²: contrary to the second highlighted sentence of its documentation cited above, the internal START […] CMD […] command executes an arbitrary application CMD.EXE that happens to exist in the CWD!
  5. Rename the empty file CMD.EXE to CMD.COM and repeat the last command to prove the first (highlighted) remark of the documentation cited above again completely wrong:

    [Screen shot of message box from module loader on Windows 7]

    RENAME CMD.EXE CMD.COM
    START CMD
    Access Denied

    OUCH³: START CMD also executes an arbitrary application CMD.COM that happens to exist in the CWD!
  6. Delete the empty file CMD.COM, create the text file CMD.BAT with the single (command) line @VERIFY & EXIT in the current directory and conduct the proof from step 4. once more:

    ERASE CMD.COM
    1>CMD.BAT ECHO @VERIFY & EXIT
    START /B CMD
    VERIFY is off.
    OUCH⁴: START […] CMD […] even executes an arbitrary batch script CMD.BAT that happens to exist in the CWD!

    Note: a repetition of the previous step 6. using other file name extensions from the PATHEXT environment variable is left as an exercise to the reader.

  7. Execute the following command line to show undocumented (mis)behaviour:

    START /B EXIT
    ERROR: Invalid Argument/Option - '/K'.
    Type "REG /?" for usage.
    OUCH⁵: contrary to its documentation cited above, without a first token CMD the internal command START ‹arguments› evaluates the (user-controlled) environment variable COMSPEC and executes an arbitrary application using the command line %COMSPEC% /K ‹arguments›!

    Note: properly implemented, the Command Processor would fetch its own path name with the Win32 function GetModuleFileName() instead to use the value of a user-controlled environment variable!

  8. Delete the environment variable COMSPEC and repeat the previous step 7. to show more undocumented (mis)behaviour:

    SET COMSPEC=
    START /B EXIT
    The COMSPEC environment variable does not point to CMD.EXE.
  9. Finally clean up and quit the Command Processor:

    ERASE CMD.BAT
    EXIT

Security Impact

The (unintended) execution of (a bogus) application or batch script CMD.EXE, CMD.COM, CMD.BAT etc. from the CWD constitutes a security vulnerability, similar to CVE-2014-0315 alias MS14-019 I discovered and reported at the MSRC about 10 years ago and which was fixed with security update 2922229 back then.

Note: the post MS14-019 – Fixing a binary hijacking via .cmd or .bat file on Microsoft’s Security Research and Defense Blog provides additional information.

The well-known underlying weakness is documented as CWE-426: Untrusted Search Path and CWE-427: Uncontrolled Search Path Element in the CWE; the well-known attacks are documented as CAPEC-471: Search Order Hijacking and CAPEC-635: Alternative Execution Due to Deceptive Filenames in the CAPEC.

The (unintended) execution of a (bogus) application determined by the value of a (user-controlled) environment variable like COMSPEC is another well-known weakness, documented as CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') and CWE-73: External Control of File Name or Path in the CWE; it too allows well-known attacks like CAPEC-13: Subverting Environment Variable Values documented in the CAPEC.

MSRC Case 59749

Due to its security impact I reported this bug at the MSRC where case number 59749 was assigned.

They replied with the following statements:

The engineering team has looked over the issue and has stated that this appears to be a documentation error and has been handed over to the team owning that site. In this case this does not appear to be a vulnerability and we will be closing it out on our side. The method of using this information in an attack would require a remote attacker to find malicious program that uses the START cmd and trick the user to trigger this program as a minimum.
OUCH: it appears to me that the engineering team is wrong – I recommend to have them read (and understand) especially (the end of) the chapter titled Current Working Directory (CWD) DLL planting of the blog post Triaging a DLL planting vulnerability and recognise the striking similarity:
A DLL planting issue that falls into this category of CWD DLL planting is treated as an Important severity issue and we will issue a security patch for this.
How does execution of an application planted in the CWD differ from execution of a DLL planted there?

Remediation

Set the environment variable NoDefaultCurrentDirectoryInExePath which controls the Win32 function NeedCurrentDirectoryForExePath() with an arbitrary value to remove the (implicit) . alias CWD from (the front of) the search path for executable files.

CAVEAT: the (internal) command SET NoDefaultCurrentDirectoryInExePath= removes this environment variable from the environment block of the current instance of the Command Processor and neutralises the remediation for it and all its future child processes; the command line SetX.exe NoDefaultCurrentDirectoryInExePath "" as well as the VBScript statement WScript.CreateObject("WScript.Shell").Environment("VOLATILE").Item("NoDefaultCurrentDirectoryInExePath") = vbNullString remove this environment variable from the environment block of the shell Explorer.exe and neutralise the remediation for all its future child processes!

Fault № 27

The documentation for the internal Start command of the Command Processor states:
Starts a separate Command Prompt window to run a specified program or command.

[…]

Remarks

Falsification

Perform the following 2 simple steps to prove the highlighted statement of the documentation cited above wrong and show undocumented (mis)behaviour.
  1. Start the Command Processor Cmd.exe in an arbitrary, preferable empty directory, then execute the following command lines to display the file type associated with the file name extension .txt and its command line template:

    ASSOC .txt
    FTYPE txtfile
    Note: the command lines can be copied and pasted as block into a Command Processor window.
    .bat=batfile
    txtfile=%SystemRoot%\system32\NOTEPAD.EXE %1
  2. Execute the following command lines to create the text file comspec.txt containing the string MZ followed by a CR/LF pair, (try to) run this nonexecutable text file via the internal Start command and display the error level:

    [Screen shot of message box from module loader on Windows 7]

    1>comspec.txt ECHO MZ
    START comspec.txt
    ECHO %ERRORLEVEL%
    This version of C:\Users\Stefan\Desktop\comspec.txt is not compatible with the version of Windows you're running. Check your computer's system information to see whether you need an x86 (32-bit) or x64 (64-bit) version of the program, and then contact the software publisher.
    216

    OUCH: contrary to the highlighted statement of the documentation cited above, the internal Start command fails to run the program associated with the file name extension .txt – due to the magic number MZ at offset 0 of the file blunder.txt it (mis)interprets it as a 16-bit DOS application and calls the Win32 function CreateProcess() which fails with Win32 error code 216 alias ERROR_EXE_MACHINE_TYPE_MISMATCH on 64-bit editions of Windows NT respectively Win32 error code 193 alias ERROR_BAD_EXE_FORMAT on 32-bit editions of Windows NT!
Note: a repetition of this falsification for other registered file name extensions is left as an exercise to the reader.

Contact and Feedback

If you miss anything here, have additions, comments, corrections, criticism or questions, want to give feedback, hints or tipps, report broken links, bugs, deficiencies, errors, inaccuracies, misrepresentations, omissions, shortcomings, vulnerabilities or weaknesses, …: don’t hesitate to contact me and feel free to ask, comment, criticise, flame, notify or report!

Use the X.509 certificate to send S/MIME encrypted mail.

Note: email in weird format and without a proper sender name is likely to be discarded!

I dislike HTML (and even weirder formats too) in email, I prefer to receive plain text.
I also expect to see your full (real) name as sender, not your nickname.
I abhor top posts and expect inline quotes in replies.

Terms and Conditions

By using this site, you signify your agreement to these terms and conditions. If you do not agree to these terms and conditions, do not use this site!

Data Protection Declaration

This web page records no (personal) data and stores no cookies in the web browser.

The web service is operated and provided by

Telekom Deutschland GmbH
Business Center
D-64306 Darmstadt
Germany
<‍hosting‍@‍telekom‍.‍de‍>
+49 800 5252033

The web service provider stores a session cookie in the web browser and records every visit of this web site with the following data in an access log on their server(s):


Copyright © 1995–2025 • Stefan Kanthak • <‍stefan‍.‍kanthak‍@‍nexgo‍.‍de‍>