# REPRO-2026-00124: Vim modeline handling for the tabpanel option allows sandbox escape via autocmd_add, enabling OS command execution when opening a crafted file. ## Summary Status: published Severity: high Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00124 GHSA: GHSA-2GMJ-RPQF-PXVH CVE: CVE-2026-34714 ## Package Name: Vim Ecosystem: github Affected: Treat Vim < 9.2.0272 as affected Fixed: 9.2.0272 ## Root Cause # 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 ## Reproduction Details Reproduced: 2026-04-01T10:08:17.251Z Duration: 1178 seconds Tool calls: 255 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00124 pruva-verify GHSA-2GMJ-RPQF-PXVH pruva-verify CVE-2026-34714 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00124&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00124/artifacts/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - GitHub Advisory: https://github.com/advisories/GHSA-2GMJ-RPQF-PXVH - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-34714 ## Artifacts - repro/rca_report.md (analysis, 4903 bytes) - repro/reproduction_steps.sh (reproduction_script, 3989 bytes) - vuln_variant/rca_report.md (analysis, 5889 bytes) - vuln_variant/reproduction_steps.sh (reproduction_script, 7352 bytes) - bundle/AGENTS.repro.md (documentation, 462 bytes) - bundle/ticket.json (other, 4150 bytes) - bundle/ticket.md (ticket, 3694 bytes) - repro/bang_test.txt (other, 80 bytes) - repro/bang_test2.txt (other, 72 bytes) - repro/base64_test.txt (other, 105 bytes) - repro/base64_test2.txt (other, 74 bytes) - repro/debug_poc.txt (other, 157 bytes) - repro/dict_test.txt (other, 47 bytes) - repro/dict_test2.txt (other, 52 bytes) - repro/escape_brace.txt (other, 81 bytes) - repro/escape_colon.txt (other, 69 bytes) - repro/escape_test.txt (other, 71 bytes) - repro/execute_test.txt (other, 72 bytes) - repro/execute_test2.txt (other, 72 bytes) - repro/expr_poc.txt (other, 150 bytes) - repro/expr_test.txt (other, 63 bytes) - repro/final_poc.txt (other, 83 bytes) - repro/fn_test.txt (other, 61 bytes) - repro/fn_test2.txt (other, 62 bytes) - repro/len_test.txt (other, 48 bytes) - repro/length_test.txt (other, 97 bytes) - repro/length_test2.txt (other, 74 bytes) - repro/length_test3.txt (other, 60 bytes) - repro/list_test.txt (other, 55 bytes) - repro/minimal_expr.txt (other, 47 bytes) - repro/poc.txt (other, 135 bytes) - repro/poc2.txt (other, 51 bytes) - repro/poc3.txt (other, 154 bytes) - repro/poc4.txt (other, 154 bytes) - repro/poc5.txt (other, 185 bytes) - repro/quote_test.txt (other, 74 bytes) - repro/redraw_poc.txt (other, 144 bytes) - repro/reeval_test.txt (other, 56 bytes) - repro/reeval_test2.txt (other, 83 bytes) - repro/result_poc.txt (other, 145 bytes) - repro/runtime_manifest.json (other, 649 bytes) - repro/short2_poc.txt (other, 58 bytes) - repro/short_poc.txt (other, 134 bytes) - repro/simple_expr.txt (other, 65 bytes) - repro/simple_poc.txt (other, 43 bytes) - repro/simplest_poc.txt (other, 46 bytes) - repro/single_quote.txt (other, 77 bytes) - repro/statusline_test.txt (other, 52 bytes) - repro/string_dict.txt (other, 79 bytes) - repro/test.vim (other, 246 bytes) - repro/test_flags.vim (other, 118 bytes) - repro/validation_verdict.json (other, 722 bytes) - repro/vim9_dict.txt (other, 70 bytes) - repro/working_poc.txt (other, 80 bytes) - logs/full_exploit.txt (other, 152 bytes) - logs/modeline_test.txt (other, 42 bytes) - logs/t1.txt (other, 23 bytes) - logs/vuln_variant/final_verification.log (log, 3269 bytes) - logs/vuln_variant/ph_test.txt (other, 38 bytes) - logs/vuln_variant/printheader_fixed_test.txt (other, 38 bytes) - logs/vuln_variant/tabpanel_fixed.txt (other, 35 bytes) - logs/vuln_variant/tabpanel_fixed_test.txt (other, 12 bytes) - logs/vuln_variant/test1_modeline.txt (other, 42 bytes) - logs/vuln_variant/test1_output.txt (other, 23 bytes) - logs/vuln_variant/test3_output.txt (other, 38 bytes) - logs/vuln_variant/test3_printheader.txt (other, 77 bytes) - logs/vuln_variant/test4_modeline.txt (other, 74 bytes) - logs/vuln_variant/test4_output.txt (other, 12 bytes) - logs/vuln_variant/test6_output.txt (other, 38 bytes) - logs/vuln_variant/test7_output.txt (other, 15 bytes) - logs/vuln_variant/test7_titlestring.txt (other, 77 bytes) - logs/vuln_variant/ts_test.txt (other, 15 bytes) - logs/vuln_variant/variant_run.log (log, 3233 bytes) - logs/vuln_variant/variant_run_final.log (log, 3269 bytes) - logs/vuln_variant/variant_test1.txt (other, 146 bytes) - logs/vuln_variant/variant_test2.txt (other, 146 bytes) - logs/vuln_variant/variant_test3.txt (other, 74 bytes) - vuln_variant/patch_analysis.md (documentation, 4774 bytes) - vuln_variant/source_identity.json (other, 748 bytes) - vuln_variant/validation_verdict.json (other, 2387 bytes) - vuln_variant/variant_manifest.json (other, 2383 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00124 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00124/artifacts/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00124 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev