Arbitrary Code Execution via C Omni-Completion in Vim < 9.2.0735
================================================================
Date: 26.06.2026
Severity: Medium
CVE: *requested, not yet assigned*
CWE: Improper Neutralization of Special Elements (CWE-94),
     Inclusion of Functionality from Untrusted Control Sphere (CWE-829)

## Summary
The C omni-completion script in `runtime/autoload/ccomplete.vim` interpolates
the `typeref:` / `typename:` extension field of a `tags` entry, without
escaping, into a `:vimgrep` pattern that is run through `:execute`.  Because
`:vimgrep` honors the bar (`|`) as a command separator, a crafted tag field
can close the search pattern and append an arbitrary Ex command.  Opening a
hostile `.c` file whose project `tags` file contains such an entry and
invoking C omni-completion runs that command as the editing user.

## Description
`runtime/ftplugin/c.vim` installs `omnifunc=ccomplete#Complete` on every C
buffer when Vim has filetype plugins enabled.  When the user invokes omni-
completion with `CTRL-X CTRL-O` on a structure-member access (for example
`myvar.field`), the completer looks up the variable in the `tags` files,
reads the type from the entry's `typeref:` (or `typename:`) field, and
searches the `tags` files for members of that type.

The member search builds a `:vimgrep` command and runs it with `:execute`.
The type name taken from the tag field is concatenated directly into the
search pattern with no escaping, while the file-name argument is passed
through `escape()`.  The `typeref:` field is parsed verbatim by `taglist()`;
its only constraint is that it contains no internal whitespace, so the
characters `/ | ' " ( )` all survive.

`:vimgrep` carries the `EX_TRLBAR` attribute, which means an unescaped bar in
the constructed command line terminates the `:vimgrep` and begins a new Ex
command.  A tag field of the form `x/|<command>|"` therefore closes the
regular expression with `/`, starts a fresh Ex command after `|`, and comments
out the remainder of the generated line.

## Impact
Arbitrary local code execution as the user running Vim, with that user's full
credential set, file-system access, and network egress.  Realistic delivery
vectors include:

- cloning or checking out a third-party repository that ships a `tags` file
  alongside its C sources,
- extracting a source tarball or archive whose layout places a hostile `tags`
  file next to the `.c` file being inspected,
- auditing a malware sample or untrusted source tree in its own directory.

Exploitation requires:

- Vim with filetype plugins enabled (`filetype plugin on`, the default in
  `runtime/defaults.vim` and most distribution `vimrc` files),
- a `tags` file reachable through the `'tags'` option (the default `./tags`
  resolves to the attacker's file in the conventional ctags workflow, where
  Vim's working directory is the project root),
- the victim opening the hostile `.c` file and invoking C omni-completion on a
  member access.

The severity is rated Medium because the user must manually invoke omni-
completion on a member access after opening the file; the bug does not fire on
file-open alone and the `ccomplete.vim` produces an error message, which makes
the whole attack quite noticeable.

## Acknowledgements
The Vim project would like to thank Cipher / Causal Security
(https://causalsecurity.com/) for reporting and analyzing the issue and
providing a proof of concept.

## References
The issue has been fixed as of Vim patch 
[v9.2.0735](https://github.com/vim/vim/releases/tag/v9.2.0735).
- 
[Commit](https://github.com/vim/vim/commit/6b611b0d15603c52ebdad17172b0232b4f65704e)
- [Github Security 
Advisory](https://github.com/vim/vim/security/advisories/GHSA-mf92-v4xw-j45x)


Thanks,
Christian
-- 
Ich schoß ihn über seinen eigenen Haufen.
                -- Heinz Erhardt

Reply via email to