CVE-2019-20807
Description
In Vim versions before 8.1.0881, restricted mode (rvim) can be bypassed via scripting interfaces to execute arbitrary OS commands.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Vim versions before 8.1.0881, restricted mode (rvim) can be bypassed via scripting interfaces to execute arbitrary OS commands.
Vulnerability
In Vim before version 8.1.0881, the restricted mode (rvim or vim -Z) fails to properly block execution of OS commands through scripting language interfaces such as Python, Ruby, Lua, Perl, and MzScheme. This allows users to circumvent the intended restrictions and run arbitrary shell commands.
Exploitation
An attacker with local access to a system where Vim is running in restricted mode can invoke scripting interfaces (e.g., :python, :lua, :ruby) to call operating system functions like os.execute() or system(). No special privileges are required beyond the ability to run Vim commands; the attacker simply needs to use the scripting language's built-in functions to bypass the shell command restrictions.
Impact
Successful exploitation enables an attacker to execute arbitrary operating system commands as the current user, leading to full compromise of the user's account and potential lateral movement. While restricted mode is not intended as a security boundary, this bypass allows any user in a restricted Vim session to gain arbitrary command execution.
Mitigation
The vulnerability is fixed in Vim version 8.1.0881 and later [2][3]. Users should update to at least this version. Apple included the fix in macOS Catalina 10.15.6, Security Update 2020-004 Mojave, and Security Update 2020-004 High Sierra (July 15, 2020) [1]. Ubuntu released updates in USN-4582-1 (October 14, 2020) [4]. No workaround is available; the only mitigation is to apply the patch or avoid using restricted mode in untrusted environments.
- About the security content of macOS Catalina 10.15.6, Security Update 2020-004 Mojave, Security Update 2020-004 High Sierra - Apple Support
- Release v8.1.0881: patch 8.1.0881: can execute shell commands in rvim through interfaces · vim/vim
- patch 8.1.0881: can execute shell commands in rvim through interfaces · vim/vim@8c62a08
- USN-4582-1: Vim vulnerabilities | Ubuntu security notices | Ubuntu
AI Insight generated on May 26, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
28- Vim/Vimdescription
- osv-coords26 versionspkg:rpm/opensuse/vim&distro=openSUSE%20Leap%2015.1pkg:rpm/suse/vim&distro=HPE%20Helion%20OpenStack%208pkg:rpm/suse/vim&distro=SUSE%20Enterprise%20Storage%205pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-ESPOSpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015-LTSSpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Basesystem%2015%20SP1pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Basesystem%2015%20SP2pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Desktop%20Applications%2015%20SP1pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Desktop%20Applications%2015%20SP2pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Point%20of%20Sale%2011%20SP3pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2011%20SP4-LTSSpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP2-BCLpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP2-LTSSpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP3-BCLpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP3-LTSSpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP4pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2012%20SP5pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%2015-LTSSpkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP2pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP3pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP4pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2012%20SP5pkg:rpm/suse/vim&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015pkg:rpm/suse/vim&distro=SUSE%20OpenStack%20Cloud%207pkg:rpm/suse/vim&distro=SUSE%20OpenStack%20Cloud%208pkg:rpm/suse/vim&distro=SUSE%20OpenStack%20Cloud%20Crowbar%208
< 8.0.1568-lp151.5.6.1+ 25 more
- (no CPE)range: < 8.0.1568-lp151.5.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 7.2-8.21.6.2
- (no CPE)range: < 7.2-8.21.6.2
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 8.0.1568-5.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
- (no CPE)range: < 7.4.326-17.6.1
Patches
18c62a08faf89patch 8.1.0881: can execute shell commands in rvim through interfaces
8 files changed · +151 −18
runtime/doc/starting.txt+10 −4 modified@@ -248,12 +248,18 @@ a slash. Thus "-R" means recovery and "-/R" readonly. changes and writing. {not in Vi} - *-Z* *restricted-mode* *E145* + *-Z* *restricted-mode* *E145* *E981* -Z Restricted mode. All commands that make use of an external shell are disabled. This includes suspending with CTRL-Z, - ":sh", filtering, the system() function, backtick expansion, - delete(), rename(), mkdir(), writefile(), libcall(), - job_start(), etc. + ":sh", filtering, the system() function, backtick expansion + and libcall(). + Also disallowed are delete(), rename(), mkdir(), job_start(), + etc. + Interfaces, such as Python, Ruby and Lua, are also disabled, + since they could be used to execute shell commands. Perl uses + the Safe module. + Note that the user may still find a loophole to execute a + shell command, it has only been made difficult. {not in Vi} *-g*
src/evalfunc.c+18 −4 modified@@ -6817,7 +6817,7 @@ f_histadd(typval_T *argvars UNUSED, typval_T *rettv) #endif rettv->vval.v_number = FALSE; - if (check_restricted() || check_secure()) + if (check_secure()) return; #ifdef FEAT_CMDHIST str = tv_get_string_chk(&argvars[0]); /* NULL on type error */ @@ -7898,6 +7898,9 @@ f_luaeval(typval_T *argvars, typval_T *rettv) char_u *str; char_u buf[NUMBUFLEN]; + if (check_restricted() || check_secure()) + return; + str = tv_get_string_buf(&argvars[0], buf); do_luaeval(str, argvars + 1, rettv); } @@ -8644,6 +8647,8 @@ f_mzeval(typval_T *argvars, typval_T *rettv) char_u *str; char_u buf[NUMBUFLEN]; + if (check_restricted() || check_secure()) + return; str = tv_get_string_buf(&argvars[0], buf); do_mzeval(str, rettv); } @@ -8932,6 +8937,9 @@ f_py3eval(typval_T *argvars, typval_T *rettv) char_u *str; char_u buf[NUMBUFLEN]; + if (check_restricted() || check_secure()) + return; + if (p_pyx == 0) p_pyx = 3; @@ -8950,6 +8958,9 @@ f_pyeval(typval_T *argvars, typval_T *rettv) char_u *str; char_u buf[NUMBUFLEN]; + if (check_restricted() || check_secure()) + return; + if (p_pyx == 0) p_pyx = 2; @@ -8965,6 +8976,9 @@ f_pyeval(typval_T *argvars, typval_T *rettv) static void f_pyxeval(typval_T *argvars, typval_T *rettv) { + if (check_restricted() || check_secure()) + return; + # if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) init_pyxversion(); if (p_pyx == 2) @@ -10819,7 +10833,7 @@ f_setbufvar(typval_T *argvars, typval_T *rettv UNUSED) typval_T *varp; char_u nbuf[NUMBUFLEN]; - if (check_restricted() || check_secure()) + if (check_secure()) return; (void)tv_get_number(&argvars[0]); /* issue errmsg if type error */ varname = tv_get_string_chk(&argvars[1]); @@ -11341,7 +11355,7 @@ f_settabvar(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = 0; - if (check_restricted() || check_secure()) + if (check_secure()) return; tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); @@ -14714,7 +14728,7 @@ f_writefile(typval_T *argvars, typval_T *rettv) blob_T *blob = NULL; rettv->vval.v_number = -1; - if (check_restricted() || check_secure()) + if (check_secure()) return; if (argvars[0].v_type == VAR_LIST)
src/ex_cmds.c+1 −1 modified@@ -4775,7 +4775,7 @@ check_restricted(void) { if (restricted) { - emsg(_("E145: Shell commands not allowed in rvim")); + emsg(_("E145: Shell commands and some functionality not allowed in rvim")); return TRUE; } return FALSE;
src/ex_docmd.c+6 −1 modified@@ -2007,11 +2007,16 @@ do_one_cmd( #ifdef HAVE_SANDBOX if (sandbox != 0 && !(ea.argt & SBOXOK)) { - /* Command not allowed in sandbox. */ + // Command not allowed in sandbox. errormsg = _(e_sandbox); goto doend; } #endif + if (restricted != 0 && (ea.argt & RESTRICT)) + { + errormsg = _("E981: Command not allowed in rvim"); + goto doend; + } if (!curbuf->b_p_ma && (ea.argt & MODIFY)) { /* Command not allowed in non-'modifiable' buffer */
src/if_perl.xs+5 −8 modified@@ -971,6 +971,7 @@ VIM_init(void) #ifdef DYNAMIC_PERL static char *e_noperl = N_("Sorry, this command is disabled: the Perl library could not be loaded."); #endif +static char *e_perlsandbox = N_("E299: Perl evaluation forbidden in sandbox without the Safe module"); /* * ":perl" @@ -1019,13 +1020,12 @@ ex_perl(exarg_T *eap) vim_free(script); } -#ifdef HAVE_SANDBOX - if (sandbox) + if (sandbox || secure) { safe = perl_get_sv("VIM::safe", FALSE); # ifndef MAKE_TEST /* avoid a warning for unreachable code */ if (safe == NULL || !SvTRUE(safe)) - emsg(_("E299: Perl evaluation forbidden in sandbox without the Safe module")); + emsg(_(e_perlsandbox)); else # endif { @@ -1037,7 +1037,6 @@ ex_perl(exarg_T *eap) } } else -#endif perl_eval_sv(sv, G_DISCARD | G_NOARGS); SvREFCNT_dec(sv); @@ -1298,13 +1297,12 @@ do_perleval(char_u *str, typval_T *rettv) ENTER; SAVETMPS; -#ifdef HAVE_SANDBOX - if (sandbox) + if (sandbox || secure) { safe = get_sv("VIM::safe", FALSE); # ifndef MAKE_TEST /* avoid a warning for unreachable code */ if (safe == NULL || !SvTRUE(safe)) - emsg(_("E299: Perl evaluation forbidden in sandbox without the Safe module")); + emsg(_(e_perlsandbox)); else # endif { @@ -1320,7 +1318,6 @@ do_perleval(char_u *str, typval_T *rettv) } } else -#endif /* HAVE_SANDBOX */ sv = eval_pv((char *)str, 0); if (sv) {
src/testdir/Make_all.mak+2 −0 modified@@ -213,6 +213,7 @@ NEW_TESTS = \ test_regexp_utf8 \ test_registers \ test_reltime \ + test_restricted \ test_retab \ test_ruby \ test_scriptnames \ @@ -375,6 +376,7 @@ NEW_TESTS_RES = \ test_quotestar.res \ test_regex_char_classes.res \ test_registers.res \ + test_restricted.res \ test_retab.res \ test_ruby.res \ test_scriptnames.res \
src/testdir/test_restricted.vim+107 −0 added@@ -0,0 +1,107 @@ +" Test for "rvim" or "vim -Z" + +source shared.vim + +func Test_restricted() + let cmd = GetVimCommand('Xrestricted') + if cmd == '' + return + endif + + call writefile([ + \ "silent !ls", + \ "call writefile([v:errmsg], 'Xrestrout')", + \ "qa!", + \ ], 'Xrestricted') + call system(cmd . ' -Z') + call assert_match('E145:', join(readfile('Xrestrout'))) + + call delete('Xrestricted') + call delete('Xrestrout') +endfunc + +func Run_restricted_test(ex_cmd, error) + let cmd = GetVimCommand('Xrestricted') + if cmd == '' + return + endif + + call writefile([ + \ a:ex_cmd, + \ "call writefile([v:errmsg], 'Xrestrout')", + \ "qa!", + \ ], 'Xrestricted') + call system(cmd . ' -Z') + call assert_match(a:error, join(readfile('Xrestrout'))) + + call delete('Xrestricted') + call delete('Xrestrout') +endfunc + +func Test_restricted_lua() + if !has('lua') + throw 'Skipped: Lua is not supported' + endif + call Run_restricted_test('lua print("Hello, Vim!")', 'E981:') + call Run_restricted_test('luado return "hello"', 'E981:') + call Run_restricted_test('luafile somefile', 'E981:') + call Run_restricted_test('call luaeval("expression")', 'E145:') +endfunc + +func Test_restricted_mzscheme() + if !has('mzscheme') + throw 'Skipped: MzScheme is not supported' + endif + call Run_restricted_test('mzscheme statement', 'E981:') + call Run_restricted_test('mzfile somefile', 'E981:') + call Run_restricted_test('call mzeval("expression")', 'E145:') +endfunc + +func Test_restricted_perl() + if !has('perl') + throw 'Skipped: Perl is not supported' + endif + " TODO: how to make Safe mode fail? + " call Run_restricted_test('perl system("ls")', 'E981:') + " call Run_restricted_test('perldo system("hello")', 'E981:') + " call Run_restricted_test('perlfile somefile', 'E981:') + " call Run_restricted_test('call perleval("system(\"ls\")")', 'E145:') +endfunc + +func Test_restricted_python() + if !has('python') + throw 'Skipped: Python is not supported' + endif + call Run_restricted_test('python print "hello"', 'E981:') + call Run_restricted_test('pydo return "hello"', 'E981:') + call Run_restricted_test('pyfile somefile', 'E981:') + call Run_restricted_test('call pyeval("expression")', 'E145:') +endfunc + +func Test_restricted_python3() + if !has('python3') + throw 'Skipped: Python3 is not supported' + endif + call Run_restricted_test('py3 print "hello"', 'E981:') + call Run_restricted_test('py3do return "hello"', 'E981:') + call Run_restricted_test('py3file somefile', 'E981:') + call Run_restricted_test('call py3eval("expression")', 'E145:') +endfunc + +func Test_restricted_ruby() + if !has('ruby') + throw 'Skipped: Ruby is not supported' + endif + call Run_restricted_test('ruby print "Hello"', 'E981:') + call Run_restricted_test('rubydo print "Hello"', 'E981:') + call Run_restricted_test('rubyfile somefile', 'E981:') +endfunc + +func Test_restricted_tcl() + if !has('tcl') + throw 'Skipped: Tcl is not supported' + endif + call Run_restricted_test('tcl puts "Hello"', 'E981:') + call Run_restricted_test('tcldo puts "Hello"', 'E981:') + call Run_restricted_test('tclfile somefile', 'E981:') +endfunc
src/version.c+2 −0 modified@@ -783,6 +783,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 881, /**/ 880, /**/
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- lists.opensuse.org/opensuse-security-announce/2020-06/msg00018.htmlmitrevendor-advisoryx_refsource_SUSE
- usn.ubuntu.com/4582-1/mitrevendor-advisoryx_refsource_UBUNTU
- seclists.org/fulldisclosure/2020/Jul/24mitremailing-listx_refsource_FULLDISC
- github.com/vim/vim/commit/8c62a08faf89663e5633dc5036cd8695c80f1075mitrex_refsource_MISC
- github.com/vim/vim/releases/tag/v8.1.0881mitrex_refsource_MISC
- lists.debian.org/debian-lts-announce/2022/01/msg00003.htmlmitremailing-listx_refsource_MLIST
- support.apple.com/kb/HT211289mitrex_refsource_CONFIRM
- www.starwindsoftware.com/security/sw-20220812-0003/mitrex_refsource_MISC
News mentions
0No linked articles in our index yet.