Human
Machine
REPRO-2026-00124 HIGH
Verified
Vim modeline handling for the tabpanel option allows sandbox escape via autocmd_add, enabling OS command execution when opening a crafted file.
Vim (github) Apr 1, 2026
What's the vulnerability?
Vim modeline handling for the tabpanel option allows sandbox escape via autocmd_add, enabling OS command execution when opening a crafted file.
Root Cause Analysis
# Root Cause Analysis: GHSA-2gmj-rpqf-pxvh
## 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:
```c
{"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:
```c
{"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:
```c
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:
```vim
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:**
```bash
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 autocommand
- `logs/modeline_test.txt` - Crafted modeline test file
- `logs/t1.txt` - Verification that tabpanel was set via modeline
### Build Evidence
- Vim v9.2.0271 built from source with `+tabpanel` feature
- Build artifacts in `vim-repo/src/vim`
## Attack Chain
1. Attacker crafts a file with a malicious modeline
2. Victim opens the file in Vim (with default settings - `modeline` enabled)
3. Modeline sets `tabpanel` to an expression containing `autocmd_add()` call
4. When tabpanel is evaluated (on redraw), expression executes in sandbox
5. `autocmd_add()` registers malicious autocommand (no sandbox check)
6. After sandbox exits, autocommand triggers on next event
7. Arbitrary command executes with victim's privileges
## Recommendations
### Immediate Actions
1. **Upgrade** to Vim 9.2.0272 or later
2. **Workaround:** Disable modelines in `.vimrc`:
```vim
set nomodeline
```
### Testing Recommendations
1. Verify `P_MLE` flag is set on all expression-evaluating options
2. Ensure all sandbox-escaping functions call `check_secure()`
3. 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:
1. Builds Vim if not present
2. Cleans up previous test artifacts
3. Runs all three tests
4. Reports results
One Command
Verify with pruva-verify
Run the Pruva CLI to automatically fetch and execute the reproduction script.
pruva-verify REPRO-2026-00124 or
pruva-verify GHSA-2GMJ-RPQF-PXVH or
pruva-verify CVE-2026-34714 Install:
curl -fsSL https://pruva.dev/install.sh | sh Or Run Manually
1
Download the script
curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00124/artifacts/reproduction_steps.sh 2
Make executable
chmod +x reproduction_steps.sh 3
Run the script
./reproduction_steps.sh Run in a VM, container, or disposable environment. This exploits a real vulnerability.
How Pruva Reproduced This
Watch the AI agent's step-by-step process.
Loading session...
Artifacts
No artifacts available