Medium severity4.4NVD Advisory· Published May 8, 2026· Updated May 14, 2026
CVE-2026-42307
CVE-2026-42307
Description
Vim is an open source, command line text editor. Prior to version 9.2.0383, an OS command injection vulnerability exists in the netrw standard plugin bundled with Vim. By inducing a user to open a crafted URL (e.g., using the sftp:// or file:// protocol handlers), an attacker can execute arbitrary shell commands with the privileges of the Vim process. This issue has been patched in version 9.2.0383.
Affected products
1Patches
1405e2fb6d54dpatch 9.2.0383: [security]: runtime(netrw): shell-injection via sftp: and file: URLs
6 files changed · +43 −16
runtime/doc/pi_netrw.txt+0 −4 modified@@ -2854,10 +2854,6 @@ your browsing preferences. (see also: |netrw-settings|) such as listing, file removal, etc. default: ssh - *g:netrw_tmpfile_escape* =' &;' - escape() is applied to all temporary files - to escape these characters. - *g:netrw_timefmt* specify format string to vim's strftime(). The default, "%c", is "the preferred date and time representation for the current
runtime/doc/tags+0 −1 modified@@ -7970,7 +7970,6 @@ g:netrw_ssh_browse_reject pi_netrw.txt /*g:netrw_ssh_browse_reject* g:netrw_ssh_cmd pi_netrw.txt /*g:netrw_ssh_cmd* g:netrw_sshport pi_netrw.txt /*g:netrw_sshport* g:netrw_timefmt pi_netrw.txt /*g:netrw_timefmt* -g:netrw_tmpfile_escape pi_netrw.txt /*g:netrw_tmpfile_escape* g:netrw_uid pi_netrw.txt /*g:netrw_uid* g:netrw_use_noswf pi_netrw.txt /*g:netrw_use_noswf* g:netrw_use_nt_rcp pi_netrw.txt /*g:netrw_use_nt_rcp*
runtime/pack/dist/opt/netrw/autoload/netrw.vim+11 −7 modified@@ -26,6 +26,8 @@ " 2026 Apr 05 by Vim Project Fix netrw#RFC2396() #19913 " 2026 Apr 15 by Vim Project Add missing escape() " 2026 Apr 19 by Vim Project expand ~ on Windows #20003 +" 2026 Apr 21 by Vim Project fix shell-injection via tempfile suffix (sftp://, file://) +" 2026 Apr 21 by Vim Project drop unused g:netrw_tmpfile_escape " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -400,7 +402,6 @@ else call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\') endif call s:NetrwInit("g:netrw_menu_escape",'.&? \') -call s:NetrwInit("g:netrw_tmpfile_escape",' &;') call s:NetrwInit("s:netrw_map_escape","<|\n\r\\\<C-V>\"") if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4') let s:treedepthstring= "│ " @@ -1821,14 +1822,14 @@ function netrw#NetRead(mode,...) "......................................... " NetRead: (sftp) NetRead Method #9 {{{3 elseif b:netrw_method == 9 - call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile) + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1)." ".netrw#os#Escape(tmpfile,1)) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice "......................................... " NetRead: (file) NetRead Method #10 {{{3 elseif b:netrw_method == 10 && exists("g:netrw_file_cmd") - call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_file_cmd." ".netrw#os#Escape(b:netrw_fname,1)." ".tmpfile) + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_file_cmd." ".netrw#os#Escape(b:netrw_fname,1)." ".netrw#os#Escape(tmpfile,1)) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice @@ -8965,14 +8966,17 @@ function s:GetTempfile(fname) endif " use fname's suffix for the temporary file + " Restrict the suffix to word characters so shell metacharacters in a + " remote filename (e.g. sftp://host/foo.txt;id) cannot ride along into + " the tempfile name and out into a downstream shell command. if a:fname != "" - if a:fname =~ '\.[^./]\+$' + if a:fname =~ '\.\w\+$' if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$' - let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + let suffix = ".tar".substitute(a:fname,'^.*\(\.\w\+\)$','\1','e') elseif a:fname =~ '.txz$' - let suffix = ".txz".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + let suffix = ".txz".substitute(a:fname,'^.*\(\.\w\+\)$','\1','e') else - let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + let suffix = substitute(a:fname,'^.*\(\.\w\+\)$','\1','e') endif let tmpfile= substitute(tmpfile,'\.tmp$','','e') let tmpfile .= suffix
runtime/pack/dist/opt/netrw/doc/netrw.txt+0 −4 modified@@ -2854,10 +2854,6 @@ your browsing preferences. (see also: |netrw-settings|) such as listing, file removal, etc. default: ssh - *g:netrw_tmpfile_escape* =' &;' - escape() is applied to all temporary files - to escape these characters. - *g:netrw_timefmt* specify format string to vim's strftime(). The default, "%c", is "the preferred date and time representation for the current
src/testdir/test_plugin_netrw.vim+30 −0 modified@@ -604,6 +604,36 @@ func Test_netrw_FileUrlEdit_pipe_injection() call assert_false(filereadable(fname), 'Command injection via pipe in file URL') endfunc +" The remote filename after '.' was allowed to contain shell metacharacters +" and rode unescaped into the tempfile name passed to sftp/file_cmd, giving a +" shell injection on :e sftp://host/foo.txt;<cmd>. +func Test_netrw_tempfile_suffix_injection() + CheckUnix + CheckExecutable id + let save_sftp = g:netrw_sftp_cmd + let save_file = exists('g:netrw_file_cmd') ? g:netrw_file_cmd : v:null + let g:netrw_sftp_cmd = 'true' + let g:netrw_file_cmd = 'true' + let fname = 'Xrce_marker' + try + call delete(fname) + sil! call netrw#NetRead(2, 'sftp://localhost/foo.txt;id>'..fname) + call assert_false(filereadable(fname), 'Command injection via sftp:// tempfile suffix') + + call delete(fname) + sil! call netrw#NetRead(2, 'file://localhost/foo.txt;id>'..fname) + call assert_false(filereadable(fname), 'Command injection via file:// tempfile suffix') + finally + call delete(fname) + let g:netrw_sftp_cmd = save_sftp + if save_file is v:null + unlet! g:netrw_file_cmd + else + let g:netrw_file_cmd = save_file + endif + endtry +endfunc + func Test_netrw_RFC2396() let fname = 'a%20b' call assert_equal('a b', netrw#RFC2396(fname))
src/version.c+2 −0 modified@@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 383, /**/ 382, /**/
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
3News mentions
0No linked articles in our index yet.