Security·14 min read

The State of Shadow AI Exposure: We Watched 34,836 AI Services Appear on the Public Internet

For 30 days we passively tracked new AI infrastructure appearing on the public internet via Certificate Transparency logs, then checked whether each service enforced authentication. 700 were confirmed serving with no auth (one of them an MCP server, a count withdrawn on 2026-09-29) — and the data reveals one clear thesis: exposure tracks the software's default posture. Ollama was open in 26 of 28 instances; Jupyter in 0 of 2,738. Every CVE, incident, and stat is cited to a primary source, with exactly how to lock each thing down.

E

EchelonGraph Research

Security Research

Most security research about AI is about the models. This is about the plumbing — the inference servers, vector databases, model registries, notebooks and LLM proxies that teams stand up to actually *run* AI. It is the least glamorous layer, and it is the one leaking.

For 30 days we watched new AI infrastructure appear on the public internet, in near real time, by passively reading Certificate Transparency logs — the same public append-only ledgers that every TLS certificate is published to. Then we checked, politely and non-intrusively, whether each service was asking anyone to log in. This post is what we found, why it happens, the specific software and CVEs involved, what the wider research community has independently measured, and exactly how to lock each thing down. Every internal number is from our own data; every external claim links to its primary source.

One finding up front, because it reframes the whole problem: exposure is not random. It tracks the *default* security posture of the software. The tools that ship with authentication on stay locked; the tools that ship open get left open — at wildly different rates. We will show you the exact numbers.

How we measured this (and what "exposed" honestly means)

Our Shadow-AI radar finds its candidates passively. It tails public Certificate Transparency (CT) logs — the RFC 6962 ledgers operated by Google, Cloudflare, Sectigo and others — and matches newly-issued certificate hostnames against a library of AI/ML service fingerprints (patterns for inference servers, vector stores, LLM proxies, notebooks, model registries, MLOps dashboards, and MCP servers). For each candidate it then runs a liveness check — a short series of identified GET requests, starting over HTTPS — and records one of four states (*corrected 2026-09-27: this paragraph called the radar passive and the check a single request; the method correction below says exactly what it sends*):

  • authenticated — the service responded with a 401/403 or served a login page. *Corrected 2026-09-27: in this dataset the label also covered services where authentication was never observed — see the correction below.*
  • active — the service responded 200 with *no* authentication gate. This is a confirmed, wide-open exposure.
  • resolved — the hostname resolves but returned a 4xx/5xx or an unclear response.
  • unreachable — the hostname no longer resolves.
  • Two honesty notes that matter for reading everything below:

  • We report aggregates only, host-redacted. We never name an exposed party, never store credentials, never send a payload, and never "prove" an exposure by using it. Detection for this report used GET requests only (*corrected 2026-09-27 — see the method correction below*). If you take one methodological idea from this post, take that one.
  • "Confirmed open" is a floor, not a ceiling. We only count a service as *active* when it cleanly serves an unauthenticated 200 to a benign request. Plenty of services in the *resolved* bucket are almost certainly also open but returned a redirect, a non-standard port banner, or an ambiguous body — so the true open rate is higher than the confirmed number. We would rather undercount than overclaim.
  • Correction, 2026-09-27: the method. This post said the radar "is passive", that it performs "a single, read-only liveness check over HTTPS", and that "detection is read-only by design"; the first chart, the methodology note and this page's FAQ data said the same. Finding candidates in CT logs is passive. Checking them is not one request. For the data in this post the check sent GET requests only, each identified as ours in its User-Agent: one to the root of the host over HTTPS, following up to four redirects; one to the target of a JavaScript redirect, if the page was one; and, when the root page answered without asking for credentials, one to each of up to four known API paths for that kind of service, stopping at the first conclusive answer — plus a second fetch of the root page when an API did answer openly, to judge whether the service was deliberately public. An entry held as an IP address and port rather than a hostname was tried over HTTP first, then over HTTPS if that failed. Since this post was published we have added a second check, shown on the same radar page: one identified GET per recognised product and, for hosts named like MCP servers, the protocol's own handshake, which a GET cannot perform. That is one POST of a JSON-RPC initialize request to /mcp, which advertises no client capabilities and calls no tool, followed — only if the server opened a session for it — by one DELETE that closes the session. If that handshake is refused with HTTP 401 or 403, at most two further identified GETs to the same host read its OAuth protected-resource metadata (RFC 9728): the same-origin URL the server names in its challenge or, if it names none, /.well-known/oauth-protected-resource/mcp and then /.well-known/oauth-protected-resource, the second only if the first did not return the metadata. No redirect is followed, and a metadata URL on another origin is never requested. Opening a session is a change on someone else's server, however brief, so we no longer describe the radar as read-only. This correction is about how the method was described; it does not change any figure in this post. Updated 2026-09-29. The MCP check described above changed on 2026-09-28, after this note was written. It now first sends one POST of a JSON-RPC server/discover request to /mcp, which asks the server which protocol versions it supports and, under the 2026-07-28 revision, opens no session: a server that answers it with a DiscoverResult, or with an error that revision defines, is sent no further POST, so it is not written to. The initialize request is sent, as a second POST, only when server/discover is refused with an HTTP 4xx status other than 401, 403, 405 or 429 that is not a web page, or with a JSON-RPC error, and the DELETE follows only a POST that opened a session. A refusal of either POST with HTTP 401 or 403 is followed by the metadata GETs described above. On a host named like an MCP server where neither POST found an MCP endpoint and the last was answered 400, 404 or 405, the radar sends one identified GET to /sse, the stream of the deprecated HTTP+SSE transport, reads at most its first event and closes the connection.

    Window: 14 June – 14 July 2026 (rolling 30 days). 34,836 AI/ML service observations, host-redacted and aggregated. This is a continuously-moving picture, not a one-time scan.

    What 30 days of AI on the public internet looks like

    Of 34,836 AI/ML services observed, 18,778 were recorded as *authenticated* — a label that, as we found later, covered both services we saw demand credentials and services where we could not tell (see the correction below). 700 were confirmed serving with no authentication gate at all (one of them an MCP server, a count withdrawn on 2026-09-29; see the correction below the category chart), and another 14,504 were reachable in states we could not confirm as locked.

    Correction, 2026-09-27. The 18,778 "authenticated" figure in this post is not a count of services we saw enforce authentication. When this data was collected, our verifier also filed a service as authenticated when it could not tell — for example a JavaScript-only app shell whose login state is decided in the browser (MinIO's web console is one), a deep probe that matched neither an open nor a locked response, or a redirect chain that never reached a page. Those services answered, but we never saw them ask for credentials. The verifier now records them as inconclusive — counted neither as exposed nor as protected — and the chart below is relabelled "Auth observed or not determined". We have not yet re-measured how the 18,778 split between the two, so we do not print a replacement number. The same applies to the 0% figures for Jupyter and MinIO further down: they count services we confirmed open, and are not evidence that every other instance was locked. This correction concerns the authenticated count only.

    Liveness of 34,836 observed AI services (30 days)Passive CT-log discovery + a GET-only liveness check of several requests per host (corrected 2026-09-27)Auth observedor not determined18,778Reachable, unconfirmed14,504DNS dead854Confirmed NO auth700"Confirmed NO auth" is a floor — many "reachable, unconfirmed" are open too but returned ambiguous responses.2026-09-27: the top bar was labelled "Auth enforced". It included services where authentication was never observed.2026-09-29: one of the 700 is an MCP server, a count withdrawn; see the correction below the category chart.

    Where are the open ones? Overwhelmingly in two categories: LLM proxies/gateways and vector databases. Those are exactly the components teams bolt on quickly to wire an app to a model — and exactly the ones most likely to ship without auth.

    Observations by category — total vs. confirmed-openBar = total observed · red number = confirmed serving with no authAI-WORKFLOW13,52839MODEL-STORE8,0870LLM-PROXY5,738443NOTEBOOK3,3000MCP-SERVER1,7001ANNOTATION1,0780VECTOR-DB714187MODEL-REG2799INFERENCE-SRV20821ML-PIPELINE1930LLM proxies (443 open) and vector DBs (187 open — 26% of those observed) dominate the confirmed exposures.Red number left of each bar = services in that category confirmed serving with no authentication.2026-09-29: the MCP-SERVER count of 1 is withdrawn; the check behind it never spoke MCP (see the correction below).

    Correction, 2026-09-29: the MCP servers. The chart above counts 1 of 1,700 MCP-SERVER observations as "confirmed serving with no auth", in the window 14 June – 14 July 2026. That count is withdrawn. The check behind it fetched the server's web page with GET requests and counted the server open when the page contained words such as "tools" and "jsonrpc". A GET cannot perform the MCP handshake, so that check could not tell whether a server demands authorization; we deleted it on 2026-08-06. The protocol-level check we have run since does not count even a completed handshake as open, because a server can accept it from an anonymous client and still demand credentials when a tool is called, and we never call a tool: an MCP server counts as requiring authorization only when it refuses our server/discover request, or the older initialize handshake, with HTTP 401 or 403 and publishes matching OAuth protected-resource metadata (RFC 9728); anything short of that is reported as not assessed, never as open. The 700 confirmed-open services above include that one; this correction concerns it only. The 1,700 are candidates matched by name or banner; none was sent an MCP request. This window's observations are no longer held. Read from our public API on 2026-09-29 (02:34 UTC), the Shadow AI radar held 1,339 MCP-server observations, largely derived from Shodan data, and counted none of them open. Shodan data is owned by Shodan, which holds its copyright (© Shodan). EchelonGraph claims no ownership of it or copyright in it. The AI-Exposure Radar never reports an MCP server as open, and its public counts were not split by service type until 30 September 2026, when it began publishing its MCP counts, adjudicated under RFC 9728, on the Shadow AI radar page.

    The thesis: exposure tracks the software's default posture

    Here is the finding that should change how you buy and deploy AI tooling. When we break the confirmed-open services down by the specific software running them, the pattern is stark. Software that ships with authentication on is almost never found open. Software that ships open is found open constantly.

    Share serving with NO authentication, by softwareOf instances we observed and could fingerprint · red = ships open · green = ships authenticatedOllama92.9%Weaviatewithdrawn — measured with a probe we no longer trust (see note)LocalAI24.2%ComfyUI6.5%LiteLLM3.0%Jupyter0% of 2,738MinIO0% of 5,161Ollama: 26 of 28 observed instances served with no auth. Jupyter: 0 of 2,738. Same internet, opposite defaults.2026-08-06: the Weaviate figure is withdrawn. It came from probing /v1/meta, which we later confirmed answers 200 to anunauthorised caller — so it could not distinguish open from secured. Re-measuring; no replacement number until it is verified.2026-09-27: each share counts instances CONFIRMED open. A 0% is not proof the rest were locked — some, such as MinIO'sweb console, were JavaScript app shells whose authentication we could not determine (see the correction above).

    Read that bottom line again. Ollama — which is trivial to reconfigure from its localhost default to OLLAMA_HOST=0.0.0.0 with no auth — was serving unauthenticated in 26 of the 28 instances we fingerprinted. Weaviate was reported here at 62%, and that figure is withdrawn as of 2026-08-06 — see the note below the chart. Meanwhile Jupyter (token auth on by default) and MinIO (credentialed by default) were *confirmed* open in zero of the ~7,900 instances we saw between them — a floor on exposure, not a count of locked instances (see the 2026-09-27 correction above).

    This is not because Ollama users are careless and Jupyter users are careful. It is because defaults are destiny. The single highest-leverage security decision in your AI stack is choosing tools that are secure by default — and, for the ones that are not, treating "close the default" as a launch-blocking step, not a follow-up ticket.

    The software, and what it does out of the box

    We validated the default authentication posture of every major self-hosted AI platform against its own official documentation. The split is clean, and it lines up precisely with what we see exposed.

    PlatformAuth by default?How to secure itSource
    Jupyter / JupyterHubYes — token on by defaultKeep the token or set a password; run JupyterHub + an Authenticator + SSL for multi-userjupyter-server docs
    TorchServeYes — token auth since 0.11Keep token auth on; do not pass --disable-token-auth; bind to localhostpytorch.org/serve
    OllamaNo — localhost default, but commonly set to 0.0.0.0 with no authKeep it on 127.0.0.1; expose only via a reverse proxy that adds TLS + authollama docs
    RayNo — token auth is opt-in (since 2.52.0)Network-isolate the cluster; optionally enable token auth as defense-in-depthray-security
    vLLMNo — --api-key is opt-in and only guards /v1 pathsSet an API key, put it behind an endpoint-allowlisting proxy, firewall itvllm security
    NVIDIA TritonNo — designed to sit behind a gatewayFront with a secure proxy; mTLS for gRPC; restricted-API shared secretstriton deploy
    MLflowNo — open when bound to 0.0.0.0Launch with --app-name basic-auth, or restrict via proxy/networkmlflow security
    KServeNo — endpoints reachable by anyone who can route to themIstio strict-mTLS + AuthorizationPolicy + JWT/OIDC RequestAuthenticationkserve servicemesh
    GradioNo — share=True mints a public URLPass auth= to launch(); avoid share=True for sensitive appsgradio sharing
    KubeflowIt depends — standard manifests bundle Dex/oauth2-proxy at the gateway, but components have no auth of their ownAuthenticate at the Istio gateway; isolate with Profiles + K8s RBACkubeflow

    And in the maintainers' own words — these are not our warnings, they are theirs:

  • Ray: "anybody who can access the associated ports can execute arbitrary code on your Ray Cluster." (docs)
  • vLLM: "Never expose these internal ports to the public internet or untrusted networks." (docs)
  • NVIDIA Triton: "Triton Inference Server is not exposed directly to an untrusted network." (docs)
  • Jupyter: "access to the Jupyter Server means access to running arbitrary code." (docs)
  • Why an open endpoint is not a small thing: the CVEs

    An unauthenticated AI service is not just a data-leak risk; for much of this software it is remote code execution waiting for a visitor. We validated the following CVEs on the NVD, and — importantly — we downloaded the full CISA Known Exploited Vulnerabilities catalog and checked every one. None of these AI-infrastructure CVEs are on CISA KEV yet, even the one under active mass exploitation. The catalogs have not caught up with this attack surface, which is exactly why exposure detection matters more here than "patch what's in KEV."

    CVEProductCVSSClassExploited?
    CVE-2023-48022 "ShadowRay"Ray9.8Missing auth → RCEYes, in the wild
    CVE-2024-37032 "Probllama"Ollama < 0.1.348.8Path traversal → RCEPoC public
    CVE-2025-23266 "NVIDIAScape"NVIDIA Container Toolkit ≤ 1.17.79.0Container escape → host rootPoC (Pwn2Own)
    CVE-2024-0132NVIDIA Container Toolkit ≤ 1.16.18.3TOCTOU container escapePoC
    CVE-2023-43654 "ShellTorch"TorchServe 0.3–0.8.19.8SSRF → RCEPoC public
    CVE-2025-32444vLLM (with Mooncake)9.8Deserialization → RCEPoC public
    CVE-2025-23319NVIDIA Triton9.8Out-of-bounds writePoC (Wiz chain)
    CVE-2024-37052MLflow8.8Model deserialization → RCEPoC public
    CVE-2024-34359 "Llama Drama"llama-cpp-python9.6Jinja2 SSTI → RCEPoC public
    CVE-2023-51449Gradio < 4.11.07.5Path traversal (file read)Scanning observed
    CVE-2021-32797JupyterLab9.6XSS → RCEExposed Jupyter abused ITW

    The disputed one that matters most. ShadowRay (CVE-2023-48022) is marked "disputed" on NVD because Anyscale, Ray's maintainer, argues Ray "is not intended for use outside of a strictly controlled network environment." That is a fair design stance — but it also means there is no patch coming and Ray ships without auth by default. When an unpatched-by-design RCE meets a cluster on the public internet, the design assumption has already failed. Detection of the exposure is the only real control.

    This is not theoretical — the incidents

    Independent researchers have been documenting exactly this class of exposure, at scale, for two years. We link every primary source so you can read the originals:

  • ShadowRay (Oligo Security, Mar 2024). Thousands of publicly exposed Ray servers, many already compromised; active exploitation running for roughly seven months before disclosure. Attackers stole OpenAI/Hugging Face/Stripe tokens and AWS/GCP/Azure keys, and hijacked GPU compute Oligo valued at "almost a billion USD." (primary)
  • ShadowRay 2.0 (Oligo Security, Nov 2025). The same unpatched flaw, now driving a self-propagating GPU-cryptomining botnet across "over 200,000" exposed Ray servers — roughly a tenfold increase. One hijacked cluster's compute alone was worth over 3 million USD a year. (primary)
  • NVIDIAScape (Wiz, Jul 2025). A three-line Dockerfile escapes the NVIDIA Container Toolkit to host root. Wiz reported that "37% of cloud environments have resources vulnerable" — a tenant-isolation break on shared GPU hosts. (primary)
  • DeepSeek's open database (Wiz, Jan 2025). A publicly reachable, fully unauthenticated ClickHouse database belonging to one of the most-discussed AI labs in the world, leaking "over a million lines of log streams" including plaintext chat history and API keys — with an open path that allowed arbitrary SQL. (primary)
  • Exposed Ollama at scale (Cisco Talos, Sep 2025). A Shodan census found 1,139 exposed Ollama servers, 214 of them actively hosting live models — all over unauthenticated interfaces. (primary)
  • "Silent Brothers" (SentinelLABS + Censys, Jan 2026). A far larger census: 175,108 unique exposed Ollama hosts across 130 countries, with nearly half configured for tool-calling or code execution — an ungoverned public AI-compute layer operating outside any platform guardrail. (primary)
  • Two years of exposed-AI-infrastructure researchOct 2023ShellTorchMar 2024ShadowRayMay 2024ProbllamaJan 2025DeepSeek leakJul 2025NVIDIAScape 37%Nov 2025ShadowRay 2.0Jan 2026175k OllamaRed = active in-the-wild exploitation or mass exposure. The trend line is up and to the right.

    Why it keeps happening: shadow AI, by the numbers

    The reason this surface keeps growing is organizational, not technical. AI infrastructure gets stood up faster than security teams can inventory it — often by people who are not security teams at all.

  • IBM's Cost of a Data Breach 2025 put a number on it: breaches involving shadow AI carried a $670,000 cost premium, and 20% of breached organizations attributed their breach to shadow AI. Of organizations that suffered an AI-related breach, 97% lacked proper AI access controls, and 63% have no AI governance policy at all. (IBM newsroom · report hub)
  • Menlo Security (2025) found 68% of employees use free-tier generative-AI tools via personal accounts, and 57% admit to entering sensitive data into them. (primary)
  • "97% lacked proper AI access controls" and "an unauthenticated Ollama in 26 of 28 instances" are the same sentence written from two directions.

    Where this maps in the frameworks you already use

    If you need to file this under a control owner, the mappings are unambiguous — and every identifier below is verified against its primary source:

  • OWASP Top 10 for LLM Applications 2025 — most directly LLM02:2025 Sensitive Information Disclosure and LLM10:2025 Unbounded Consumption; an unauthenticated endpoint also opens the door to LLM01 Prompt Injection and LLM06 Excessive Agency.
  • MITRE ATLAS — AML.T0049 Exploit Public-Facing Application, AML.T0040 AI Model Inference API Access, AML.T0055 Unsecured Credentials, AML.T0012 Valid Accounts.
  • OWASP Machine Learning Security Top 10 — ML05 Model Theft, ML03 Model Inversion, ML04 Membership Inference: free query access to a model is itself the vulnerability.
  • NIST AI RMF 1.0 — MEASURE 2.7 ("AI system security and resilience are evaluated and documented"), plus MANAGE 4.1 and GOVERN 1.1/6.1.
  • CISA/NSA and five partner agencies, "Deploying AI Systems Securely" (2024) — explicitly: "secure exposed APIs" with authentication and authorization, use phishing-resistant MFA, and harden the deployment environment with firewall allow-lists and Zero Trust. (joint guidance)
  • What to actually do about it

    None of this requires exotic tooling. In priority order:

  • Inventory the AI plumbing, not just the models. You cannot secure an inference server, vector DB, or MLOps dashboard you do not know exists. This is where shadow AI hides.
  • Prefer secure-by-default software — and where you cannot, make "close the default" a launch blocker. Bind Ollama to localhost. Set vLLM's API key *and* firewall it (the maintainers say do not rely on the key alone). Keep Jupyter and TorchServe tokens on.
  • Never put an AI service directly on the internet. Put a gateway in front that enforces authentication, TLS, and rate limits. This single control neutralizes the majority of what we found.
  • Patch the RCE-class CVEs above on anything reachable — and don't wait for them to appear in CISA KEV, because as of this writing they are not there.
  • Continuously check your own external surface the way an attacker does — from the outside, watching for the new endpoint someone spun up this afternoon. A quarterly scan misses a service that lived for three weeks.
  • Our commitment, and an invitation. Everything in this report was produced without touching a single exposed service beyond a benign liveness check, and without naming anyone. If you run AI infrastructure and want to see your own external exposure the way we see the internet's, EchelonGraph's radars and free external attack-surface scan do exactly this — watch the Shadow-AI radar live or run a free scan. That is the only sentence in this post that is about us.

    Methodology, limits, and references

    Method. Passive Certificate Transparency log discovery (RFC 6962) matched against AI/ML service fingerprints, followed by a GET-only liveness check of several identified requests per host, starting over HTTPS, classified as authenticated / active / resolved / unreachable (*corrected 2026-09-27 — see the method correction above*). Aggregate and host-redacted; no credentials stored, no payloads sent, no exposure "proven" by use. Window 14 June – 14 July 2026, n = 34,836.

    Limits. CT-log discovery sees services that get a public TLS certificate for a hostname — it under-counts bare-IP and internal-CA services. Product fingerprinting is best-effort; roughly 62% of observations were not confidently fingerprinted and are excluded from per-software rates. "Confirmed open" counts only clean unauthenticated 200s, so it undercounts true exposure. Country attribution is by hosting ASN, not operator nationality. These are honest floors, and we would rather publish a defensible floor than an impressive ceiling.

    Primary sources are linked inline throughout; the key ones: NVD (each CVE), CISA/NSA joint guidance, IBM Cost of a Data Breach 2025, Menlo Security 2025, Oligo Security (ShadowRay 1.0/2.0, ShellTorch), Wiz (NVIDIAScape, Probllama, DeepSeek), Cisco Talos and SentinelLABS+Censys (Ollama censuses), and each platform's official security documentation.

    This is the first in a recurring series measuring what is actually exposed on the public internet. If there is a slice of this data you want us to go deeper on, tell us at [email protected].