From 334a13bff78aa0ad206bc436885f63e3a0bab399 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 2 Mar 2025 19:33:51 +0100 Subject: [PATCH] patch 9.1.1164: [security]: code execution with tar.vim and special crafted tar files Problem: editing a special crafted tar file allows code execution (RyotaK, after 129a8446d23cd9cb4445fcfea259cba5e0487d29) Solution: escape the filename before feeding it to the `:read` command Github Advisory: https://github.com/vim/vim/security/advisories/GHSA-wfmf-8626-q3r3 Signed-off-by: Christian Brabandt --- runtime/autoload/tar.vim | 29 ++++++++++++++++------------- src/version.c | 2 ++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 1c2ed78c19..14e68b7899 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -11,6 +11,7 @@ " 2025 Feb 06 by Vim Project: add support for lz4 (#16591) " 2025 Feb 28 by Vim Project: add support for bzip3 (#16755) " 2025 Mar 01 by Vim Project: fix syntax error in tar#Read() +" 2025 Mar 02 by Vim Project: escape the filename before using :read " " Contains many ideas from Michael Toren's " @@ -284,6 +285,8 @@ fun! tar#Read(fname,mode) set report=10 let tarfile = substitute(a:fname,'tarfile:\(.\{-}\)::.*$','\1','') let fname = substitute(a:fname,'tarfile:.\{-}::\(.*\)$','\1','') + " be careful not to execute special crafted files + let escape_file = fname->fnameescape() " changing the directory to the temporary earlier to allow tar to extract the file with permissions intact if !exists("*mkdir") @@ -361,13 +364,13 @@ fun! tar#Read(fname,mode) if tarfile =~# '\.bz2$' exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif tarfile =~# '\.bz3$' exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif tarfile =~# '\.\(gz\)$' exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif tarfile =~# '\(\.tgz\|\.tbz\|\.txz\)' if has("unix") && executable("file") let filekind= system("file ".shellescape(tarfile,1)) @@ -376,40 +379,40 @@ fun! tar#Read(fname,mode) endif if filekind =~ "bzip2" exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif filekind =~ "bzip3" exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif filekind =~ "XZ" exe "sil! r! xz -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif filekind =~ "Zstandard" exe "sil! r! zstd --decompress --stdout -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file else exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file endif elseif tarfile =~# '\.lrp$' exe "sil! r! cat -- ".shellescape(tarfile,1)." | gzip -d -c - | ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif tarfile =~# '\.lzma$' exe "sil! r! lzma -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif tarfile =~# '\.\(xz\|txz\)$' exe "sil! r! xz --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file elseif tarfile =~# '\.\(lz4\|tlz4\)$' exe "sil! r! lz4 --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file else if tarfile =~ '^\s*-' " A file name starting with a dash is taken as an option. Prepend ./ to avoid that. let tarfile = substitute(tarfile, '-', './-', '') endif exe "silent r! ".g:tar_cmd." -".g:tar_readoptions.shellescape(tarfile,1)." ".tar_secure.shellescape(fname,1).decmp - exe "read ".fname + exe "read ".escape_file endif redraw! diff --git a/src/version.c b/src/version.c index 25c29256c8..6fd80d6b12 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1164, /**/ 1163, /**/