The District of Columbia Department of Health Care Finance (DHCF) is notifying nearly 400,000 Medicaid and DC Healthcare Alliance beneficiaries that their personal information may have been exposed — not through a zero-day, not through ransomware, but through reports published on a publicly accessible website.
This is the breach pattern that keeps showing up in healthcare incident queues, and it deserves more attention than it gets: sensitive beneficiary reports — generated as part of normal business operations for enrollees in DC Medicaid and the DC Healthcare Alliance program — were placed on, or made reachable from, a public-facing website where anyone with the URL could retrieve them. No authentication bypass. No malware. Just an access-control failure on a workflow that touched protected health information (PHI) and personally identifiable information (PII) at scale.
For defenders, this incident class is uncomfortable precisely because it bypasses most of the detection stack. Your EDR saw nothing. Your IDS saw nothing. The data walked out the front door over ordinary HTTPS, served by a web server doing exactly what it was configured to do. If your organization publishes any operational reports to web-accessible paths — and if you run Medicaid, managed care, claims, or eligibility systems, you almost certainly do — you need to treat this as a live-fire audit prompt, not a news item.
Technical Analysis: How 'Accidental Publication' Breaches Actually Happen
The affected environment
Based on the notification, the exposure involves reports containing personal information of individuals enrolled in DC Medicaid and the DC Healthcare Alliance between the affected enrollment window. At ~400,000 records, this is one of the larger government healthcare exposures of the past year and squarely within HIPAA breach-notification territory (the 500-record HHS Office for Civil Rights 'wall of shame' threshold was exceeded by three orders of magnitude).
No CVE is associated with this incident. This is not a vulnerability in a product — it is a process and configuration failure. That distinction matters for your threat model, because it means no patch will save you.
The attack chain — from a defender's perspective
In engagements where we've investigated exposures of this type, the failure almost always decomposes into one of these four mechanisms:
- Wrong destination on an automated transfer. A scheduled job (SFTP push, Robocopy, PowerShell, an ETL tool like SSIS or Informatica) copies periodic beneficiary/eligibility reports to a directory that is mapped into a public web root — for example, a path under
C:\inetpub\wwwroot\on IIS or/var/www/html/on Apache/Nginx — instead of the intended internal share or authenticated portal. - Directory listing enabled on a staging path. Reports land in a 'temporary' or 'staging' folder on a public server. No index file exists, and the server has autoindex/directory browsing enabled, so the entire contents are enumerable by anyone — and by search-engine crawlers.
- Unauthenticated predictable URLs. Reports are intentionally web-published but 'protected' only by obscurity — sequential IDs, dated filenames (
eligibility_report_2025-11.pdf), or GUIDs that leak through referrers, logs, or partner systems. No authentication layer, no authorization check per object. - Vendor/contractor infrastructure drift. The reports live on a third-party or contractor-operated site whose configuration was never in scope for the covered entity's own hardening program. This is the most common variant in government healthcare — the business associate's web tier is where the control actually failed.
Exploitation status
'Exploitation' here is trivial and passive: any party who discovered the URL could download the data, and there is no way to retrospectively prove they didn't. In similar incidents, discovery has come from security researchers, search-engine indexing (Google/Bing crawl of directory listings), and — worst case — data appearing on breach forums months after the exposure window opened. Defenders should assume that any PHI sitting unauthenticated on a public web path for weeks or months was collected, most likely at minimum by automated crawlers and internet-wide scanners.
The compliance exposure is severe: HIPAA Breach Notification Rule obligations (45 CFR §§ 164.400–414), HHS OCR reporting for 500+ record breaches, state notification statutes, and — because this is a Medicaid population — potential CMS and state AG scrutiny. For business associates, this is exactly the scenario that terminates contracts.
Detection & Response
The hard truth: by the time you're writing detection rules, the data is already gone. The goal of the detections below is threefold — (a) catch mass retrieval of exposed report paths while the exposure is live, (b) catch the process-level behavior that stages PHI into web-served directories in the first place, and (c) hunt your own estate right now for files that should not be public.
Sigma Rules
The first rule targets the staging behavior — administrative copy tooling placing report-like files into IIS web roots, which is almost never legitimate outside a controlled deployment pipeline. The second targets bulk retrieval patterns in web server logs. The third catches enabling of directory browsing on IIS, a common precursor or co-condition of these exposures.
---
title: Sensitive Report Files Copied Into Public Web Root
id: 3f8a2c14-9b61-4e07-a5d2-8c1e7f30a921
status: experimental
description: Detects administrative copy tooling (robocopy, xcopy, copy, PowerShell Copy-Item) writing report or export files into IIS web-served directories, a common mechanism in accidental PHI publication incidents.
references:
- https://attack.mitre.org/techniques/T1567/
- https://attack.mitre.org/techniques/T1030/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.exfiltration
- attack.t1567
- attack.t1030
logsource:
category: process_creation
product: windows
detection:
selection_tool:
Image|endswith:
- '\robocopy.exe'
- '\xcopy.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\cmd.exe'
selection_dest:
CommandLine|contains:
- 'inetpub\wwwroot'
- 'inetpub\wwwroot\reports'
- '\wwwroot\'
- '\www\'
selection_data:
CommandLine|contains:
- 'report'
- 'export'
- 'eligibility'
- 'beneficiar'
- 'enroll'
- 'claim'
- '.csv'
- '.xlsx'
condition: selection_tool and selection_dest and selection_data
falsepositives:
- Controlled application deployment pipelines publishing non-sensitive report templates
- Web administrators publishing intended public content
level: high
---
title: IIS Directory Browsing Enabled via AppCmd or PowerShell
id: 91c4d6e8-2a57-4b3c-9f01-6d8e2a47b055
status: experimental
description: Detects configuration changes enabling directory browsing on IIS, which exposes enumerable file listings on public paths and is a frequent factor in accidental data publication breaches.
references:
- https://attack.mitre.org/techniques/T1590/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.discovery
- attack.t1590
logsource:
category: process_creation
product: windows
detection:
selection_appcmd:
Image|endswith: '\appcmd.exe'
CommandLine|contains:
- 'directoryBrowse'
- '/enabled:true'
selection_ps:
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
CommandLine|contains:
- 'DirectoryBrowsing'
- 'Set-WebConfigurationProperty'
- 'directoryBrowse'
condition: 1 of selection_*
falsepositives:
- Legitimate intranet file-share sites intentionally using directory browsing
level: medium
---
title: Bulk Download From Web Server Report Directories
id: 5d71b9a3-43e6-48fc-b2a9-07c5e19f38d4
status: experimental
description: Detects single source IPs requesting an abnormally high number of files from report, export, or document paths on web servers, consistent with scraping of accidentally published data.
references:
- https://attack.mitre.org/techniques/T1530/
- https://attack.mitre.org/techniques/T1213/
author: Security Arsenal
date: 2026/01/15
tags:
- attack.collection
- attack.t1530
- attack.t1213
logsource:
category: webserver
detection:
selection:
cs-uri-stem|contains:
- '/reports/'
- '/report/'
- '/exports/'
- '/export/'
- '/documents/'
- '/files/'
- '/downloads/'
- '/staging/'
- '/temp/'
filter_status:
sc-status:
- 200
- 206
condition: selection and filter_status
falsepositives:
- Legitimate high-volume API consumers or partner integrations
- Search engine crawlers (validate against known crawler IP ranges before suppression)
level: medium
Note on the third rule: web-log Sigma rules like this are most useful when your pipeline enriches with per-source aggregation. Deploy it as a thresholded correlation (e.g., >50 requests from one c-ip to these paths within 10 minutes) in your SIEM rather than as a raw per-event alert, or it will drown you.
KQL — Microsoft Sentinel / Defender
This hunt assumes IIS W3C logs ingested into W3CIISLog (or Azure Application Gateway / front-end logs in AzureDiagnostics), with a fallback against CommonSecurityLog for proxy/firewall visibility. It surfaces external IPs pulling report-like objects at volume, and separately flags responses serving files with PII-adjacent names from paths with no authentication.
// Hunt: bulk retrieval of report/export files by external clients
// Tune threshold and path list to your estate; baseline crawler IPs first.
let ReportPaths = dynamic(["/reports/", "/report/", "/exports/", "/export/", "/documents/", "/staging/", "/temp/", "/files/"]);
let KnownCrawlers = dynamic(["googlebot", "bingbot", "slurp", "duckduckbot", "baiduspider"]);
W3CIISLog
| where TimeGenerated > ago(30d)
| where scStatus in ("200", "206")
| where csUriStem has_any (ReportPaths)
| where csUserAgent has_any (KnownCrawlers) == false
| where ipv4_is_private(cIP) == false
| summarize
FileRequests = count(),
DistinctFiles = dcount(csUriStem),
BytesSent = sum(toint(scBytes)),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
SamplePaths = make_set(csUriStem, 25)
by cIP, csUserAgent, Computer
| where DistinctFiles > 20 or FileRequests > 100
| sort by BytesSent desc;
// Companion hunt: files with beneficiary/PII naming patterns served at all
W3CIISLog
| where TimeGenerated > ago(30d)
| where scStatus == "200"
| where csUriStem matches regex @"(?i)(eligib|beneficiar|enroll|claim|member|roster|phi|pii|ssn|medicaid|834|835|270|271)"
| where csUriStem endswith_any (".csv", ".xlsx", ".xls", ".pdf", ".txt", ".zip")
| summarize Requests = count(), Clients = dcount(cIP), ClientIPs = make_set(cIP, 15)
by csUriStem, Computer
| sort by Requests desc;
If you only have perimeter visibility, run the same logic against CommonSecurityLog filtering on RequestURL/RequestContext instead of csUriStem. The second query is the high-value one: if files named like eligibility rosters or 834 enrollment transactions are being served by a web tier at all, you need to know why — today.
Velociraptor VQL — Hunt Your Own Web Roots
The most direct defensive move: enumerate what is actually sitting in your web-served directories and flag anything that looks like it contains PHI/PII. This artifact inventories report-like files under common IIS and Linux web roots, prioritizing recently modified content and sensitive naming patterns.
-- Inventory sensitive-looking files in public web roots
-- Scope: IIS (inetpub) and common Linux web roots. Run across all web-tier hosts.
LET roots = SELECT * FROM flatten(query={
SELECT 'C:/inetpub/wwwroot/**' AS Pattern FROM scope()
}, query2={
SELECT '/var/www/html/**' AS Pattern FROM scope()
})
SELECT FullPath, Size, Mtime, Atime,
Btime,
(FullPath =~ '(?i)(eligib|beneficiar|enroll|claim|member|roster|phi|pii|ssn|medicaid|834|835|270|271|report|export)') AS SensitiveName,
(FullPath =~ '(?i)\\.(csv|xlsx?|pdf|txt|zip|sql|bak|dbf)$') AS DataFileType
FROM glob(globs=roots.Pattern)
WHERE NOT IsDir
AND (SensitiveName OR DataFileType)
ORDER BY Mtime DESC
For a deeper pass, extend this with a content-grep second stage (e.g., grep over candidate files for SSN regex \b\d{3}-\d{2}-\d{4}\b on a sampled basis) — but be deliberate about generating alert artifacts containing actual PHI. Log file paths and counts, not the matched content.
Remediation Script — Audit and Lock Down Public Web Roots
Run this on IIS web-tier servers to (1) inventory data-like files under web roots, (2) check for directory browsing being enabled anywhere in the config, and (3) verify anonymous access surfaces. Treat the output as an exposure report, not a log to file and forget.
#Requires -RunAsAdministrator
# DC-Medicaid-style exposure audit: what's public that shouldn't be?
$webRoots = @("C:\inetpub\wwwroot")
# Add application-specific roots:
# $webRoots += "D:\Sites\Portal\wwwroot"
$dataExtensions = @("*.csv","*.xlsx","*.xls","*.pdf","*.txt","*.zip","*.sql","*.bak","*.dbf","*.xml","*.json")
$sensitiveNames = "eligib|beneficiar|enroll|claim|member|roster|phi|pii|ssn|medicaid|834|835|report|export"
Write-Host "=== [1] Data-like files under web roots ===" -ForegroundColor Cyan
$findings = foreach ($root in $webRoots) {
if (Test-Path $root) {
Get-ChildItem -Path $root -Recurse -File -Include $dataExtensions -ErrorAction SilentlyContinue |
Select-Object FullName, Length, LastWriteTime,
@{N='SensitiveName';E={ $_.Name -match $sensitiveNames }}
}
}
$findings | Sort-Object LastWriteTime -Descending | Format-Table -AutoSize
$findings | Export-Csv -Path ".\WebRoot_DataFile_Inventory_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation
Write-Host "=== [2] IIS directory browsing status (should be Disabled everywhere public) ===" -ForegroundColor Cyan
Import-Module WebAdministration -ErrorAction SilentlyContinue
Get-WebConfigurationProperty -Filter "system.webServer/directoryBrowse" -PSPath "IIS:\" -Name enabled -Recurse -ErrorAction SilentlyContinue |
Select-Object PSPath, Value
Write-Host "=== [3] Anonymous authentication enabled on sites ===" -ForegroundColor Cyan
Get-WebConfigurationProperty -Filter "system.webServer/security/authentication/anonymousAuthentication" -PSPath "IIS:\" -Name enabled -Recurse -ErrorAction SilentlyContinue |
Select-Object PSPath, Value
Write-Host "=== [4] Sites/bindings listening on public-facing ports ===" -ForegroundColor Cyan
Get-Website | Select-Object Name, State, @{N='Bindings';E={ ($_.Bindings.Collection | ForEach-Object { $_.bindingInformation }) -join '; ' }}
Write-Host "`nReview the CSV inventory. Any file with SensitiveName=True on an anonymous-enabled, directory-browsable, or unauthenticated site is an exposure — remove it from the web root immediately and open an IR ticket." -ForegroundColor Yellow
Remediation and Hardening
There is no patch for this incident class. The remediation is architectural and procedural:
Immediate (24–72 hours):
- Inventory every public web root across your estate and your business associates'/contractors' infrastructure. Use the VQL hunt and PowerShell audit above. If you cannot enumerate your public attack surface from memory, that is the finding.
- Pull any PHI/PII-bearing files from web-served paths. Move report distribution to an authenticated portal, a managed file-transfer platform (SFTP/MFT with per-user accounts), or pre-signed, short-TTL object URLs — never static public paths.
- Disable directory browsing/autoindex on all public sites (IIS:
directoryBrowse enabled=false; Nginx:autoindex off; Apache:Options -Indexes). - Check search-engine indexing for your report paths (
site:yourdomain.gov filetype:csv, etc.) and submit removal requests for anything indexed. Pull Wayback Machine captures where applicable.
Near-term (this quarter): 5. DLP scan web roots on a schedule. Run content inspection (SSN/PHI pattern matching) against everything in web-served directories weekly. This catches reintroduction, which is how these incidents recur. 6. Gate the publication workflow. Any automated job that writes to a web-served path should require a data-classification check or explicit approval. Robocopy-to-wwwroot as a report distribution mechanism should be eliminated entirely. 7. Alert on the behavior, not just the state. Deploy the Sigma and KQL detections above so that the next staging event or bulk scrape fires an alert instead of a breach notification. 8. Extend the audit to business associates. If a contractor operates any web infrastructure that touches your beneficiary data, their public paths are your breach. Put web-root attestation and scan rights into BAAs and MSA security schedules.
For affected individuals (client-facing guidance if you're advising the notifying organization): standard HIPAA breach-response package — written notification within 60 days, HHS OCR report (500+ threshold), media notice for 500+ in a state, and credit/identity monitoring offers. Given the population (Medicaid beneficiaries), expect heightened state AG and CMS attention; engage counsel early on the regulatory response.
Strategic: 9. Adopt a 'publish is a verb with an approval' culture. Data reaches the public internet only through a deliberate, logged, reversible action — never as a side effect of a file landing in the wrong directory. 10. Map this to your frameworks. NIST CSF PR.DS (data security) and PR.IP (information protection processes), CIS Controls 3 (Data Protection) and 13 (Network Monitoring), HIPAA §164.312(e) transmission security. This incident is a clean case study for your next tabletop: "a report appears on a public URL — walk me from detection to notification."
If you operate in government healthcare or manage Medicaid/MHP data flows and you haven't audited your public web surface in the last 90 days, this incident is your forcing function. The next 400,000-record notification doesn't need to have your letterhead on it.
Related Resources
Security Arsenal Healthcare Cybersecurity AlertMonitor Platform Book a SOC Assessment healthcare Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.