Skip to content

Defender: Internet-Exposed VM Remediation

Status: In Progress Created: 2026-02-27 Jira: DEVOPS-35, DEVOPS-123, DEVOPS-128, DEVOPS-142 Trigger: Daily Defender attack path alerts since 2026-01-27 — “Internet exposed Azure VM with high severity vulnerabilities” Subscription: 9639039c-86c6-4859-b014-d28688863cd5

Summary

Defender has identified VMs reachable from the internet with high severity vulnerabilities allowing remote code execution. Investigation found:

  • 2 deleted VMs with orphaned resources (public IPs, NSGs, NICs, disks still allocated)
  • 13 orphaned NSGs with wide-open rules (* source) not attached to any NIC
  • 4 deallocated/stopped VMs with public IPs and open NSGs (will be exposed if started)
  • 7 running VMs with public IPs and NSGs allowing inbound from the entire internet

Phase 1: Orphaned Resource Cleanup

No dev input needed — these resources have no VM behind them. Safe to delete.

1.1 — hisd-test-vm (hisd-testing)

  • VM confirmed deleted — only orphaned resources remain
  • Delete NIC hisd-test-vm218
  • Delete public IP hisd-test-vm-ip (52.180.95.103)
  • Delete NSG hisd-test-vm-nsg (had RDP 3389 + SQL 1433 + HTTP 80 from *)
  • Delete disk hisd-test-vm_298a3764bc5c4d4caddb6223c749b4d3
  • Check if hisd-testing RG can be deleted entirely

1.2 — vm-eshars-work (ops-esh-sbox)

  • VM confirmed deleted — only orphaned resources remain
  • Delete NIC vm-eshars-work722
  • Delete public IP vm-eshars-work-ip (13.82.69.62)
  • Delete NSG vm-eshars-work-nsg (had RDP 3389 from *)
  • Check if ops-esh-sbox RG can be deleted entirely

1.3 — Orphaned NSGs (not attached to any NIC)

All have open inbound rules from *. None are attached to a network interface.

#NSGResource GroupOpen PortsDeleted?
1vm-chris-deploytest-nsgvm-chris-deploytest_groupRDP 3389[ ]
2vm-cwl-test-nsgvm-chris-deploytest_groupRDP 3389[ ]
3vm-cwl-testmv-nsgrg-new-infra-southcentralusRDP 3389[ ]
48-year-cleanup-worker-vm-nsgrg-eshars-prodRDP 3389[ ]
5vm-cwl-test2-nsgrg-eshars-prodRDP 3389[ ]
6vm-eshars-build-nsgrg-eshars-prod8080[ ]
7vm-historic-cleanup-worker-nsgrg-eshars-prodRDP 3389[ ]
8vm-tmhp-prod2-nsgrg-eshars-prodRDP 3389[ ]
9vm-tmhp-prod3-nsgrg-eshars-prodRDP 3389[ ]
10vm-tmhp2-prod-nsgrg-eshars-prodRDP 3389[ ]
11vm-cwltest-2-nsgrg-eshars-nonprodRDP 3389[ ]
12vm-keyvault-test-nsgrg-eshars-nonprodRDP 3389[ ]
13vm-api-connect-nsgrg-eshars-nonprodRDP 3389[ ]

Investigate: vm-tmhp-prod-2-nsg (rg-eshars-nonprod) IS attached to NIC vm-tmhp-prod-2151 — do not delete without checking what it serves.


Phase 2: Deallocated/Stopped VMs

These VMs exist but are not running. They still have public IPs allocated (costing money) and open NSGs that will expose them if anyone starts them.

DEV TEAM: Please confirm for each VM — is it still needed? If yes, why?

2.1 — vm-eshars-dev (rg-eshars-dev) — DEALLOCATED

DetailValue
Public IP20.81.233.176
NSG open from *HTTP 80, FTP 20, FTP 21, passive FTP 60000-65000, Seq 5341
Power stateDeallocated
  • Dev response: Is this VM still needed? What is it used for?
  • Decision: Keep / Delete / Keep but lock down NSG
  • Action taken: _________________

2.2 — import-staging-deletion-agent (rg-eshars-historical-data-deletion) — DEALLOCATED

DetailValue
Public IP20.29.49.122
NSG open from *RDP 3389
Power stateDeallocated
  • Dev response: Is this VM still needed? Was this a one-time historical data deletion job?
  • Decision: Keep / Delete / Keep but lock down NSG
  • Action taken: _________________

2.3 — vm-tmhp-prod1 (rg-eshars-prod) — STOPPED

DetailValue
Public IP40.122.162.156 Removed 2026-03-21
NSG rules from *RDP 3389 (blocked by DenyAllInbound_Isolation at pri 100)
Power stateStopped (still billing for reserved resources)
  • Dev response: Is this decommissioned? vm-tmhp-prod is running — is prod1 a predecessor?
  • Decision: Keep / Delete / Keep but lock down NSG
  • Action taken: Public IP removed, orphaned IP deleted. Bastion allow rule added. (2026-03-21)

2.4 — vm-tmhp-int (rg-eshars-prod) — DEALLOCATED

DetailValue
Public IP52.165.170.168 Removed 2026-03-21
NSG open from *20150-25150 Set to Deny 2026-03-21
Power stateDeallocated
  • Dev response: Is this still needed? What are ports 20150-25150 for?
  • Decision: Keep / Delete / Keep but lock down NSG
  • Action taken: Public IP removed, passive FTP rule set to Deny, Bastion allow rule added. Zero network traffic — likely legacy/standby. (2026-03-21)

Phase 3: Running VMs — Nonprod

These VMs are running with public IPs and NSG rules allowing inbound from * (the entire internet).

DEV TEAM: For each VM, please answer:

  1. Does this VM need a public IP, or can it be accessed via Bastion/VPN?
  2. If it needs open ports, what source IPs should be allowed instead of *?
  3. Why is this port open? What service uses it?

3.1 — vm-db-api (rg-eshars-nonprod) — RUNNING

DetailValue
Public IP13.86.64.151
NSG open from *RDP 3389
RiskCRITICAL — RDP brute force from internet
  • Dev response: Why does a DB API VM need RDP from the internet?
  • Recommended: Remove * rule, use Bastion or restrict to office/VPN IP
  • Action taken: Public IP removed, AllowBastionRDP rule added, NAT gateway for outbound. (2026-03-21)

3.2 — vm-successed-nonprod (rg-eshars-nonprod) — RUNNING

DetailValue
Public IP20.15.225.23
NSG open from *FTP 21, SSH 22, ports 5001-5051
RiskHIGH — FTP is unencrypted, SSH brute force
  • Dev response: What service runs on this VM? Why FTP instead of SFTP? What are ports 5001-5051?
  • Recommended: Replace FTP with SFTP, restrict all source IPs
  • Action taken: Public IP removed, AllowBastionRDP rule added, NAT gateway for outbound. VM is no longer internet-exposed, but existing NSG open port rules still allow access from within the VNet/peered networks; inbound NSG tightening remains required. (2026-03-21)

3.3 — vm-jobscheduler (rg-eshars-nonprod) — RUNNING

DetailValue
Public IP20.114.116.215
NSG open from *HTTPS 443, WinRM 5986
RiskHIGH — WinRM allows remote management from internet
  • Dev response: Why does the nonprod job scheduler need HTTPS from the internet? What uses WinRM 5986?
  • Recommended: Restrict source IPs, remove WinRM * rule
  • Action taken: VM on separate VNet (southcentralus) — disk copy to centralus in progress. Will rebuild on main nonprod VNet, remove public IP. Old VM kept as fallback. (2026-03-21 — IN PROGRESS)

3.4 — vm-sql16-1-np (rg-eshars-nonprod-centralUS) — RUNNING

DetailValue
Public IP40.122.54.1
NSG open from *HTTP 80, HTTPS 443
RiskHIGH — SQL Server VM serving web traffic directly?
  • Dev response: Why does a SQL Server VM have HTTP/HTTPS open from the internet? What web service runs here?
  • Recommended: Put behind App Gateway or restrict source IPs
  • Action taken: _________________

Note (2026-03-03): DNS nonprodreports.myeshars.com resolves to 40.113.238.4 (vm-sql16-np). These VMs likely host SSRS + Eshars.ReportViewer for nonprod — same pattern as prod. Check MvcReportViewerReportServerUrl in kv-eshars-qa/kv-eshars-uat/kv-eshars-regression for HTTP URLs pointing at these VMs on port 80.

3.5 — vm-sql16-np (rg-eshars-nonprod) — RUNNING

DetailValue
Public IP40.113.238.4
NSG open from *HTTP 80, HTTPS 443
RiskHIGH — same concern as vm-sql16-1-np
  • Dev response: Same question — why HTTP/HTTPS from internet on a SQL VM?
  • Recommended: Put behind App Gateway or restrict source IPs
  • Action taken: _________________

Note (2026-03-03): This is the nonprod SSRS VM. nonprodreports.myeshars.com40.113.238.4. See prod findings in Phase 4.1 — same MvcReportViewerReportServerUrl HTTP pattern likely applies here.


Phase 4: Running VMs — Prod

Highest care required. Changes here affect production services.

4.1 — vm-ssrs16-prod (rg-eshars-prod) — RUNNING

DetailValue
Public IP23.99.180.223
NSG open from *HTTP 80, HTTPS 443
RiskHIGH — SSRS reports accessible from entire internet
DNSprodreports.myeshars.com23.99.180.223 (no App Gateway)

Investigation Findings (2026-03-03)

Port 443 (HTTPS) — required for user-facing access. SSRSReportServerUrl in all Key Vaults already uses HTTPS:

Key VaultValue
kv-eshars-prodhttps://prodreports.myeshars.com
kv-eshars-qahttps://nonprodreports.myeshars.com
kv-eshars-uathttps://nonprodreports.myeshars.com
kv-eshars-traininghttps://trainingreports.myeshars.com
kv-eshars-regressionhttps://nonprodreports.myeshars.com

Port 80 (HTTP) — required because of server-to-server calls. MvcReportViewerReportServerUrl in kv-eshars-prod is:

http://23.99.180.223/ReportServer

This is used by wa-eshars-prod (Eshars.WebUI) for server-side PDF rendering:

  • ReportsController.cs:724-730 — PDF downloads
  • SAFormService.cs:371 — SA form rendering
  • ReferralFormService.cs:323 — referral form rendering

The Eshars.ReportViewer app is deployed on this VM alongside SSRS. It calls SSRS via http://localhost/ReportServer (from kv-eshars-reportviewer), which is internal to the VM and unaffected by NSG rules.

Architecture:

User browser ──HTTPS 443──▶ vm-ssrs16-prod (IIS)
├── Eshars.ReportViewer ──http://localhost:80──▶ SSRS
└── SSRS ReportServer
wa-eshars-prod ──HTTP 80──▶ vm-ssrs16-prod:80/ReportServer ← PROBLEM
(WebUI App Service) (plain HTTP over public internet)

Remediation Steps

  1. Verify https://prodreports.myeshars.com/ReportServer responds correctly
  2. Update MvcReportViewerReportServerUrl in kv-eshars-prod to https://prodreports.myeshars.com/ReportServer
  3. Restart wa-eshars-prod to pick up new config
  4. Test: PDF downloads, SA forms, referral forms, progress notes
  5. Close port 80 on NSG, verify reports still work
  6. Repeat for nonprod vaults (kv-eshars-qa, kv-eshars-uat, kv-eshars-regression, kv-eshars-training)
  7. Restrict port 443 source IPs (currently *) — at minimum to wa-eshars-prod outbound IPs

4.2 — vm-eshars-sftp (vm-eshars-sftp_group) — RUNNING

DetailValue
Public IP40.77.98.52
NSG open from *SSH 22
RiskMEDIUM — SFTP by nature needs external access, but should be restricted
  • Dev response: Which external partners/IPs connect to this SFTP? (HISD, TMHP, others?)
  • Recommended: Restrict SSH 22 source to known partner IP ranges
  • Action taken: _________________

Remediation Log

DateItemActionBy
2026-02-27InvestigationAudited all VMs, public IPs, and NSGs in subscriptionLeon
2026-03-03vm-ssrs16-prodRoot cause found: MvcReportViewerReportServerUrl in kv-eshars-prod uses http://23.99.180.223/ReportServer (HTTP). User-facing SSRSReportServerUrl already HTTPS. Fix: update KV secret to HTTPS, then close port 80Leon
2026-03-21InfrastructureDeployed Azure Bastion (bastion-eshars-prod, Developer SKU) to prod VNet for RDP/SSH without public IPsLeon
2026-03-21InfrastructureDeployed NAT Gateway (natgw-eshars-prod + pip-natgw-prod) on default subnet for outbound internet after public IP removalLeon
2026-03-21vm-jobscheduler-prodRemoved public IP (40.86.93.124), deleted orphaned IP resource. Now private-only (10.0.0.9)Leon
2026-03-21vm-tmhp-intRemoved public IP (52.165.170.168), deleted orphaned IP resource. Now private-only (10.0.0.8). Set passive FTP rule (20150-25150) to DenyLeon
2026-03-21vm-tmhp-prodRemoved public IP, deleted orphaned IP resource. Now private-only (10.0.0.7)Leon
2026-03-21vm-rns-prodRemoved public IP (20.84.144.199), deleted orphaned IP resource. Now private-only (10.0.0.10)Leon
2026-03-21vm-tmhp-prod1Removed public IP (40.122.162.156), deleted orphaned IP resource. Now private-onlyLeon
2026-03-21NSGs (x6)Added AllowBastionRDP rule (pri 200, TCP 3389+22 from 10.0.1.0/26 + 168.63.129.16) to: vm-rns-prod-nsg, vm-jobscheduler-prod-nsg, vm-tmhp-int-nsg, vm-ssrs16-prod, vm-tmhp-prod1-nsg, vm-eshars-sftp-nsgLeon
2026-03-21Patch assessmentAll 4 assessed VMs fully patched (0 missing): vm-rns-prod, vm-jobscheduler-prod, vm-tmhp-prod, vm-tmhp-int. vm-eshars-sftp assessment failed (agent issue)Leon
2026-03-21vm-tmhp-prodConfirmed still active — 2.2 GB/day network traffic. Do not decommissionLeon

References

  • Defender Findings Register
  • Jira: DEVOPS-35, DEVOPS-123, DEVOPS-128, DEVOPS-142
  • Defender attack path subscription scope: 9639039c-86c6-4859-b014-d28688863cd5