diff --git a/0001-patch-9.2.0073-security-possible-command-injection-u.patch b/0001-patch-9.2.0073-security-possible-command-injection-u.patch new file mode 100644 index 00000000..962ee487 --- /dev/null +++ b/0001-patch-9.2.0073-security-possible-command-injection-u.patch @@ -0,0 +1,56 @@ +diff -up vim91/runtime/autoload/netrw.vim.CVE-2026-28417 vim91/runtime/autoload/netrw.vim +--- vim91/runtime/autoload/netrw.vim.CVE-2026-28417 2026-03-17 19:22:17.101915588 +0100 ++++ vim91/runtime/autoload/netrw.vim 2026-03-17 19:32:29.134514079 +0100 +@@ -3376,13 +3376,26 @@ endif + + " s:NetrwValidateHostname: Validate that the hostname is valid {{{2 + " Input: +-" hostname ++" hostname, may include an optional username, e.g. user@hostname ++" allow a alphanumeric hostname or an IPv(4/6) address + " Output: + " true if g:netrw_machine is valid according to RFC1123 #Section 2 + fun! s:NetrwValidateHostname(hostname) +- " RFC1123#section-2 mandates, a valid hostname starts with letters or digits +- " so reject everyhing else +- return a:hostname =~? '^[a-z0-9]' ++ " Username: ++ let user_pat = '\%([a-zA-Z0-9._-]\+@\)\?' ++ " Hostname: 1-64 chars, alphanumeric/dots/hyphens. ++ " No underscores. No leading/trailing dots/hyphens. ++ let host_pat = '[a-zA-Z0-9]\%([-a-zA-Z0-9.]{,62}[a-zA-Z0-9]\)\?$' ++ ++ " IPv4: 1-3 digits separated by dots ++ let ipv4_pat = '\%(\d\{1,3}\.\)\{3\}\d\{1,3\}$' ++ ++ " IPv6: Hex, colons, and optional brackets ++ let ipv6_pat = '\[\?\%([a-fA-F0-9:]\{2,}\)\+\]\?$' ++ ++ return a:hostname =~? '^'.user_pat.host_pat || ++ \ a:hostname =~? '^'.user_pat.ipv4_pat || ++ \ a:hostname =~? '^'.user_pat.ipv6_pat + endfun + + " --------------------------------------------------------------------- +@@ -11880,15 +11893,15 @@ endfun + " a correct command for use with a system() call + fun! s:MakeSshCmd(sshcmd) + " call Dfunc("s:MakeSshCmd(sshcmd<".a:sshcmd.">) user<".s:user."> machine<".s:machine.">") +- if s:user == "" +- let sshcmd = substitute(a:sshcmd,'\',s:machine,'') +- else +- let sshcmd = substitute(a:sshcmd,'\',s:user."@".s:machine,'') ++ let machine = shellescape(s:machine, 1) ++ if s:user != '' ++ let machine = shellescape(s:user, 1).'@'.machine + endif ++ let sshcmd = substitute(a:sshcmd,'\',machine,'') + if exists("g:netrw_port") && g:netrw_port != "" +- let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.g:netrw_port,'') ++ let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(g:netrw_port,1),'') + elseif exists("s:port") && s:port != "" +- let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.s:port,'') ++ let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(s:port,1),'') + else + let sshcmd= substitute(sshcmd,"USEPORT ",'','') + endif diff --git a/0001-patch-9.2.0089-netrw-does-not-take-port-into-account.patch b/0001-patch-9.2.0089-netrw-does-not-take-port-into-account.patch new file mode 100644 index 00000000..15abf6ce --- /dev/null +++ b/0001-patch-9.2.0089-netrw-does-not-take-port-into-account.patch @@ -0,0 +1,39 @@ +diff -up vim91/runtime/autoload/netrw.vim.validateportnum vim91/runtime/autoload/netrw.vim +--- vim91/runtime/autoload/netrw.vim.validateportnum 2026-03-17 19:35:34.062575124 +0100 ++++ vim91/runtime/autoload/netrw.vim 2026-03-17 19:39:39.005999509 +0100 +@@ -3376,7 +3376,8 @@ endif + + " s:NetrwValidateHostname: Validate that the hostname is valid {{{2 + " Input: +-" hostname, may include an optional username, e.g. user@hostname ++" hostname, may include an optional username and port number, e.g. ++" user@hostname:port + " allow a alphanumeric hostname or an IPv(4/6) address + " Output: + " true if g:netrw_machine is valid according to RFC1123 #Section 2 +@@ -3385,17 +3386,19 @@ fun! s:NetrwValidateHostname(hostname) + let user_pat = '\%([a-zA-Z0-9._-]\+@\)\?' + " Hostname: 1-64 chars, alphanumeric/dots/hyphens. + " No underscores. No leading/trailing dots/hyphens. +- let host_pat = '[a-zA-Z0-9]\%([-a-zA-Z0-9.]{,62}[a-zA-Z0-9]\)\?$' ++ let host_pat = '[a-zA-Z0-9]\%([-a-zA-Z0-9.]\{0,62}[a-zA-Z0-9]\)\?' ++ " Port: 16 bit unsigned integer ++ let port_pat = '\%(:\d\{1,5\}\)\?$' + + " IPv4: 1-3 digits separated by dots +- let ipv4_pat = '\%(\d\{1,3}\.\)\{3\}\d\{1,3\}$' ++ let ipv4_pat = '\%(\d\{1,3}\.\)\{3\}\d\{1,3\}' + + " IPv6: Hex, colons, and optional brackets +- let ipv6_pat = '\[\?\%([a-fA-F0-9:]\{2,}\)\+\]\?$' ++ let ipv6_pat = '\[\?\%([a-fA-F0-9:]\{2,}\)\+\]\?' + +- return a:hostname =~? '^'.user_pat.host_pat || +- \ a:hostname =~? '^'.user_pat.ipv4_pat || +- \ a:hostname =~? '^'.user_pat.ipv6_pat ++ return a:hostname =~? '^'.user_pat.host_pat.port_pat || ++ \ a:hostname =~? '^'.user_pat.ipv4_pat.port_pat || ++ \ a:hostname =~? '^'.user_pat.ipv6_pat.port_pat + endfun + + " --------------------------------------------------------------------- diff --git a/0001-runtime-netrw-upstream-snapshot-of-v179.patch b/0001-runtime-netrw-upstream-snapshot-of-v179.patch new file mode 100644 index 00000000..f62fb1ac --- /dev/null +++ b/0001-runtime-netrw-upstream-snapshot-of-v179.patch @@ -0,0 +1,65 @@ +diff -up vim91/runtime/autoload/netrw.vim.validatehostname vim91/runtime/autoload/netrw.vim +--- vim91/runtime/autoload/netrw.vim.validatehostname 2024-02-09 06:33:54.000000000 +0100 ++++ vim91/runtime/autoload/netrw.vim 2026-03-17 19:16:22.210561235 +0100 +@@ -1453,6 +1453,10 @@ fun! netrw#Obtain(islocal,fname,...) + call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname) + endif + call s:NetrwMethod(b:netrw_curdir) ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + + if b:netrw_method == 4 + " obtain file using scp +@@ -2143,6 +2147,10 @@ fun! netrw#NetRead(mode,...) + " call Dret("netrw#NetRead : unsupported method") + return + endif ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix + + " Check whether or not NetrwBrowse() should be handling this request +@@ -2565,6 +2573,10 @@ fun! netrw#NetWrite(...) range + " call Dfunc("netrw#NetWrite : unsupported method") + return + endif ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + + " ============= + " NetWrite: Perform Protocol-Based Write {{{3 +@@ -3362,6 +3374,17 @@ if has("win95") && exists("g:netrw_win95 + endfun + endif + ++" s:NetrwValidateHostname: Validate that the hostname is valid {{{2 ++" Input: ++" hostname ++" Output: ++" true if g:netrw_machine is valid according to RFC1123 #Section 2 ++fun! s:NetrwValidateHostname(hostname) ++ " RFC1123#section-2 mandates, a valid hostname starts with letters or digits ++ " so reject everyhing else ++ return a:hostname =~? '^[a-z0-9]' ++endfun ++ + " --------------------------------------------------------------------- + " NetUserPass: set username and password for subsequent ftp transfer {{{2 + " Usage: :call NetUserPass() -- will prompt for userid and password +@@ -8842,6 +8865,10 @@ fun! s:NetrwUpload(fname,tgt,...) + + elseif a:tgt =~ '^ftp:' + call s:NetrwMethod(a:tgt) ++ if !s:NetrwValidateHostname(g:netrw_machine) ++ call netrw#ErrorMsg(s:ERROR,"Rejecting invalid hostname: <" .. g:netrw_machine .. ">",107) ++ return ++ endif + + if b:netrw_method == 2 + " handle uploading a list of files via ftp+.netrc diff --git a/vim.spec b/vim.spec index 4b2240ca..77c775e0 100644 --- a/vim.spec +++ b/vim.spec @@ -27,7 +27,7 @@ Summary: The VIM editor URL: http://www.vim.org/ Name: vim Version: %{baseversion}.%{patchlevel} -Release: 25%{?dist} +Release: 26%{?dist} License: Vim and MIT Source0: ftp://ftp.vim.org/pub/vim/unix/vim-%{baseversion}-%{patchlevel}.tar.bz2 Source1: virc @@ -155,6 +155,15 @@ Patch3057: 0001-patch-9.1.1551-security-path-traversal-issue-in-zip..patch # 0001-patch-9.1.2133-Another-case-of-buffer-overflow-with-.patch Patch3058: 0001-patch-9.1.2132-security-buffer-overflow-in-helpfile-.patch Patch3059: 0001-patch-9.1.2133-Another-case-of-buffer-overflow-with-.patch +# RHEL-155438 CVE-2026-28417 vim: Vim: Arbitrary code execution via OS command injection in the netrw plugin +# 3 patches: +# 0001-runtime-netrw-upstream-snapshot-of-v179.patch - introduces NetrwValidateHostname +# 0001-patch-9.2.0073-security-possible-command-injection-u.patch - CVE patch which sanitizes hostnames +# and reports invalid characters in SSH commands +# 0001-patch-9.2.0089-netrw-does-not-take-port-into-account.patch - include portnumber in hostname checking +Patch3060: 0001-runtime-netrw-upstream-snapshot-of-v179.patch +Patch3061: 0001-patch-9.2.0073-security-possible-command-injection-u.patch +Patch3062: 0001-patch-9.2.0089-netrw-does-not-take-port-into-account.patch # gcc is no longer in buildroot by default @@ -402,6 +411,9 @@ perl -pi -e "s,bin/nawk,bin/awk,g" runtime/tools/mve.awk %patch -P 3057 -p1 -b .CVE-2025-53906 %patch -P 3058 -p1 -b .tag-overflow %patch -P 3059 -p1 -b .tag-overflow2 +%patch -P 3060 -p1 -b .validatehostname +%patch -P 3061 -p1 -b .CVE-2026-28417 +%patch -P 3062 -p1 -b .validateportnum %build cd src @@ -410,8 +422,8 @@ autoconf sed -e "s+VIMRCLOC = \$(VIMLOC)+VIMRCLOC = /etc+" Makefile > Makefile.tmp mv -f Makefile.tmp Makefile -export CFLAGS="%{optflags} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2" -export CXXFLAGS="%{optflags} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2" +export CFLAGS="%{optflags} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -O0" +export CXXFLAGS="%{optflags} -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2 -O0" cp -f os_unix.h os_unix.h.save cp -f ex_cmds.c ex_cmds.c.save @@ -954,6 +966,9 @@ touch %{buildroot}/%{_datadir}/%{name}/vimfiles/doc/tags %endif %changelog +* Thu Mar 19 2026 Zdenek Dohnal - 2:8.2.2637-26 +- RHEL-155438 CVE-2026-28417 vim: Vim: Arbitrary code execution via OS command injection in the netrw plugin + * Tue Feb 10 2026 Zdenek Dohnal - 2:8.2.2637-25 - RHEL-147941 CVE-2026-25749 vim: Heap Overflow in Vim