§ 3 · Findings — detail
Three weaknesses, each reproducible from the cited evidence.
§ 3.1 · INFRA-PORT-01-A
Critical
Open recursive DNS resolver on both domain controllers.
Status. Escalated from v1 High (passive evidence of port 53 open) to Critical (active confirmation of recursive resolution behaviour).
Active evidence — DCSvr-001 (203.0.113.11)
$ dig +time=5 +tries=1 google.com A @203.0.113.11
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16318
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
google.com. 86 IN A 172.253.122.101
google.com. 86 IN A 172.253.122.113
google.com. 86 IN A 172.253.122.100
google.com. 86 IN A 172.253.122.139
google.com. 86 IN A 172.253.122.102
google.com. 86 IN A 172.253.122.138
;; Query time: 23 msec
Active evidence — DCSvr-002 (203.0.113.12)
$ dig +time=5 +tries=1 google.com A @203.0.113.12
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
google.com. 120 IN A 172.253.62.138 ... (5 more rows)
;; Query time: 28 msec
Recursion proof — random subdomain
Proves the DC is recursing, not just returning a cached entry.
$ dig +time=5 +tries=1 a3f9b1c2d0e4f5a8.example.com A @203.0.113.11
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5635
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; Query time: 35 msec
A non-recursive server would have returned REFUSED. This server returned NOERROR with an AUTHORITY section pointing at the parent zone — proving it walked the DNS hierarchy on our behalf.
Why critical
This is the textbook “open recursive resolver” pattern. The IPs will be picked up by:
- Open Resolver Project (openresolverproject.org) — public list, scraped by attackers.
- Shadowserver — distributed daily reports to network owners and CSIRTs.
- Spamhaus DROP / EDROP — if the IPs get used in amplification, they land here, and any mail server using Spamhaus will reject your outbound email.
- DNS-amplification botnets — once on the lists, attackers point UDP-spoofed queries with large ANY responses at your IP. Your IP is the source of the attack from the victim's perspective; you eat the bandwidth.
Remediation guidance
-
Snapshot first.
az snapshot create --resource-group <rg> --name DCSvr-001-pre-fix-$(date +%Y%m%d) --source <osdisk-id>
az snapshot create --resource-group <rg> --name DCSvr-002-pre-fix-$(date +%Y%m%d) --source <osdisk-id>
-
Identify and replace the offending NSG rules.
In Azure Portal: each DC VM → Networking → Inbound port rules → look for any rule with destination port 53 and source * / Internet / 0.0.0.0/0. Replace with a rule allowing only your trusted CIDRs (on-prem office, VPN client pool, internal VNet), then add an explicit Deny on Internet→53 below it.
$nsg = Get-AzNetworkSecurityGroup -ResourceGroupName <rg> -Name <nsg-of-DCSvr-001>
$nsg | Set-AzNetworkSecurityRuleConfig -Name "Allow-DNS-Internal" -Access Allow `
-Protocol "*" -Direction Inbound -Priority 100 `
-SourceAddressPrefix @("10.0.0.0/8","172.16.0.0/12","<on-prem-CIDR>","<VPN-pool>") `
-SourcePortRange "*" -DestinationAddressPrefix "*" -DestinationPortRange "53"
$nsg | Set-AzNetworkSecurityRuleConfig -Name "Deny-DNS-Internet" -Access Deny `
-Protocol "*" -Direction Inbound -Priority 110 `
-SourceAddressPrefix "Internet" -SourcePortRange "*" `
-DestinationAddressPrefix "*" -DestinationPortRange "53"
$nsg | Set-AzNetworkSecurityGroup
# Repeat for DCSvr-002
-
Disable recursion for non-trusted clients on the DC itself (via Bastion).
# Hard option — disables recursion entirely (use if no internet-bound clients need it)
Set-DnsServerRecursion -Enable $false
# Soft option — keep recursion but only for trusted scope
Set-DnsServerRecursionScope -Name "." -EnableRecursion $false
Add-DnsServerRecursionScope -Name "InternalOnly" -EnableRecursion $true
Set-DnsServerQueryResolutionPolicy -Name "InternalRecursion" `
-Action ALLOW -ServerInterfaceIP "EQ,<DC-internal-IP>" `
-ApplyOnRecursion -RecursionScope "InternalOnly"
# Always enable the global query block list
Set-DnsServerGlobalQueryBlockList -Enable $true -List "wpad","isatap"
-
Patch verification.
Get-HotFix | Where-Object { $_.HotFixID -eq 'KB4569509' } # SIGRed
Get-HotFix | Sort-Object InstalledOn -Descending | Select -First 5
-
Verify externally.
Re-run from any internet-connected host (or ask the assessor):
dig +time=5 +tries=1 google.com A @203.0.113.11
dig +time=5 +tries=1 google.com A @203.0.113.12
Expected new behaviour: connection timed out or REFUSED. If qr rd ra flag still appears with answer rows, the change did not take effect.
Long-term
architectural option
Migrate internal DNS to Azure DNS Private Resolver. The DCs lose their need for public IPs entirely, closing this finding class permanently.
§ 3.2 · INFRA-PORT-01-B
Critical
VPN Gateway PIP1 exposes a Windows Communication Foundation service stack on six ports.
Status. Escalated from v1 Medium (one diagnostic port observed via Shodan) to Critical (six ports of WCF / .NET service surface confirmed via active probing).
Active evidence — AzureVPN-PIP1 (203.0.113.21)
PORT STATE SERVICE NOTES
7999/tcp open http (Microsoft-HTTPAPI/2.0) WCF default page, "Endpoint not found"
8081/tcp open https (XML response, 1565 bytes) WCF service template (HTTP 200)
8443/tcp open https (Microsoft-HTTPAPI/2.0) Valid TLS cert, ALPN h2/http1.1
Subject: 3a4b5c6d-4444-4d55-af66-3007b882cd10.gwt.cloudapp.net
Issuer: CN=CCME G1 TLS RSA 2048 SHA256 2049 CUS CA 01
Valid: 2026-02-27 → 2027-02-23
Body: "Windows® Communication Foundation service.
Metadata publishing for this service is currently disabled."
10001/tcp open https (HTTP 200, 6437 bytes) Same WCF body
10002/tcp open https (HTTP 200, 6437 bytes) Same WCF body
20000/tcp open https (HTTP 200, 6437 bytes) Same WCF body
For comparison — PIP2 (203.0.113.22) shows one port
PORT STATE SERVICE
8083/tcp open us-srv (closes connection on direct request, no banner)
The asymmetry is the smoking gun
Both PIPs are bound to the same Azure VPN Gateway resource (3a4b5c6d-4444-4d55-af66-3007b882cd10). In an active-active VPN Gateway deployment, both PIPs should be functionally equivalent — same service surface, same NSG, same gateway image. The fact that PIP1 exposes six management/.NET ports and PIP2 exposes one means either:
- Microsoft's gateway image is configured asymmetrically — one PIP is the “primary management” PIP and the other is the “client traffic” PIP, contrary to Azure documentation that says active-active means equivalent.
- An NSG (yours or Microsoft's) is filtering PIP2 but not PIP1.
- A failover event has left PIP1 in a different state than PIP2 — possibly stuck mid-reconfiguration.
Any of these is a Microsoft support case.
Why critical
Six ports of WCF service surface on a customer-facing public IP with the kernel-mode Microsoft-HTTPAPI/2.0 (http.sys) underneath:
- http.sys CVE class. MS15-034 (CVE-2015-1635) is a remote pre-auth RCE in
http.sys that affected every Windows server with a kernel-mode HTTP listener. Subsequent CVEs have appeared in the same code path (CVE-2021-31166, CVE-2022-21907). Six exposed http.sys listeners is six attack surfaces.
- WCF deserialisation CVEs. WCF has a long history of unsafe deserialisation (CVE-2020-0646
NetDataContractSerializer, multiple others). The “Metadata publishing is currently disabled” body is the WCF default — it does not mean the underlying SOAP/REST endpoints are absent; it means metadata exchange (the WSDL) is hidden. The service is still listening.
- You cannot patch this from your side. This is the gateway appliance OS image. Microsoft owns the patch cadence.
- Spectre of customer-managed misconfiguration. Even if Microsoft's intent is to expose only documented IKE / IPsec ports, the asymmetry between PIP1 and PIP2 suggests something on your side or theirs is different between the two — and the difference is in your favour to investigate.
Remediation guidance
-
Document the asymmetry in an Azure support case.
Suggested title:
Azure VPN Gateway PIPs 203.0.113.21 and 203.0.113.22 show asymmetric port exposure — PIP1 exposes ports 7999, 8081, 8443, 10001, 10002, 20000 (Microsoft-HTTPAPI/2.0 + WCF “Endpoint not found”); PIP2 exposes only 8083. Both are bound to the same gateway resource 3a4b5c6d-4444-4d55-af66-3007b882cd10. Please confirm: (a) is this asymmetry intended? (b) is the WCF management plane on PIP1 reachable from public internet by design or by misconfiguration? (c) confirm patch level of http.sys against MS15-034 / CVE-2021-31166 / CVE-2022-21907; (d) if not intended, how do we suppress?
Attach: nmap output, TLS cert details for 8443, the HTML body returned by 7999 / 8081 / 10001 / 10002 / 20000, and this report ID.
Severity in Microsoft's case-grading scheme: B (Moderate) at minimum; bump to A (Critical) if Microsoft pushes back or does not substantively reply — the active-active misalignment can also affect VPN failover, which is a production risk.
-
Confirm Gateway SKU and image vintage.
az network vnet-gateway show --resource-group <rg> --name <vpn-gw-name> \
--query "{sku:sku, generation:vpnGatewayGeneration, type:gatewayType, vpnType:vpnType, activeActive:activeActive, enableBgp:enableBgp}"
Acceptable: VpnGw1 and above, vpnGatewayGeneration: Generation2.
Not acceptable: Basic, Generation1. If on Basic, plan a migration to VpnGw1AZ or higher (zone-redundant) — the migration will cause a brief gateway re-image which may also clear the asymmetric state.
-
Confirm the cert.
The TLS cert on 8443 is dated 2026-02-27 → 2027-02-23 and chained to CCME G1 TLS RSA 2048 SHA256 2049 CUS CA 01 (Microsoft Cloud-Managed Endpoint). Consistent with a recently re-keyed gateway. If the cert pre-dates a known Microsoft VPN Gateway maintenance event, that event may have re-introduced the asymmetric exposure — note the date in the support ticket.
-
Compensating control.
Your customer NSG cannot directly close gateway-side listeners, but you can document the exposure and watch the NSG flow logs on the gateway subnet for any actual connection attempts to those ports. If anything other than known Microsoft IPs hits 7999 / 8081 / 8443 / 10001 / 10002 / 20000, escalate.
§ 3.3 · INFRA-IP-01
Medium
IP recycling — two client IPs were previously used by other Microsoft customers.
Two of the client's public IPs were previously used by other Microsoft Azure customers per Mnemonic PassiveDNS:
- 203.0.113.31 (currently labelled ACC-001) — historically pgflexserver0748291630551207.postgres.database.azure.com (last seen 2023-08-20) and azuregateway-1a2b3c4d-0a0a-4bcc-85dd-966d1ee82d76-b7788c99d0e1.vpn.azure.com (last seen 2020-10-15).
- 203.0.113.22 (currently labelled AzureVPN-PIP2) — historically ext.quietmeadow-2b7c1d09.eastus.azurecontainerapps.io, scm.quietmeadow-2b7c1d09…, internal.quietmeadow-2b7c1d09… (last seen 2024-10-25).
Why this matters
Azure recycles public IPs across tenants. Any allowlists, OAuth redirect URIs, monitoring probes, callback URLs, or HSTS-pinned client apps that the previous tenants configured to point at these IPs are now sending traffic to your servers. The risk is bidirectional: stale inbound traffic from the previous tenants' partners may show up as false positives in your monitoring, and when you eventually release these IPs, the next tenant will receive the traffic that was destined for you.
Active scan partial confirmation
The historic Postgres surface on 203.0.113.31 is closed (port 5432 returned filtered); the historic ACA HTTP surface on 203.0.113.22 is also no longer responding on 80 / 443. So the recycled-tenant services are not currently reachable through the client's IPs — but DNS / firewall-allowlist references at third parties may still be active.
Remediation guidance
-
Audit current behaviour on the recycled IPs.
From a VM inside the same VNet (does not touch the public IP):
Test-NetConnection -ComputerName <internal-ip-of-ACC-001> -Port 5432
Test-NetConnection -ComputerName <internal-ip-of-AzureVPN-PIP2-VM> -Port 80
Test-NetConnection -ComputerName <internal-ip-of-AzureVPN-PIP2-VM> -Port 443
Each should TimedOut or report Blocked unless those services are intentionally part of your environment.
-
Use Azure Public IP Prefix for IPs that need stability.
Where an IP genuinely needs to outlive a single VM (e.g. for partner allowlists), allocate from a Microsoft.Network/publicIPPrefixes resource so the prefix is reserved to your subscription and rotating the VM behind it does not change the IP.
az network public-ip prefix create \
--resource-group <rg> --name contoso-stable-ips \
--length 28 --location <region>
az network public-ip create --resource-group <rg> --name contoso-app-pip1 \
--public-ip-prefix contoso-stable-ips
-
Document the recycling pattern in the DR runbook.
When investigating any unusual traffic on these IPs, the on-call engineer should know to check historic Mnemonic PassiveDNS data first to rule out “stale traffic from a previous tenant.”