Two disclosures surfaced in late September 2026 that look unrelated on the surface. One is a bug bounty find in an internal Microsoft analytics service. The other is academic research into file-notification APIs that ship in every major operating system.
They share a pattern worth pausing on. In both cases, the control everyone could see wasn’t the control actually deciding who got access.
A VPN page in front, an open API behind it#
Titan is an internal Microsoft analytics service. Its web interface sat behind a Microsoft VPN requirement, which is the kind of thing that makes a service feel internal. Its API, however, was reachable through an Azure Cloud Services host and had public documentation.
A 16-year-old researcher found that API on August 25, 2026 while participating in Microsoft’s bug bounty program, using an AI-assisted research tool he built himself. The /v2/Query endpoint accepted SQL and required authentication. The problem was how it authenticated.
Titan checked the tenant, audience, and application claims inside incoming JWTs. It never verified the signature. That meant a token with an alg:none header, carrying no signature at all, passed every check the service performed.
From there, the researcher changed the upn claim to admin. Titan treated that value as a local username, mapped it to user ID 1, and granted the service’s Admin role. Admin-level SQL followed, with no valid Microsoft credentials involved.
Using platform metadata, the researcher tested 56 routing values from an archived configuration, found 30 still active, and traced them to 17 ClickHouse analytics databases containing 9,863 unique table names. He also pulled two one-row samples from a Bing analytics partition to confirm that data source was reachable.
The timeline was tight. He reported the issue to MSRC on September 5, Microsoft locked down the endpoint on September 9, and a $5,000 bounty followed on September 17. The researcher also disclosed that Microsoft had editorial input on his published writeup, which is worth keeping in mind when reading the impact description.
What actually went wrong#
Validating claims without verifying the signature isn’t authentication. It’s parsing. Every downstream check on tenant, audience, and app ID becomes meaningless once the caller can write those values themselves.
The second failure is quieter and, in my view, more common. Titan took a free-text identity claim and mapped it directly to a local account. Even with signature validation in place, mapping upn to a local username is fragile. Stable identifiers like oid or sub, scoped to a trusted issuer, exist for exactly this reason.
This is significant because the VPN-gated UI likely shaped how people thought about the service’s exposure. Teams tend to threat-model the front door they built. The API hostname was a separate door, and it was open.
What to check in your own services#
- Reject
alg:noneexplicitly and pin the algorithms you accept. Don’t let the token header choose. - Verify the signature against the issuer’s published keys before reading any claim.
- Map identities using
issplussuboroid, never a display value likeupnor email. - Inventory API hostnames separately from UI hostnames. A VPN on the web front end says nothing about the API.
- Add a negative test to CI that sends an unsigned and a tampered token and expects a 401. For Python services using PyJWT, the safe pattern is short:
import jwt
jwks = jwt.PyJWKClient("https://login.microsoftonline.com/<tenant>/discovery/v2.0/keys")
signing_key = jwks.get_signing_key_from_jwt(token).key
claims = jwt.decode(
token,
signing_key,
algorithms=["RS256"], # pinned, never taken from the header
audience="<app-id-uri>",
issuer="https://login.microsoftonline.com/<tenant>/v2.0",
)
user_key = (claims["iss"], claims["oid"]) # stable identity, not upnFile watchers that see across user boundaries#
The second disclosure comes from a university research team whose paper was accepted to ACM CCS 2026 (November 15 to 19, 2026, in The Hague). It targets the file-notification APIs applications use to react when files change: inotify on Linux, FileObserver on Android, ReadDirectoryChangesW on Windows, and FSEvents on macOS.
These APIs don’t leak file contents. They leak events, and sometimes file names. The research shows that’s enough to reconstruct a surprising amount of behavior from another account.
Linux. Placing a watch on a file you can’t read fails. Placing a watch on its readable parent directory succeeds and reports every event on that file anyway. Pointed at /dev/input, that turns into keystroke timing: not which key, but exactly when each key went down and up. The same technique on /dev/pts works across SSH users on a shared server. In one test, a watching process caught all 402 keystrokes a remote user typed. Hidden password prompts like sudo with feedback disabled didn’t generate events.
Inter-keystroke timing has more than two decades of research behind it, so “only timing” is less comforting than it sounds.
Windows. Watching a directory you can’t read fails, but watching the root of C:\ succeeds and reports full paths for file activity across the whole system, including inside other users’ profiles. Because Firefox creates per-site storage directories named after the site, the team identified visits across the top 1,000 websites with a 97.8% F1 score. Microsoft described the behavior as an undocumented feature.
EnforceDirectoryChangeNotificationPermissionCheck, that mitigates the Windows path disclosure. It ships disabled by default, so the attacks in the paper work out of the box. On multi-user Windows systems such as RDS and VDI hosts, test and evaluate enabling it.Android. FileObserver bypasses the FUSE layer’s per-app storage isolation. An unprivileged app with no permissions could watch another app’s private media folder and see when WhatsApp media arrived, was sent, or was deleted, file names included.
macOS. The least exposed of the four. Leakage was limited to globally readable files, though app launches, printing, and Bluetooth changes were still observable.
Watching was cheap, costing at most 0.21% of CPU time in the team’s measurements. The researchers aren’t aware of in-the-wild exploitation.
Where this matters in practice#
The threat model is a local, cross-user attacker: a compromised service account, a malicious local user, or a package pulled in through a supply chain compromise. That last one is the part I’d weigh most heavily. A poisoned dependency running under a low-privilege service account on a shared build server doesn’t need root to start learning what other users on that box are doing.
The environments that deserve a second look are the ones where multiple identities share a host: jump servers and bastions, multi-user build agents, RDS and VDI farms, and shared analyst workstations.
What’s patched and what isn’t#
- Linux: CVE-2025-68788 partially mitigates the issue by suppressing access and modify events on character devices. Fixed kernels are 5.10.248, 5.15.198, 6.1.160, 6.6.120, 6.12.65, and 6.18.3. You can check a host by running
inotifywait -m -e access,modify /dev/inputand pressing keys. No output means the mitigation is in place. - Windows: Mitigation exists through the registry policy above, off by default.
- Android and macOS: No fixes at the time of disclosure.
What ties them together#
In both cases, the protection people pointed to was real. The VPN did gate the Titan UI. File permissions did block direct reads. Neither one sat on the path an attacker would actually use.
That’s a useful review question for any control you rely on: is this where enforcement happens, or just where it’s most visible?
Sources#
- How I could’ve accessed 17 trillion Microsoft records (researcher writeup)
- Hackread: Teen hacker finds auth flaw in Microsoft system
- File Notification Attacks (research site and paper)
- Microsoft: Access check enhancements to prevent unauthorized disclosure of file paths
- Help Net Security: File notification attacks
