From 86c57031110a25e362f30a17c1d2997bb947c7cb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 15 May 2026 09:05:23 +0800 Subject: [PATCH 1/2] vim-patch:9.2.0480: [security]: runtime(netrw): code injection via mf command Problem: [security]: runtime(netrw): code injection via mf command (Christopher Lusk, Zdenek Dohnal) Solution: Do not use string concatenation inside the filter() commands (Zdenek Dohnal) Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-66hr-7p6x-x5j3 https://github.com/vim/vim/commit/8af0f098c3a42a28661d0295364e6e0fd7dbc92c Co-authored-by: Christian Brabandt --- runtime/pack/dist/opt/netrw/autoload/netrw.vim | 9 ++++----- test/old/testdir/test_plugin_netrw.vim | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 36a1d5a604..b6f8da937b 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -1,7 +1,7 @@ " Creator: Charles E Campbell " Previous Maintainer: Luca Saccarola " Maintainer: This runtime file is looking for a new maintainer. -" Last Change: 2026 May 11 +" Last Change: 2026 May 14 " 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 @@ -5157,7 +5157,7 @@ function s:NetrwMarkFile(islocal,fname) else " remove filename from buffer's markfilelist - call filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname') + call filter(s:netrwmarkfilelist_{curbufnr}, {_, v -> v !=# a:fname}) if s:netrwmarkfilelist_{curbufnr} == [] " local markfilelist is empty; remove it entirely call s:NetrwUnmarkList(curbufnr,curdir) @@ -5178,7 +5178,6 @@ function s:NetrwMarkFile(islocal,fname) else " initialize new markfilelist - let s:netrwmarkfilelist_{curbufnr}= [] call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','','')) @@ -5198,7 +5197,7 @@ function s:NetrwMarkFile(islocal,fname) call add(s:netrwmarkfilelist,netrw#fs#ComposePath(b:netrw_curdir,a:fname)) else " remove new filename from global markfilelist - call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"') + call filter(s:netrwmarkfilelist, {_, v -> v !=# dname}) if s:netrwmarkfilelist == [] unlet s:netrwmarkfilelist endif @@ -7221,7 +7220,7 @@ function s:NetrwTreeDisplay(dir,depth) " hide given patterns let listhide= split(g:netrw_list_hide,',') for pat in listhide - call filter(w:netrw_treedict[dir],'v:val !~ "'.escape(pat,'\\').'"') + call filter(w:netrw_treedict[dir], {_, v -> v !~# pat}) endfor elseif g:netrw_hide == 2 diff --git a/test/old/testdir/test_plugin_netrw.vim b/test/old/testdir/test_plugin_netrw.vim index 759bc3e6db..cdc68403e5 100644 --- a/test/old/testdir/test_plugin_netrw.vim +++ b/test/old/testdir/test_plugin_netrw.vim @@ -732,4 +732,19 @@ func Test_netrw_bookmark_marked_file() bw! endfunc +func Test_netrw_mf_command_injection() + CheckUnix + CheckExecutable touch + let path = tempname() + let fname = 'x" . execute("silent! !touch poc") . "' + call mkdir(path, 'R') + exe "cd " path + call writefile([], fname) + Explore . + call search('^x') + :norm mf + :norm mf + call assert_false(filereadable('poc'), 'Command injection via mf command') +endfunc + " vim:ts=8 sts=2 sw=2 et From 10432d0df814ffeeee11ef2db118385add36a830 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 15 May 2026 09:06:11 +0800 Subject: [PATCH 2/2] vim-patch:9.2.0481: runtime(netrw): command injection possible via maps Problem: runtime(netrw): command injection possible via crafted directory names in NetrwMaps() (Christopher Lusk) Solution: Temporarily remove B flag in NetrwMaps() to prevent command injection https://github.com/vim/vim/commit/8e41c34aba0e775d2e3bf6f0e2da1b1c5317f3df Co-authored-by: Christian Brabandt --- .../pack/dist/opt/netrw/autoload/netrw.vim | 7 +++++++ test/old/testdir/test_plugin_netrw.vim | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index b6f8da937b..61c7ecf754 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -4814,6 +4814,12 @@ endfunction " s:NetrwMaps: {{{2 function s:NetrwMaps(islocal) + " remove B flag from 'cpo' so that \, \, etc. inside + " interpolated path names play back as literal text rather than + " the actual key — without this, a crafted directory name can + " inject keystrokes into the cmdline the mapping is typing + let _cpo = &cpo + set cpo-=B " mouse maps: {{{3 if g:netrw_mousemaps && g:netrw_retmap @@ -5058,6 +5064,7 @@ function s:NetrwMaps(islocal) " support user-specified maps call netrw#UserMaps(0) endif " }}}3 + let &cpo = _cpo endfunction " s:NetrwCommands: set up commands {{{2 diff --git a/test/old/testdir/test_plugin_netrw.vim b/test/old/testdir/test_plugin_netrw.vim index cdc68403e5..ca8746a590 100644 --- a/test/old/testdir/test_plugin_netrw.vim +++ b/test/old/testdir/test_plugin_netrw.vim @@ -738,6 +738,7 @@ func Test_netrw_mf_command_injection() let path = tempname() let fname = 'x" . execute("silent! !touch poc") . "' call mkdir(path, 'R') + let _cwd = getcwd() exe "cd " path call writefile([], fname) Explore . @@ -745,6 +746,25 @@ func Test_netrw_mf_command_injection() :norm mf :norm mf call assert_false(filereadable('poc'), 'Command injection via mf command') + exe "cd " _cwd + bw! endfunc +function Test_netrw_NetrwMaps_CR_dirname() + CheckNotMSWindows + + let tmpdir = tempname() . '/evil:let g:netrw_pwn=1' + call mkdir(tmpdir, 'pR') + call assert_true(isdirectory(tmpdir)) + exe ":Explore " tmpdir + " Fire D + " If the commands are injected successfully, + " this fails with + " Vim(let):E488: Trailing characters: \ @ command line script + call feedkeys("D\\", "xt") + call assert_false(exists("g:netrw_pwn")) + + unlet! g:netrw_pwn + bw! +endfunction " vim:ts=8 sts=2 sw=2 et