Skip to content

[Bugfix] Address command injection vulnerability in vm-repair extension; validate and quote tag values#10057

Open
EdwinBernal1 wants to merge 7 commits into
Azure:mainfrom
EdwinBernal1:edwin/msrc_bugfix
Open

[Bugfix] Address command injection vulnerability in vm-repair extension; validate and quote tag values#10057
EdwinBernal1 wants to merge 7 commits into
Azure:mainfrom
EdwinBernal1:edwin/msrc_bugfix

Conversation

@EdwinBernal1

@EdwinBernal1 EdwinBernal1 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Description

az vm repair create / az vm repair repair-and-restore build the underlying
az vm create call as a command string and, on Windows, execute it through
cmd.exe. Tag values copied from the source VM (--copy-tags) or supplied via
--tags were interpolated into that string without escaping. A tag value
containing shell metacharacters (for example &, |, >, <, ^) was therefore
re-interpreted by cmd.exe instead of being treated as literal text.

Because a tag value only requires Microsoft.Resources/tags/write on the source VM
to set, an operator running the command on Windows against an affected VM could have
attacker-influenced tag content executed on their workstation. Linux was not affected
(the argument vector is passed directly to the process with no shell).

This was reported through MSRC and is being addressed as a security fix.

Cause

  • custom.py flattened the merged tag dictionary into a single
    key=value key=value string and interpolated it into the az vm create command
    with no quoting.
  • repair_utils._call_az_command tokenized the string with shlex.split (POSIX
    mode, which preserves cmd.exe metacharacters) and, on Windows, prepended
    ['cmd', '/c'] and passed the list to subprocess.Popen. subprocess.list2cmdline
    only quotes tokens that contain whitespace, so a token such as env=ok&echo
    reached cmd.exe unquoted and the & was parsed as a command separator.

Fix

Defense in depth across both layers:

  1. Neutralize tags at the boundary (custom.py) — tag keys/values are validated
    (rejecting double quotes and control characters that cannot be represented as a
    single shell argument) and each key=value token is quoted with shlex.quote
    before it is interpolated into the command. This also fixes a pre-existing
    functional bug where tag values containing spaces were split into multiple tokens.

  2. Harden command execution (repair_utils._call_az_command) — on Windows the
    command is now built explicitly: every token is wrapped in double quotes
    (with CommandLineToArgvW-correct backslash/quote escaping via the new
    _quote_cmd_arg helper) and the whole command is invoked as
    cmd /s /c "<quoted tokens>".

    The /s flag plus a single outer pair of quotes is required: without /s,
    cmd.exe strips the first and last quote on the line (its documented /c
    behavior), which would unbalance the quoting around the final argument and
    re-expose metacharacters. With /s, cmd.exe strips exactly the outer quotes and
    parses the remainder verbatim, keeping every per-token quote balanced. The POSIX
    path is unchanged (argument list passed to subprocess, no shell).

Testing

Added azext_vm_repair/tests/latest/test_command_injection.py:

  • Unit tests for _quote_cmd_arg covering metacharacters (& | < > ^ ( ) && ||),
    embedded double quotes, single/multiple trailing backslashes, backslash-before-quote,
    internal backslashes, empty/space-only values, and normal paths.
  • Tests asserting the Windows command is built as cmd /s /c "..." with each token
    quoted, and that the POSIX path passes an argument list with no shell.
  • Windows-only (skipped elsewhere) real cmd.exe round-trip tests that execute the
    exact production construction and assert each payload is received by the target
    program verbatim, plus an injection probe that fails if a metacharacter payload is
    able to create a marker file.
  • A test asserting non-az command strings are still rejected.

All tests pass; the previously affected payloads are delivered as literal,
single arguments with no command execution.

History

  • Bumped vm-repair extension version to 2.2.1.
  • Added a 2.2.1 entry to HISTORY.rst.

This checklist is used to make sure that common guidelines for a pull request are followed.

Related command

az vm repair create and az vm repair repair-and-restore (shared --tags / --copy-tags handling and the internal _call_az_command helper).

General Guidelines

  • Have you run azdev style <YOUR_EXT> locally? (pip install azdev required)
  • Have you run python scripts/ci/test_index.py -q locally? (azdev required; see .azure-pipelines/templates/azdev_setup.yml for the install command until azdev==0.2.11b1 is on PyPI)
  • My extension version conforms to the Extension version schema

For new extensions:

About Extension Publish

There is a pipeline to automatically build, upload and publish extension wheels.
Once your pull request is merged into main branch, a new pull request will be created to update src/index.json automatically.
You only need to update the version information in file setup.py and historical information in file HISTORY.rst in your PR but do not modify src/index.json.

Copilot AI review requested due to automatic review settings July 1, 2026 12:23
@azure-client-tools-bot-prd

Copy link
Copy Markdown
Validation for Breaking Change Starting...

Thanks for your contribution!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR mitigates a Windows-specific command injection risk in the vm-repair extension by ensuring untrusted tag data (from --copy-tags / --tags) cannot be re-interpreted by cmd.exe when az vm create is executed via a command string.

Changes:

  • Validate and POSIX-quote each key=value tag token before interpolating tags into the az vm create command string.
  • Harden _call_az_command on Windows to invoke cmd /s /c "..." with per-token CommandLineToArgvW-compatible quoting via a new _quote_cmd_arg helper.
  • Add regression tests (including Windows-only cmd.exe round-trip tests), and bump extension version/history to 2.2.1.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/vm-repair/setup.py Bumps extension version to 2.2.1.
src/vm-repair/HISTORY.rst Adds 2.2.1 release notes describing the security fix.
src/vm-repair/azext_vm_repair/repair_utils.py Adds _quote_cmd_arg and updates _call_az_command to safely build Windows cmd /s /c invocation.
src/vm-repair/azext_vm_repair/custom.py Validates tag key/value content and quotes each key=value token before embedding into the command string.
src/vm-repair/azext_vm_repair/tests/latest/test_command_injection.py Adds unit + Windows-only integration-style tests for quoting and injection prevention.

Comment thread src/vm-repair/azext_vm_repair/custom.py Outdated
@yonzhan

yonzhan commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

VM

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread src/vm-repair/azext_vm_repair/repair_utils.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread src/vm-repair/azext_vm_repair/repair_utils.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto-Assign Auto assign by bot Compute

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants