mirror of
https://github.com/vim/vim.git
synced 2025-12-10 18:46:57 -05:00
patch 9.1.1947: [security]: Windows: Vim may execute commands from current directory
Problem: [security]: Windows: Vim may execute commands from current
directory (Simon Zuckerbraun)
Solution: Set the $NoDefaultCurrentDirectoryInExePath before running
external commands.
Github Advisory:
https://github.com/vim/vim/security/advisories/GHSA-g77q-xrww-p834
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
@ -2711,13 +2711,15 @@ executable({expr}) *executable()*
|
|||||||
then the name is also tried without adding an extension.
|
then the name is also tried without adding an extension.
|
||||||
On MS-Windows it only checks if the file exists and is not a
|
On MS-Windows it only checks if the file exists and is not a
|
||||||
directory, not if it's really executable.
|
directory, not if it's really executable.
|
||||||
|
|
||||||
On MS-Windows an executable in the same directory as the Vim
|
On MS-Windows an executable in the same directory as the Vim
|
||||||
executable is always found. Since this directory is added to
|
executable is always found. Since this directory is added to
|
||||||
$PATH it should also work to execute it |win32-PATH|.
|
$PATH it should also work to execute it |win32-PATH|.
|
||||||
*NoDefaultCurrentDirectoryInExePath*
|
*NoDefaultCurrentDirectoryInExePath*
|
||||||
On MS-Windows an executable in Vim's current working directory
|
On MS-Windows an executable in Vim's current working directory
|
||||||
is also normally found, but this can be disabled by setting
|
is also normally found, but this can be disabled by setting
|
||||||
the $NoDefaultCurrentDirectoryInExePath environment variable.
|
the `$NoDefaultCurrentDirectoryInExePath` environment variable.
|
||||||
|
This is always done for |:!| commands, for security reasons.
|
||||||
|
|
||||||
The result is a Number:
|
The result is a Number:
|
||||||
1 exists
|
1 exists
|
||||||
|
|||||||
@ -5483,6 +5483,21 @@ mch_call_shell_terminal(
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
/* Restore a previous environment variable value, or unset it if NULL.
|
||||||
|
* 'must_free' indicates whether 'old_value' was allocated.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
restore_env_var(char_u *name, char_u *old_value, int must_free)
|
||||||
|
{
|
||||||
|
if (old_value != NULL)
|
||||||
|
{
|
||||||
|
vim_setenv(name, old_value);
|
||||||
|
if (must_free)
|
||||||
|
vim_free(old_value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vim_unsetenv(name);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Either execute a command by calling the shell or start a new shell
|
* Either execute a command by calling the shell or start a new shell
|
||||||
@ -5495,6 +5510,8 @@ mch_call_shell(
|
|||||||
int x = 0;
|
int x = 0;
|
||||||
int tmode = cur_tmode;
|
int tmode = cur_tmode;
|
||||||
WCHAR szShellTitle[512];
|
WCHAR szShellTitle[512];
|
||||||
|
int must_free;
|
||||||
|
char_u *oldval;
|
||||||
|
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
ch_log(NULL, "executing shell command: %s", cmd);
|
ch_log(NULL, "executing shell command: %s", cmd);
|
||||||
@ -5519,6 +5536,11 @@ mch_call_shell(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// do not execute anything from the current directory by setting the
|
||||||
|
// environemnt variable $NoDefaultCurrentDirectoryInExePath
|
||||||
|
oldval = vim_getenv((char_u *)"NoDefaultCurrentDirectoryInExePath",
|
||||||
|
&must_free);
|
||||||
|
vim_setenv((char_u *)"NoDefaultCurrentDirectoryInExePath", (char_u *)"1");
|
||||||
|
|
||||||
out_flush();
|
out_flush();
|
||||||
|
|
||||||
@ -5552,6 +5574,8 @@ mch_call_shell(
|
|||||||
// Use a terminal window to run the command in.
|
// Use a terminal window to run the command in.
|
||||||
x = mch_call_shell_terminal(cmd, options);
|
x = mch_call_shell_terminal(cmd, options);
|
||||||
resettitle();
|
resettitle();
|
||||||
|
restore_env_var((char_u *)"NoDefaultCurrentDirectoryInExePath",
|
||||||
|
oldval, must_free);
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5776,6 +5800,10 @@ mch_call_shell(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore original value of NoDefaultCurrentDirectoryInExePath
|
||||||
|
restore_env_var((char_u *)"NoDefaultCurrentDirectoryInExePath",
|
||||||
|
oldval, must_free);
|
||||||
|
|
||||||
if (tmode == TMODE_RAW)
|
if (tmode == TMODE_RAW)
|
||||||
{
|
{
|
||||||
// The shell may have messed with the mode, always set it.
|
// The shell may have messed with the mode, always set it.
|
||||||
|
|||||||
@ -440,6 +440,14 @@ func Test_zz2_terminal_guioptions_bang()
|
|||||||
call writefile(contents, filename, 'D')
|
call writefile(contents, filename, 'D')
|
||||||
call setfperm(filename, 'rwxrwx---')
|
call setfperm(filename, 'rwxrwx---')
|
||||||
|
|
||||||
|
if has("win32")
|
||||||
|
" should not execute anything below the current directory
|
||||||
|
let exitval = 1
|
||||||
|
execute printf(':!%s%s %d', prefix, filename, exitval)
|
||||||
|
call assert_equal(exitval, v:shell_error)
|
||||||
|
let prefix = '.\'
|
||||||
|
endif
|
||||||
|
|
||||||
" Check if v:shell_error is equal to the exit status.
|
" Check if v:shell_error is equal to the exit status.
|
||||||
let exitval = 0
|
let exitval = 0
|
||||||
execute printf(':!%s%s %d', prefix, filename, exitval)
|
execute printf(':!%s%s %d', prefix, filename, exitval)
|
||||||
@ -732,5 +740,40 @@ func Test_term_gettty()
|
|||||||
exe buf . 'bwipe'
|
exe buf . 'bwipe'
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_windows_external_cmd_in_cwd()
|
||||||
|
" Check that Vim does not execute anything from current directory
|
||||||
|
CheckMSWindows
|
||||||
|
|
||||||
|
" just in case
|
||||||
|
call system('rd /S /Q Xfolder')
|
||||||
|
call mkdir('Xfolder', 'R')
|
||||||
|
cd Xfolder
|
||||||
|
|
||||||
|
let contents = ['@echo off', 'echo filename1.txt:1:AAAA']
|
||||||
|
call writefile(contents, 'findstr.cmd')
|
||||||
|
|
||||||
|
let file1 = ['AAAA', 'THIS FILE SHOULD NOT BE FOUND']
|
||||||
|
let file2 = ['BBBB', 'THIS FILE SHOULD BE FOUND']
|
||||||
|
|
||||||
|
call writefile(file1, 'filename1.txt')
|
||||||
|
call writefile(file2, 'filename2.txt')
|
||||||
|
|
||||||
|
" use silent to avoid hit-enter-prompt
|
||||||
|
sil grep BBBB filename*.txt
|
||||||
|
|
||||||
|
call assert_equal('filename2.txt', @%)
|
||||||
|
|
||||||
|
let output = system('findstr BBBB filename*')
|
||||||
|
" Match trailing newline byte
|
||||||
|
call assert_match('filename2.txt:BBBB.', output)
|
||||||
|
|
||||||
|
set guioptions+=!
|
||||||
|
|
||||||
|
let output = system('findstr BBBB filename*')
|
||||||
|
call assert_match('filename2.txt:BBBB.', output)
|
||||||
|
|
||||||
|
cd -
|
||||||
|
set guioptions&
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
|||||||
@ -729,6 +729,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1947,
|
||||||
/**/
|
/**/
|
||||||
1946,
|
1946,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
Reference in New Issue
Block a user