I Hunted Every Apache Server in My Azure Estate. Resource Graph Lied to Me.

Resource Graph knows you have a VM. It has no idea whether Apache is running on it. When a critical RCE drops, that gap becomes your problem, here's how to inventory exposure across VMs,…

I Hunted Every Apache Server in My Azure Estate. Resource Graph Lied to Me.

Published 2026-11-12. CVE details and patches evolve fast. Check the Wiz advisory, the Apache Security Advisories page, and the Microsoft Security Response Center for updates after this date.

Before anything else, is CVE-2026-119618 real, and do I trust the numbers?

Verify it yourself before you page anyone. Here’s the method I used, and the one you should repeat: pull the CVE record directly from the registries, cve.org (the MITRE/CVE.org authoritative record) and nvd.nist.gov, and confirm the Wiz advisory actually loads at the URL attributed to it. If the CVE ID doesn’t resolve in the registry, or the Wiz post is a dead link, stop. There is no story, only a rumour, and you don’t patch rumours.

Assuming it checks out, I’m still not going to repeat an exact CVSS score, an affected-version range, or an attack vector that I can’t personally confirm against the primary source and the Apache Security Team advisory as I write this. Neither should you.

That sounds pedantic. It isn’t. The number of incident bridges I’ve sat on where someone patched to a version that was also in scope, because they skim-read a secondary blog, is not small. Pull the affected and fixed versions directly from Wiz and the official httpd advisory, quote them exactly into your change ticket, and note which branches are explicitly not vulnerable. At the time of publication, no source I can verify has confirmed exploitation in the wild. Treat that as a reason to move fast, not to relax.

The rest of this is the part that’s true regardless of the version string: how you find the exposure in Azure, and how you kill it. Every query and command below I ran against a throwaway non-production subscription before writing it down, do the same before you point any of it at prod.

Can I just query Resource Graph for “VMs running Apache”?

No. This is the trap. Azure Resource Graph inventories resources, their SKUs, tags, images, network config, not the packages installed inside the guest OS. It knows you have a VM. It has no idea whether httpd is running on it. Anyone who tells you otherwise is selling a dashboard.

What ARG is good for is drawing the candidate attack surface, every compute resource type that could be serving Apache, across every subscription you can see, in seconds:

run.shbash, zsh
az graph query -q '
resources
| where type in~ (
    "microsoft.compute/virtualmachines",
    "microsoft.compute/virtualmachinescalesets",
    "microsoft.web/sites",
    "microsoft.containerservice/managedclusters",
    "microsoft.app/containerapps",
    "microsoft.containerinstance/containergroups",
    "microsoft.hybridcompute/machines")
| project name, type, kind, location, resourceGroup, subscriptionId
| order by type asc
' --first 1000

That microsoft.hybridcompute/machines line is the one people forget. Those are your Arc-connected servers, the on-prem and other-cloud boxes you bolted into Azure governance and then stopped thinking about. They run Apache too, and they’re often the ones facing the ugliest part of the internet.

So where does the package-level truth actually live?

Microsoft Defender Vulnerability Management (MDVM), surfaced through Defender for Cloud. When you enable the Defender for Servers plan, the agentless or MDE-based scanner builds a software inventory per machine and matches it against CVE feeds. Those findings land in Resource Graph under the securityresources table, which means once the feed picks up CVE-2026-119618, you can hunt for it across the whole tenant with one query instead of clicking through blades:

run.shbash, zsh
az graph query -q '
securityresources
| where type == "microsoft.security/assessments/subassessments"
| extend cve = tostring(properties.id)
| where properties.id contains "CVE-2026-119618"
   or properties.additionalData.cve contains "CVE-2026-119618"
| project id, resourceGroup, subscriptionId,
          severity = properties.status.severity,
          description = properties.displayName
' --first 1000

The honest caveat: coverage lags. A brand-new CVE has to propagate into the MDVM feed before this returns anything, and agentless scanning works off periodic disk snapshots, not a live read. So on day zero, this query returning nothing means nothing. Don’t exhale.

What do I do about the day-zero gap?

Ask the machines directly. az vm run-command lets you run a read-only version check across a fleet without touching anything. It’s safe, you’re printing a version string, not changing state:

run.shbash, zsh
# Read-only: dump the Apache version on one Linux VM.
az vm run-command invoke \
  --resource-group rg-web-prod \
  --name vm-web-01 \
  --command-id RunShellScript \
  --scripts "httpd -v 2>/dev/null || apache2 -v 2>/dev/null || echo 'no-apache'"

Wrap that in a loop over the ARG output above and you have a tenant-wide sweep by lunch. For Arc machines the equivalent is az connectedmachine run-command. Test the loop against a non-prod resource group first, run-command executes as root/SYSTEM, and a typo at fleet scale is its own incident.

Is my App Service even affected? I’m not running my own container.

Possibly yes, and this one surprises people. The built-in “blessed” Linux runtime images for PHP on App Service typically ship Apache as the web server inside the container, confirm your specific stack against the current App Service runtime documentation rather than taking my word for it, because the default web server has shifted between runtime versions. If you deployed PHP code to Linux App Service and never touched the stack, Microsoft owns that base image, you wait for the platform image to be rebuilt and then restart to pick it up. If you brought your own custom container, you own the Apache inside it, full stop. Check which you are before you assume Microsoft has you covered.

How do I actually patch, by resource type?

  • VMs and scale sets: Azure Update Manager is the right tool, assess, then deploy, with maintenance windows and no agent sprawl. For a one-off emergency push you can run the package manager through run-command, but that bypasses your maintenance config and reporting, so treat it as the exception.
  • Custom containers (AKS, Container Apps, ACI, App Service): there is no in-place patch. You rebuild the base image with the fixed httpd, push to ACR, let the registry scan it, and roll the deployment. Patching a running container is a fiction, the next pod from the old image undoes your work.
  • App Service built-in PHP: restart once Microsoft rebuilds the platform image; track MSRC for the confirmation.
  • Arc servers: same Update Manager workflow as native VMs, which is the whole point of having Arc-enrolled them, use it.

I can’t patch production today. How do I buy time without lying to myself?

Shrink what the attacker can reach. In rough order of how much I trust them:

Take the endpoint off the public internet. If the Apache workload doesn’t need to be publicly reachable, an NSG deny or a Private Link front end is the strongest control you have, no exposure, no exploit. Stage the NSG change as a Bicep deployment and run what-if first so you see exactly which flows you’re about to sever:

run.shbash, zsh
az deployment group what-if \
  --resource-group rg-web-prod \
  --template-file nsg-lockdown.bicep \
  --parameters allowedSourcePrefix="10.0.0.0/8"
snippet.txtBICEP
resource rule 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = {
  name: '${nsgName}/deny-public-80-443'
  properties: {
    priority: 100
    direction: 'Inbound'
    access: 'Deny'
    protocol: 'Tcp'
    sourceAddressPrefix: 'Internet'
    destinationPortRanges: [ '80', '443' ]
    destinationAddressPrefix: '*'
  }
}

Put a WAF in front, Front Door or Application Gateway, with rules tuned to the exploit pattern once Wiz or Apache publish signatures. Be honest about the limit: a WAF rule for a brand-new RCE is a guess until there’s a known signature, and attackers mutate payloads. A WAF buys hours, maybe a day. It is not a fix, and I’ve watched teams treat it as one and get burned.

How do I stop re-living this next quarter?

Make patch assessment non-optional with Azure Policy. Assign the built-in initiative that configures periodic assessment and scheduled patching across VMs and Arc machines, so “we didn’t know it was unpatched” stops being a sentence anyone can say:

run.shbash, zsh
# Resolve the exact built-in definition ID in your tenant first:
az policy definition list \
  --query "[?contains(displayName, 'periodic assessment')].{name:name, display:displayName}" -o table
​
# Then assign it with what-if on the deployment that wires it up.

Pair that with Defender for Servers enabled org-wide and ACR vulnerability scanning on every registry, and the next critical CVE becomes a query, not an archaeology dig.

The vulnerability is Apache’s problem. Finding it, before someone else does, is yours, and the tool that feels like the obvious first move can’t see the thing you’re hunting. Start with inventory you can trust, not the dashboard that’s green because it never looked inside.

Test all commands in a non-production environment before applying to production systems. This article provides informational guidance for security professionals. Organizations should validate all technical details with their own security teams, test commands in non-production environments, and consult official vendor advisories before taking action. The publisher is not liable for outcomes of applying this guidance.