10 March 2025
Last month, Seth Art from Datadog Security Labs published an excellent post on AWS cloud image confusion attacks. In this post, I’ll explain how Azure has a similar issue with its CLI.
If you haven’t seen the Datadog Security Labs post, I highly recommend you check it out. It’s a great read and provides a lot of context for the issue I’ll be discussing here. They do have the better title pun though.
When provisioning virtual machines within the cloud, users typically specify an image to use as the base for the VM. This image is often referred to by a name or ID. In the case of AWS, the image is referred to as an Amazon Machine Image (AMI) and is identified by an AMI ID. In Azure, the image is referred to as a Virtual Machine Image and is identified by a URN which is comprised of a combination of a publisher name, an offer name, a SKU, and a version, all concatenated by a colon (e.g. Canonical:ubuntu-24_04-lts:server:24.04.202502210
).
An image confusion attack occurs when an attacker is able to create an image with a name that matches the search or filter criteria that a user is using to select their intended image. This can lead to the attacker’s image being selected instead of the legitimate image. An attacker will generally create an image that acts just like the legitimate image, but with some additional functionality that can be used to compromise the user’s environment with remote code execution, data exfiltration, or other malicious activities. In the AWS example, this was done using the AWS CLI command aws ec2 describe-images
and Terraform data providers which performed a search for images based on the name or partial name of the image, which could include the attacker’s image.
In 2023, I was looking at how GitHub advised deploying its GitHub Enterprise Server offering on Azure. The documentation at the time advised using the Azure CLI to determine the latest version of the GitHub Enterprise Server image as follows:
$ az vm image list --all -f GitHub-Enterprise | grep '"urn":' | sort -V
This command would list all the images available in Azure that had an offer name of “GitHub-Enterprise” and then sort them by version number. The user could then select the latest version of the image to use for their deployment. Notably, the command did not filter by publisher name or SKU, only by offer name. This meant that an attacker could create an image with the offer name “GitHub-Enterprise” under their separate publisher identifier and have it appear in the list of images returned by the command. Publisher identifiers are unique in Azure, but not offers, SKUs or versions.
In Azure, to register an image which has a public URN, you list your offering on the Azure Marketplace via the Azure Partner Center. After some KYC checks, you can register any arbitrary publisher identifier. In my case, I registered “ghes” for GitHub Enterprise Server.
I then created an offer with the version number of “99.99.99” to ensure my image would appear as the latest image in the list.
I also selected the option to hide the plan from the Azure Marketplace UI, which would prevent users from more clearly identifying the difference.
This specific offer was not fully published to the Azure Marketplace to avoid direct customer impact to GitHub customers and was instead reported to GitHub. Though GitHub stated that these findings “do not present a significant security risk”, they have since updated their documentation to use a specific filter for the GitHub Enterprise Server image, as follows:
az vm image list --all -f GitHub-Enterprise | grep '"urn": "GitHub:' | sort -V
This change specifically filters the images by the publisher name “GitHub” and the offer name “GitHub-Enterprise”. If you are a provider looking to avoid this issue, I would recommend you follow this pattern in your documentation, or alternatively provide a full list of URNs for your users to select from.
In my testing of Marketplace publication, I found that when executing a deployment of my free marketplace VM image using az vm create
, Azure would initially reject my request to deploy the image. This was because the terms of the Marketplace image was not yet “accepted”.
The user would be required to execute az vm image accept-terms
or az vm image terms accept
to accept the terms of the image before the deployment could proceed. I found this to be initially confusing as images like the base Ubuntu image or the GitHub Enterprise Server image did not require this step. After some investigation and a support ticket, Microsoft confirmed this was an undocumented trait of certain images in the Azure Marketplace. Microsoft stated:
The GitHub Enterprise Server offering [sic] is a 1PP product (Core Virtual Machine) and not an Azure Virtual Machine(3PP) which are created by 3PP Publishers in-fact Marketplace Partners. Not all the partners in marketplace are allowed to create the 1PP offer and only few approved Marketplace Partners are allowed to create 1PP VM offers. And in the 1PP marketplace offers will be auto accepted the terms and conditions.
This limits the attack surface of this image confusion attack for Azure, as users would need to accept the terms of the image before deploying it, however many Marketplace images do have the requirement to accept terms before deployment.
Those of you with keen eyes will notice that the updated image search command for the GitHub example uses grep to filter the publisher of the image and not the --publisher -p
argument that exists for the az vm image list command. In fact, the use of the --publisher
flag is what many publishers such as F5, AlmaLinux and even at one point Canonical advise their users do to find the latest images for their offerings.
Using only the CLI-provided flags however makes the results still susceptible to the above attack as the --publisher
flag, as well as the --offer
and --sku
flags, are wildcarded by default. This means that if you were to register a publisher with a name that starts with the intended target publisher name, you could still have your image appear in the list of images returned by the command.
This is the reason why the updated GitHub command uses grep to filter the publisher name.
The partial search seems to only be an issue with specifically the az vm image list
command. Other commands such as az vm create
or az vm image accept-terms
do not have this issue and instead seem to directly concatenate the provided publisher, offer and SKU to form the URN. The same seems to be the case for most Terraform plans as the term latest
can be used in lieu of a version number to deploy the latest image, negating the need for a search data provider.
Similar to the official response from AWS, I believe most providers will consider this to be working as intended. The Azure CLI is a tool that is designed to be used by administrators and developers who are expected to have a certain level of knowledge about the resources they are working with and the burden of ensuring the publisher is correct would generally fall on the user.
However, as we have seen with the GitHub example, this can lead to confusion and potential security risks. Azure removing the partial wildcard nature within the az vm image list
command would mitigate this risk but this would likely be too much of a breaking change to be considered by the Azure team.
If you liked what I’ve written, or want to hear more on this topic, reach out to me on 𝕏 at @iann0036.