<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Labsec</title><subtitle>Security intel for home labs and tiny networks</subtitle><link href="https://labsec.an201.com/feed.xml" rel="self"/><link href="https://labsec.an201.com/"/><id>https://labsec.an201.com/</id><updated>2026-09-30T03:08:27Z</updated><author><name>Astronexus LLC</name></author><entry><title>ACT TONIGHT: Docker Engine: open API on 2375 and docker.sock in web-facing containers = root on host</title><link href="https://labsec.an201.com/c/2026-09-28-docker-api-2375-and-docker-sock"/><id>https://labsec.an201.com/c/2026-09-28-docker-api-2375-and-docker-sock</id><published>2026-09-28T00:00:00Z</published><updated>2026-09-28T00:00:00Z</updated><category term="docker"/><category term="compose"/><category term="portainer"/><category term="traefik"/><category term="exposed-api"/><category term="cryptominer"/><category term="botnet"/><summary>ACT TONIGHT. Close 2375.</summary><content type="html">&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; The Docker API is root on the host. Anyone who can talk to it (TCP 2375, or
&lt;code&gt;/var/run/docker.sock&lt;/code&gt;) can start a privileged container with &lt;code&gt;/&lt;/code&gt; mounted and own the box.
Bots do exactly that, nonstop. Carbonato (ThreatDown, disclosed Sept 2026, activity Oct 2024 to
Aug 2026) hits unauthenticated 2375, launches a privileged container, drops SSH keys and
cron/systemd persistence, steals AI API keys and SSH creds, and scans for the next daemon every
five minutes. Earlier strains (Trend Micro, June 2025; Akamai, Sept 2025) mount &lt;code&gt;/:/hostroot&lt;/code&gt;,
drop XMRig, add keys to &lt;code&gt;/root/.ssh/authorized_keys&lt;/code&gt;, and firewall 2375 so other gangs can&#x27;t get
in after them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who it hits:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;dockerd&lt;/code&gt; started with &lt;code&gt;-H tcp://0.0.0.0:2375&lt;/code&gt; (a daemon.json &lt;code&gt;hosts&lt;/code&gt; entry, a systemd override,
  or a leftover &quot;enable remote API&quot; tutorial). Port-forwarded, on a VPS, or anywhere the LAN is
  reachable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;2376&lt;/code&gt; without &lt;code&gt;--tlsverify&lt;/code&gt;. Using the TLS port number doesn&#x27;t give you TLS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Containers that are reachable from the web &lt;strong&gt;and&lt;/strong&gt; mount &lt;code&gt;docker.sock&lt;/code&gt;: Portainer, Traefik,
  Watchtower, Homepage/dashboards, &quot;container manager&quot; UIs. One bug or weak login in that app
  gives the attacker root on the host.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Anyone who trusts &lt;code&gt;ufw&lt;/code&gt; to protect published container ports. It doesn&#x27;t. Docker&#x27;s own
  iptables rules divert published-port traffic before ufw sees it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Check if you&#x27;re affected:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 1. Is the daemon listening on TCP? Any output here = look closer.
sudo ss -ltnp | grep -E &#x27;:2375|:2376&#x27;
systemctl cat docker | grep -n &#x27;tcp://&#x27;
grep -n &#x27;tcp://&#x27; /etc/docker/daemon.json 2&amp;gt;/dev/null

# 2. From OUTSIDE your network (phone hotspot, a VPS). JSON back = you&#x27;re open to the world.
curl -s --max-time 5 http://&amp;lt;public-ip&amp;gt;:2375/version

# 3. Which running containers mount the socket?
docker ps -q | xargs -r docker inspect --format &#x27;{{.Name}} {{range .Mounts}}{{.Source}} {{end}}&#x27; | grep docker.sock
grep -rn --include=&#x27;*compose*.y*ml&#x27; &#x27;docker.sock&#x27; ~ /opt /srv 2&amp;gt;/dev/null

# 4. Which published ports listen on every interface (and skip ufw)?
docker ps --format &#x27;{{.Names}}\t{{.Ports}}&#x27; | grep -E &#x27;0\.0\.0\.0|\[::\]|:::&#x27;

# 5. Signs you&#x27;re already hit (alpine matches plenty of legit images; look for ones you didn&#x27;t start)
docker ps -a --format &#x27;{{.ID}}\t{{.Image}}\t{{.CreatedAt}}\t{{.Command}}&#x27; | grep -Ei &#x27;alpine|base64|hostroot&#x27;
sudo cat /root/.ssh/authorized_keys; sudo crontab -l; systemctl list-timers --all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Do this:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tonight: close 2375.&lt;/strong&gt; Remove &lt;code&gt;tcp://...&lt;/code&gt; from &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; (&lt;code&gt;hosts&lt;/code&gt;) and from
   any &lt;code&gt;ExecStart&lt;/code&gt; in &lt;code&gt;/etc/systemd/system/docker.service.d/*.conf&lt;/code&gt;, then
   &lt;code&gt;sudo systemctl daemon-reload &amp;amp;&amp;amp; sudo systemctl restart docker&lt;/code&gt;. Delete the router port-forward
   too.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Need remote control? Use SSH, not TCP.&lt;/strong&gt;
   &lt;code&gt;docker context create lab --docker host=ssh://you@labbox&lt;/code&gt; then &lt;code&gt;docker --context lab ps&lt;/code&gt;.
   If you really need TCP, use TLS on 2376 with &lt;code&gt;--tlsverify&lt;/code&gt; and client certs. Never plain 2375.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Take the socket out of web-facing apps.&lt;/strong&gt; If an app only needs to read container state
   (Traefik&#x27;s Docker provider, Homepage, most dashboards), put &lt;code&gt;tecnativa/docker-socket-proxy&lt;/code&gt;
   in front with &lt;code&gt;CONTAINERS=1&lt;/code&gt; and &lt;code&gt;POST=0&lt;/code&gt; (the default). Put it on an internal network, never
   published. Mounting the socket &lt;code&gt;:ro&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; make the API read-only: it still accepts
   every call. Apps that must write (Portainer, Watchtower) stay off the public internet. Put
   them behind a VPN (Tailscale/WireGuard) or Cloudflare Access.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stop trusting ufw for container ports.&lt;/strong&gt; Anything behind a reverse proxy or tunnel should be
   published as &lt;code&gt;127.0.0.1:8080:80&lt;/code&gt; (compose: &lt;code&gt;&quot;127.0.0.1:8080:80&quot;&lt;/code&gt;), or not published at all
   and reached over a shared Docker network. On Docker older than 28.0.0, machines on the same
   L2 segment can still reach localhost-published ports, so upgrade.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify.&lt;/strong&gt; Re-run checks 1–4. &lt;code&gt;curl http://&amp;lt;public-ip&amp;gt;:2375/version&lt;/code&gt; from outside must
   time out.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;If you were already hit:&lt;/strong&gt; Treat the host as owned. They had root. Rebuild it. Before you do,
rotate everything that lived on it (SSH keys, cloud creds, AI provider API keys, tokens in &lt;code&gt;.env&lt;/code&gt;
files) because Carbonato goes after exactly those.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why this level:&lt;/strong&gt; An open 2375 or an internet-facing app holding docker.sock gives root with
no auth, and live worms are scanning 2375 right now (the mass-scanning override applies). That&#x27;s
5/5 on the rubric. act-tonight.&lt;/p&gt;</content></entry><entry><title>ACT TONIGHT: Ollama / vLLM / LiteLLM: inference API open to the internet with no auth</title><link href="https://labsec.an201.com/c/2026-09-28-exposed-llm-inference-apis"/><id>https://labsec.an201.com/c/2026-09-28-exposed-llm-inference-apis</id><published>2026-09-28T00:00:00Z</published><updated>2026-09-28T00:00:00Z</updated><category term="ollama"/><category term="vllm"/><category term="litellm"/><category term="exposed-api"/><category term="docker"/><summary>ACT TONIGHT. Stop Ollama listening on every interface.</summary><content type="html">&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Ollama, vLLM and LiteLLM don&#x27;t have auth turned on by default. If the port is reachable,
anyone can run your models on your GPU, pull or push models, and read what the API exposes.
People are actively scanning for these. SentinelLABS and Censys counted about 175,000 exposed
Ollama hosts (Oct 2025 to Jan 2026). Nearly half of them advertised tool calling. Pillar
Security found a marketplace (&quot;Operation Bizarre Bazaar&quot;) that resells access to hijacked
endpoints. Between March and June 2026, Sysdig and Zenity caught attackers using exposed Ollama
and LiteLLM backends as the brain of automated attack tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who it hits:&lt;/strong&gt; You&#x27;re affected if any of these is true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OLLAMA_HOST=0.0.0.0&lt;/code&gt; with 11434 port-forwarded, tunneled or on a VPS.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;docker run -p 11434:11434 ollama/ollama&lt;/code&gt; on a box with a public IP. Docker&#x27;s published ports
  get around ufw.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;vllm serve&lt;/code&gt; on its defaults, which bind all interfaces. &lt;code&gt;--api-key&lt;/code&gt; only guards &lt;code&gt;/v1&lt;/code&gt;-style
  paths. &lt;code&gt;/invocations&lt;/code&gt;, &lt;code&gt;/score&lt;/code&gt;, &lt;code&gt;/pooling&lt;/code&gt; and a few others stay open.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A LiteLLM proxy on &lt;code&gt;0.0.0.0:4000&lt;/code&gt; with no &lt;code&gt;master_key&lt;/code&gt;. Anyone can then spend the upstream
  OpenAI or Anthropic keys it holds.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ollama versions below 0.17.1 add CVE-2026-7482 (&quot;Bleeding Llama&quot;, CVSS 9.1). It takes three
unauthenticated API calls (&lt;code&gt;/api/create&lt;/code&gt; then &lt;code&gt;/api/push&lt;/code&gt;) to leak heap memory: prompts, env vars
and API keys. Versions below 0.1.34 also have Probllama (CVE-2024-37032), an RCE through
&lt;code&gt;/api/pull&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check if you&#x27;re affected:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# On the box: anything on 0.0.0.0 / [::] / * here is listening on every interface
ss -ltnp | grep -E &#x27;:(11434|8000|4000)\b&#x27;
# From OUTSIDE your network (phone hotspot, VPS). Any JSON back means you&#x27;re exposed:
curl -s -m 5 http://YOUR_PUBLIC_IP:11434/api/tags
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Do this:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tonight:&lt;/strong&gt; stop Ollama listening on every interface. Run &lt;code&gt;sudo systemctl edit ollama&lt;/code&gt;,
   set &lt;code&gt;Environment=&quot;OLLAMA_HOST=127.0.0.1:11434&quot;&lt;/code&gt;, then run
   &lt;code&gt;sudo systemctl daemon-reload &amp;amp;&amp;amp; sudo systemctl restart ollama&lt;/code&gt;. For Docker, publish it as
   &lt;code&gt;-p 127.0.0.1:11434:11434&lt;/code&gt;. Start vLLM with &lt;code&gt;--host 127.0.0.1&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remove the router port-forward. If other LAN machines need the API, only let the LAN in,
   e.g. &lt;code&gt;sudo ufw allow from 192.168.1.0/24 to any port 11434&lt;/code&gt;, and deny the rest. Or reach it
   over Tailscale or WireGuard instead.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you need it reachable from outside, put an auth proxy in front: Caddy or nginx with
   basic auth or forward-auth, or Cloudflare Tunnel with Cloudflare Access. For vLLM, only allow
   the paths you use through the proxy. Don&#x27;t rely on &lt;code&gt;--api-key&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LiteLLM: set &lt;code&gt;LITELLM_MASTER_KEY=sk-&amp;lt;long random&amp;gt;&lt;/code&gt; (it must start with &lt;code&gt;sk-&lt;/code&gt;) and give
   clients virtual keys. Rotate every upstream provider key the proxy held if it was ever open.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Upgrade Ollama to 0.17.1 or newer: &lt;code&gt;ollama -v&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Confirm the fix: from outside, the &lt;code&gt;curl&lt;/code&gt; above should time out. The &lt;code&gt;ss&lt;/code&gt; line should show
   only &lt;code&gt;127.0.0.1&lt;/code&gt; or your LAN or tailnet IP.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;If you were already hit:&lt;/strong&gt; Look in the Ollama logs for unfamiliar &lt;code&gt;/api/create&lt;/code&gt;, &lt;code&gt;/api/push&lt;/code&gt;
or &lt;code&gt;/api/pull&lt;/code&gt; calls, and for models you didn&#x27;t pull (&lt;code&gt;ollama list&lt;/code&gt;). Rotate any keys that
were in the process env or passed through LiteLLM. Check GPU usage history for load you
didn&#x27;t cause.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why this level:&lt;/strong&gt; It answers yes on all five rubric questions: commonly port-forwarded or
tunneled, exploited in live campaigns, a core lab AI tool, big blast radius (keys, prompts,
free compute, tool-calling agents), and no auth by default. Mass scanning of 11434 with live
LLMjacking campaigns also triggers the act-tonight override.&lt;/p&gt;</content></entry><entry><title>ACT TONIGHT: Grafana and Prometheus: admin/admin, anonymous access, and unauthenticated metrics/pprof endpoints</title><link href="https://labsec.an201.com/c/2026-09-28-grafana-prometheus-default-exposure"/><id>https://labsec.an201.com/c/2026-09-28-grafana-prometheus-default-exposure</id><published>2026-09-28T00:00:00Z</published><updated>2026-09-28T00:00:00Z</updated><category term="grafana"/><category term="prometheus"/><category term="node-exporter"/><category term="docker"/><category term="default-creds"/><category term="exposed-api"/><summary>ACT TONIGHT. Delete any port-forward for 3000, 9090, 9093 or 9100 on the router.</summary><content type="html">&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Out of the box, Grafana listens on all interfaces with &lt;code&gt;admin&lt;/code&gt;/&lt;code&gt;admin&lt;/code&gt;. Prometheus
(&lt;code&gt;:9090&lt;/code&gt;) and node_exporter (&lt;code&gt;:9100&lt;/code&gt;) have no auth at all, and both serve &lt;code&gt;/debug/pprof&lt;/code&gt;.
Aqua Nautilus found about 40,000 Prometheus servers and 296,000 exporters open to the internet in
Dec 2024. Their metrics and labels were leaking credentials, tokens, API keys and internal
hostnames. Anyone can hit pprof to pull out runtime details or tie up the host&#x27;s resources.
On top of the bad defaults, Grafana keeps shipping CVEs:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CVE&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Needs&lt;/th&gt;
&lt;th&gt;Fixed in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2021-43798&lt;/td&gt;
&lt;td&gt;Path traversal, reads files off the host&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Nothing&lt;/strong&gt; (unauth)&lt;/td&gt;
&lt;td&gt;8.3.1 / 8.2.7 / 8.1.8 / 8.0.7. In CISA KEV since 2025-10-09. GreyNoise logged 110 IPs trying it on 28 Sep 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2025-4123 &quot;Grafana Ghost&quot;&lt;/td&gt;
&lt;td&gt;Open redirect + path traversal leading to XSS/account takeover. Full-read SSRF if Image Renderer is installed&lt;/td&gt;
&lt;td&gt;Victim clicks a link. Works with anonymous access on&lt;/td&gt;
&lt;td&gt;10.4.18, 11.2.9, 11.3.6, 11.4.4, 11.5.4, 11.6.1 (all &lt;code&gt;+security-01&lt;/code&gt;), 12.0.0+security-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2024-9264&lt;/td&gt;
&lt;td&gt;SQL Expressions command injection/LFI (CVSS 9.9)&lt;/td&gt;
&lt;td&gt;Viewer account, plus &lt;code&gt;duckdb&lt;/code&gt; binary in Grafana&#x27;s PATH&lt;/td&gt;
&lt;td&gt;11.0.5/11.1.6/11.2.1 &lt;code&gt;+security-01&lt;/code&gt; (and later)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-27876&lt;/td&gt;
&lt;td&gt;sqlExpressions file write leading to RCE/SSH on the host (CVSS 9.1)&lt;/td&gt;
&lt;td&gt;Viewer account, plus the &lt;code&gt;sqlExpressions&lt;/code&gt; feature toggle&lt;/td&gt;
&lt;td&gt;12.4.2, 12.3.6, 12.2.8, 12.1.10, 11.6.14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE-2026-27880&lt;/td&gt;
&lt;td&gt;Crashes Grafana by exhausting memory&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Nothing&lt;/strong&gt; (unauth)&lt;/td&gt;
&lt;td&gt;Same as above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The Viewer-level bugs are why anonymous access matters. With anonymous on, every visitor gets
Viewer by default. Grafana published more fixes in Aug and Sep 2026, so check the
advisories page for your version.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who it hits:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Grafana on &lt;code&gt;:3000&lt;/code&gt; port-forwarded, or put on the internet through a Cloudflare Tunnel or
  reverse proxy, that is still on &lt;code&gt;admin&lt;/code&gt;/&lt;code&gt;admin&lt;/code&gt;, has &lt;code&gt;[auth.anonymous] enabled = true&lt;/code&gt;, or
  runs an unpatched version.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prometheus &lt;code&gt;:9090&lt;/code&gt; or any exporter (&lt;code&gt;:9100&lt;/code&gt;, cAdvisor, etc.) reachable from outside your LAN.
  Docker&#x27;s &lt;code&gt;-p 9090:9090&lt;/code&gt; publishes on every interface and skips ufw, so a
  host firewall doesn&#x27;t save you.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Check if you&#x27;re affected:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# What is listening, and on which address (0.0.0.0 / [::] = every interface)
ss -ltnp | grep -E &#x27;:(3000|9090|9093|9100)\b&#x27;

# Grafana version (compare against the table / advisories page)
curl -s http://HOST:3000/api/health

# Default creds still work? 200 + JSON = yes, 401 = no
curl -s -o /dev/null -w &#x27;%{http_code}\n&#x27; -u admin:admin http://HOST:3000/api/user

# Anonymous access on? 200 with a list = yes, 401 = no
curl -s -o /dev/null -w &#x27;%{http_code}\n&#x27; http://HOST:3000/api/search

# Run these from OUTSIDE (phone hotspot / a VPS) against your public IP or hostname.
# Any 200 means strangers see it too.
curl -s -o /dev/null -w &#x27;%{http_code}\n&#x27; http://PUBLIC:9090/api/v1/targets
curl -s -o /dev/null -w &#x27;%{http_code}\n&#x27; http://PUBLIC:9090/debug/pprof/
curl -s -o /dev/null -w &#x27;%{http_code}\n&#x27; http://PUBLIC:9100/metrics
curl -s -o /dev/null -w &#x27;%{http_code}\n&#x27; http://PUBLIC:9100/debug/pprof/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Do this:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tonight:&lt;/strong&gt; delete any port-forward for 3000, 9090, 9093 or 9100 on the router. Never
   forward Prometheus or exporter ports. If you need to reach them remotely, use a VPN
   (Tailscale/WireGuard).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Change the Grafana admin password:&lt;/strong&gt;
   &lt;code&gt;docker exec -it grafana grafana cli admin reset-admin-password &#x27;LONG-RANDOM&#x27;&lt;/code&gt;
   (or change it in the UI under Profile, then Change password). &lt;code&gt;GF_SECURITY_ADMIN_PASSWORD&lt;/code&gt;
   only takes effect on first start and does not change an existing database.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lock down &lt;code&gt;grafana.ini&lt;/code&gt;&lt;/strong&gt; (or the matching &lt;code&gt;GF_*&lt;/code&gt; env vars) and restart:
   &lt;code&gt;ini
   [auth.anonymous]
   enabled = false
   [users]
   allow_sign_up = false
   [server]
   http_addr = 127.0.0.1   ; if only a local reverse proxy talks to it&lt;/code&gt;
   Use Grafana&#x27;s public dashboards feature to share a single dashboard, not anonymous access.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Upgrade Grafana&lt;/strong&gt; to at least the fixed version for your branch in the table, and
   preferably the newest patch release listed on the advisories page. If you can&#x27;t
   upgrade yet: turn off the &lt;code&gt;sqlExpressions&lt;/code&gt; feature toggle, make sure &lt;code&gt;duckdb&lt;/code&gt; is not in
   Grafana&#x27;s PATH, and remove Image Renderer unless you use it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep Prometheus and exporters on localhost or the LAN.&lt;/strong&gt; Use &lt;code&gt;--web.listen-address=127.0.0.1:9090&lt;/code&gt;
   (or your LAN IP). In compose, use &lt;code&gt;&quot;127.0.0.1:9090:9090&quot;&lt;/code&gt;, or leave out &lt;code&gt;ports:&lt;/code&gt; and let
   Prometheus scrape over the Docker network.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add basic auth&lt;/strong&gt; on Prometheus and node_exporter with &lt;code&gt;--web.config.file=web.yml&lt;/code&gt;:
   &lt;code&gt;yaml
   basic_auth_users:
     prom: $2y$10$...   # bcrypt hash, e.g. htpasswd -nBC 10 &quot;&quot; | tr -d &#x27;:\n&#x27;&lt;/code&gt;
   node_exporter has no flag to turn off &lt;code&gt;/debug/pprof&lt;/code&gt; (upstream closed that request as not
   planned). Auth or network restriction is the only fix.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify:&lt;/strong&gt; rerun the outside curls. Every one should time out or return 401.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;If you were already hit:&lt;/strong&gt; Treat every data source credential and token stored in Grafana as
leaked, along with any secrets that showed up in Prometheus labels. Rotate them, check the
Grafana users and API/service-account tokens for ones you didn&#x27;t create, and rebuild the host if
you were on a version vulnerable to the RCE bugs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why this level:&lt;/strong&gt; It hits all five: the ports are commonly forwarded, Grafana is actively
scanned (GreyNoise, CISA KEV), it&#x27;s core lab software, it leaks secrets or gives RCE, and the defaults
need no credentials. If nothing answers from outside, treat it as this-week.&lt;/p&gt;</content></entry><entry><title>ACT TONIGHT: Open WebUI: 150+ advisories in 2026, and an admin login means Python on your server</title><link href="https://labsec.an201.com/c/2026-09-28-open-webui-security"/><id>https://labsec.an201.com/c/2026-09-28-open-webui-security</id><published>2026-09-28T00:00:00Z</published><updated>2026-09-28T00:00:00Z</updated><category term="open-webui"/><category term="ollama"/><category term="docker"/><category term="exposed-api"/><summary>ACT TONIGHT. Upgrade to 0.11.4 or newer.</summary><content type="html">&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Open WebUI has published 165 GitHub security advisories: 1 in 2024, 6 in 2025 and
158 so far in 2026. Most are stored XSS, session-token theft, SSRF and access-control bugs. They
all end the same way: steal an admin&#x27;s session, then use Functions or Tools to run Python on the
server. That&#x27;s by design, not a bug. Admin, or &lt;code&gt;workspace.tools&lt;/code&gt; permission, equals code
execution on the host. The newest batch (fixed in 0.11.4, advisories out Sep 27-28) includes
GHSA-vpq8-f445-hcq7. In 0.7.0 to 0.11.3, any website you visit while logged in can steal your
session token, with community sharing on (the default). In June 2025 Sysdig caught an attacker
on an internet-facing Open WebUI with auth turned off. The attacker uploaded a malicious Tool and
dropped XMRig, T-Rex, process hiders and a Windows infostealer. Sysdig counted over 17,000
instances on Shodan at the time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who it hits:&lt;/strong&gt; You&#x27;re affected if any of these is true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;WEBUI_AUTH=False&lt;/code&gt;.&lt;/strong&gt; Every visitor is signed in as the &lt;code&gt;admin@localhost&lt;/code&gt; admin. With the
  port reachable, anyone can run Python on your box. This is the setup Sysdig saw exploited.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A fresh install exposed before you create your account.&lt;/strong&gt; The first signup becomes admin,
  whoever does it. Signup turns itself off after that.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Signup turned back on with &lt;code&gt;DEFAULT_USER_ROLE=user&lt;/code&gt;.&lt;/strong&gt; Strangers get working accounts, and
  most of the 2026 bugs only need a normal user account.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Older versions, even behind a login.&lt;/strong&gt; Examples:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;CVE-2025-64496 (≤ 0.6.34): Direct Connections, malicious model server runs JS, token theft.&lt;/li&gt;
&lt;li&gt;CVE-2025-46719 (0.6.5): stored XSS in chat, admin token, RCE via Functions.&lt;/li&gt;
&lt;li&gt;CVE-2026-45395 (&amp;lt; 0.9.5): a user with write access to one tool gets code execution.&lt;/li&gt;
&lt;li&gt;CVE-2026-59214 (&amp;lt; 0.10.0): Pyodide in a shared chat, RCE when an admin clicks Run.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CVE-2026-44551 (≤ 0.8.12): LDAP with an empty password logs in as anyone, on OpenLDAP
    defaults.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ollama published next to it on 0.0.0.0:11434.&lt;/strong&gt; Even if the UI is locked down, the raw API
  is open. See the exposed-inference-APIs card.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Check if you&#x27;re affected:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Version + auth state, no login needed. Want: version &amp;gt;= 0.11.4, auth true,
# enable_signup false, and no &amp;quot;onboarding&amp;quot;: true (that means nobody has claimed admin yet).
curl -s http://localhost:3000/api/config | jq &#x27;{version, onboarding, auth: .features.auth, enable_signup: .features.enable_signup, ldap: .features.enable_ldap}&#x27;
# Same check from OUTSIDE your network (phone hotspot, VPS) against your public hostname/IP.
# Any JSON back means the UI is reachable from the internet.
# Env set on the container (UI changes override these after first boot, so trust /api/config):
docker inspect open-webui --format &#x27;{{.Config.Image}}{{range .Config.Env}}{{&amp;quot;\n&amp;quot;}}{{.}}{{end}}&#x27; | grep -E &#x27;open-webui:|WEBUI_AUTH|ENABLE_SIGNUP|DEFAULT_USER_ROLE|ENABLE_DIRECT_CONNECTIONS&#x27;
# Is Ollama exposed next to it? Want 127.0.0.1 only.
ss -ltnp | grep -E &#x27;:(11434|3000|8080)\b&#x27;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Do this:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tonight:&lt;/strong&gt; upgrade to 0.11.4 or newer. Pin the tag instead of &lt;code&gt;:main&lt;/code&gt; so you know what
   you&#x27;re running: &lt;code&gt;docker pull ghcr.io/open-webui/open-webui:v0.11.4&lt;/code&gt;, then recreate the
   container with the same volume (&lt;code&gt;-v open-webui:/app/backend/data&lt;/code&gt;). pip installs:
   &lt;code&gt;pip install -U open-webui&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If &lt;code&gt;features.auth&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, turn auth back on: remove &lt;code&gt;WEBUI_AUTH=False&lt;/code&gt; and set a
   password on the admin account. If it&#x27;s reachable from outside, take it offline first.
   Never run no-auth Open WebUI on anything but &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Claim the admin account before exposing a new install. Better: set &lt;code&gt;WEBUI_ADMIN_EMAIL&lt;/code&gt; and
   &lt;code&gt;WEBUI_ADMIN_PASSWORD&lt;/code&gt; on first boot, which creates the admin and turns signup off.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Admin Panel &amp;gt; Settings &amp;gt; General: signup off unless you need it, and Default User Role
   &lt;code&gt;pending&lt;/code&gt;. Only give Tools/Functions access to people you&#x27;d trust with a shell on the host.
   If you don&#x27;t use plugins, set &lt;code&gt;ENABLE_PLUGINS=false&lt;/code&gt;. Also turn off Direct Connections,
   community sharing, and code execution/interpreter unless you use them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don&#x27;t port-forward 3000/8080. Reach it over Tailscale or WireGuard, or put it behind
   Cloudflare Access or another forward-auth layer. Publish Ollama as &lt;code&gt;127.0.0.1:11434:11434&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Confirm: re-run the &lt;code&gt;curl&lt;/code&gt;. It should show the new version, &lt;code&gt;auth: true&lt;/code&gt;, and
   &lt;code&gt;enable_signup: false&lt;/code&gt;. From outside it should time out or hit your auth proxy first.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;If you were already hit:&lt;/strong&gt; In Workspace &amp;gt; Tools and Admin &amp;gt; Functions, look for anything you
didn&#x27;t install. Check Admin &amp;gt; Users for unknown admins. Inside the container, look for
cryptominers (&lt;code&gt;docker exec open-webui ps aux&lt;/code&gt;). Rotate &lt;code&gt;WEBUI_SECRET_KEY&lt;/code&gt; (this logs out every
session) and every provider API key stored in Connections. Assume the host is compromised if
an unknown Tool or Function ran, and rebuild it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why this level:&lt;/strong&gt; Four yeses: often exposed through tunnels and port-forwards, exploited in
the wild (Sysdig) with public PoCs in the advisories, a core lab AI tool, and admin access
means Python on the host. No-friction is &quot;no&quot; because a default install creates the first admin,
turns signup off, and sets new users to pending. Most current bugs need an account or a click.
The patch is 7 days old, so no auto-update downgrade.&lt;/p&gt;</content></entry><entry><title>ACT TONIGHT: MikroTik RouterOS: unauthenticated SSH takeover chain (&quot;MikroTrick&quot;) exploited since Sept 2</title><link href="https://labsec.an201.com/c/2026-09-03-mikrotik-routeros-mikrotrick-ssh"/><id>https://labsec.an201.com/c/2026-09-03-mikrotik-routeros-mikrotrick-ssh</id><published>2026-09-03T00:00:00Z</published><updated>2026-09-28T00:00:00Z</updated><category term="router"/><category term="mikrotik"/><summary>ACT TONIGHT. Shut SSH off to the internet.</summary><content type="html">&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; Attackers are taking full admin on MikroTik routers over SSH with no password and
no key. The chain (&quot;MikroTrick&quot;, named by CERT Polska): CVE-2026-67279 lets an unauthenticated
client skip SSH auth by asking for a rekey mid-login. CVE-2026-86060 then feeds the username
&lt;code&gt;-2&lt;/code&gt; to the login helper and gets full admin rights. Attacks started 2026-09-02, one day
before MikroTik shipped fixes. Both chain CVEs are in CISA KEV. The same release also
fixes CVE-2026-67276, where the SSH RSA public-key check ignores the exponent, and
CVE-2026-67277, an unauthenticated bandwidth-test (btest) kernel memory leak and crash (also in KEV).
Seen after break-in: a new full-admin user &lt;code&gt;ops&lt;/code&gt;, plus scripts, scheduler jobs, proxies and
tunnels, and file transfers to attacker hosts that look like config theft.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who it hits:&lt;/strong&gt; A MikroTik (hAP, RB, CCR, CRS, CHR) on RouterOS below 7.24.2 / 7.23.4 /
6.49.21 where SSH (port 22, or wherever you moved it) is reachable from the internet. That
includes a VPS running CHR, and a &quot;temporary&quot; WAN rule allowing SSH that never got removed.
MikroTik says the default config doesn&#x27;t expose SSH on WAN. Scans on 2026-09-05 still found
about 122,500 devices with SSH open to the internet. Only the SSH users are exposed to the
full chain. A btest server (port 2000) open to the internet is exposed to the leak and the
crash.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check if you&#x27;re affected:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# From a LAN box: installed version (vulnerable if below 7.24.2, 7.23.4 on long-term, 6.49.21 on v6)
ssh admin@192.168.88.1 &#x27;/system resource print&#x27; | grep -i version
# From OUTSIDE your network (phone hotspot, VPS): does SSH answer on your WAN IP?
nc -vz -w 5 YOUR_PUBLIC_IP 22
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Do this:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tonight:&lt;/strong&gt; shut SSH off to the internet. In the RouterOS terminal:
   &lt;code&gt;/ip service set ssh address=192.168.88.0/24&lt;/code&gt; (use your LAN or VPN subnet), or
   &lt;code&gt;/ip service disable ssh&lt;/code&gt; if you don&#x27;t use it. Do the same for &lt;code&gt;www&lt;/code&gt;, &lt;code&gt;www-ssl&lt;/code&gt; and &lt;code&gt;winbox&lt;/code&gt;.
   Then &lt;code&gt;/tool bandwidth-server set enabled=no&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Upgrade: &lt;code&gt;/system package update check-for-updates&lt;/code&gt; then &lt;code&gt;/system package update install&lt;/code&gt;.
   You want 7.24.2 or newer on stable, 7.23.4 on long-term, or 6.49.21 on v6. Then
   &lt;code&gt;/system routerboard upgrade&lt;/code&gt; and reboot again so the firmware matches.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Look for signs you were hit: &lt;code&gt;/user print&lt;/code&gt; (look for &lt;code&gt;ops&lt;/code&gt; or any user you don&#x27;t know),
   &lt;code&gt;/log print where message~&quot;-2&quot;&lt;/code&gt; (look for &lt;code&gt;login failure for user -2&lt;/code&gt; or &lt;code&gt;added by ssh:-2@&lt;/code&gt;),
   &lt;code&gt;/system script print&lt;/code&gt;, &lt;code&gt;/system scheduler print&lt;/code&gt;, &lt;code&gt;/ip proxy print&lt;/code&gt;, &lt;code&gt;/interface print&lt;/code&gt;
   (look for tunnels you didn&#x27;t make).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Confirm the fix: &lt;code&gt;/system resource print&lt;/code&gt; shows the fixed version, and the &lt;code&gt;nc&lt;/code&gt; check from
   outside times out.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remote management from now on should go over WireGuard or Tailscale, not an open port.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;If you were already hit:&lt;/strong&gt; Pull the WAN cable. Save &lt;code&gt;/export&lt;/code&gt; and the logs for evidence, then
netinstall or factory-reset to a fixed version. Rebuild the config by hand; don&#x27;t restore the
backup. Change every password, and rotate every key and WireGuard/VPN secret the router held.
It was your gateway, so treat LAN traffic in that window as seen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why this level:&lt;/strong&gt; It answers yes on all five rubric questions: SSH is exposed on WAN on
six-figure numbers of devices, three CVEs are in KEV with confirmed attacks, MikroTik is a
home-lab router staple, owning the gateway means owning the LAN, and no credentials are
needed. It is also an unauthenticated takeover under active exploitation, which triggers the
act-tonight override.&lt;/p&gt;</content></entry><entry><title>THIS WEEK: Cloudflare Tunnel: public hostnames with no Access policy, leaked tunnel tokens, and origins still port-forwarded</title><link href="https://labsec.an201.com/c/2026-09-28-cloudflare-tunnel-exposure-mistakes"/><id>https://labsec.an201.com/c/2026-09-28-cloudflare-tunnel-exposure-mistakes</id><published>2026-09-28T00:00:00Z</published><updated>2026-09-28T00:00:00Z</updated><category term="cloudflare-tunnel"/><category term="cloudflare-access"/><category term="proxmox"/><category term="portainer"/><category term="grafana"/><category term="compose"/><category term="router"/><summary>THIS WEEK. Put Access in front of every admin hostname.</summary><content type="html">&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; A tunnel only moves traffic. It does not add a login. Cloudflare&#x27;s own docs say it plainly:
&quot;If you do not have an Access application in place, the published application will be available to
anyone on the Internet.&quot; Three mistakes keep showing up in labs. You publish Proxmox, Portainer or
Grafana through a tunnel with no Access policy. You commit the tunnel token to a public repo or
compose file, and &quot;Anyone with the token can run the tunnel.&quot; Or you leave the old port-forward
open, so the origin can still be reached without going through Cloudflare. A separate issue:
throwaway &lt;code&gt;*.trycloudflare.com&lt;/code&gt; Quick Tunnels are a documented malware delivery channel. Proofpoint
has tracked it since February 2024, Securonix reported SERPENTINE#CLOUD in June 2025, and Cofense
reported a 2025 peak in March 2026.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who it hits:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Any public hostname on your tunnel that has no matching Access application. This is worst for
  admin UIs: Proxmox (8006), Portainer (9443/9000) and Grafana (3000). Their own login page becomes
  the only thing between the internet and your hypervisor or Docker socket.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;TUNNEL_TOKEN=eyJ...&lt;/code&gt; or &lt;code&gt;tunnel run --token eyJ...&lt;/code&gt; in a &lt;code&gt;docker-compose.yml&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;, or
  Ansible file that has ever been pushed to GitHub or a public Gitea/Forgejo, including git history.
  Anyone holding it can run a connector for your tunnel and take a share of your traffic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Routers that still forward 443, 8006 or 9443 to the box after you moved to a tunnel, and UPnP
  mappings too. Your Access policy does nothing on that path.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Anyone using &lt;code&gt;cloudflared tunnel --url ...&lt;/code&gt; Quick Tunnels to &quot;just share something&quot;. Cloudflare
  says they are &quot;intended for testing and development only.&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Check if you&#x27;re affected:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 1. For EVERY hostname in Zero Trust &amp;gt; Networks &amp;gt; Tunnels &amp;gt; &amp;lt;tunnel&amp;gt; &amp;gt; Public hostnames:
#    open it in a private window. You must see a Cloudflare Access login, not the app.
#    Scripted version: a protected host answers with a redirect to &amp;lt;team&amp;gt;.cloudflareaccess.com.
for h in proxmox.example.com portainer.example.com grafana.example.com; do
  printf &#x27;%s -&amp;gt; &#x27; &amp;quot;$h&amp;quot;
  curl -s -o /dev/null -w &#x27;%{http_code} %{redirect_url}\n&#x27; &amp;quot;https://$h/&amp;quot;
done
# 200, or a redirect to the app&#x27;s own /login  = NOT protected by Access

# 2. Tunnel tokens in files and in git history (tokens start with eyJhIjoi)
grep -rEn &#x27;TUNNEL_TOKEN|--token[ =]|eyJhIjoi&#x27; ~/ --include=&#x27;*.yml&#x27; --include=&#x27;*.yaml&#x27; \
  --include=&#x27;*.env&#x27; --include=&#x27;.env&#x27; --include=&#x27;*.sh&#x27; 2&amp;gt;/dev/null
for r in $(find ~ -name .git -type d -prune 2&amp;gt;/dev/null); do
  git -C &amp;quot;${r%/.git}&amp;quot; log -p --all 2&amp;gt;/dev/null | grep -q &#x27;eyJhIjoi&#x27; &amp;amp;&amp;amp; echo &amp;quot;TOKEN IN HISTORY: ${r%/.git}&amp;quot;
done

# 3. Is the origin reachable directly? Run this from OUTSIDE your LAN (phone hotspot):
nmap -Pn -p 22,80,443,3000,8006,9000,9443 &amp;lt;your-WAN-IP&amp;gt;
#    Any &amp;quot;open&amp;quot; port is bypassing the tunnel. Also check the router&#x27;s port-forward and UPnP pages.

# 4. Stray cloudflared / quick tunnels on your boxes
pgrep -a cloudflared; docker ps --format &#x27;{{.Names}} {{.Image}}&#x27; | grep -i cloudflared
#    Any &amp;quot;--url&amp;quot; / trycloudflare process you don&#x27;t recognise = investigate.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Do this:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Put Access in front of every admin hostname.&lt;/strong&gt; Go to Zero Trust &amp;gt; Access controls &amp;gt;
   Applications &amp;gt; Create new application &amp;gt; Self-hosted, and add the public hostname. Attach an
   Allow policy for your own email or IdP group only. Access is deny by default, so anyone not
   matched gets nothing. Do this &lt;em&gt;before&lt;/em&gt; you add new tunnel routes, not after.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If a token was ever public, rotate it and kill live connectors.&lt;/strong&gt; In the dashboard, go to
   Networking &amp;gt; Tunnels &amp;gt; your tunnel &amp;gt; &lt;strong&gt;Refresh token&lt;/strong&gt;. Some doc pages call the button &quot;Rotate
   token&quot;. Then reinstall/restart &lt;code&gt;cloudflared&lt;/code&gt; everywhere with the new token. Refreshing blocks
   &lt;em&gt;new&lt;/em&gt; connections with the old token, but existing connectors stay up until restarted. That
   includes one an attacker is running. Drop them all:
   &lt;code&gt;curl -X DELETE &quot;https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/cfd_tunnel/$TUNNEL_ID/connections&quot; -H &quot;Authorization: Bearer $CLOUDFLARE_API_TOKEN&quot;&lt;/code&gt;
   Then check the tunnel&#x27;s Connectors list shows only your hosts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Get the token out of compose.&lt;/strong&gt; On cloudflared 2025.4.0 or newer, use &lt;code&gt;--token-file&lt;/code&gt; /
   &lt;code&gt;TUNNEL_TOKEN_FILE&lt;/code&gt; pointing at a file that is not tracked by git, or keep &lt;code&gt;TUNNEL_TOKEN&lt;/code&gt; in a
   gitignored &lt;code&gt;.env&lt;/code&gt;. Scrubbing git history does not un-leak a token. Rotate it anyway.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Close the side door.&lt;/strong&gt; Delete every router port-forward to the tunnelled services and turn off
   UPnP. &lt;code&gt;cloudflared&lt;/code&gt; only makes outbound connections, so the host needs no inbound ports at all.
   Firewall inbound to deny.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Don&#x27;t use Quick Tunnels for anything real.&lt;/strong&gt; If nothing in your lab uses them, add
   &lt;code&gt;trycloudflare.com&lt;/code&gt; to a Pi-hole/AdGuard blocklist, or at least alert on it. On a lab network, a
   lookup for it usually means malware staging or someone&#x27;s test tunnel you forgot about.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify.&lt;/strong&gt; Re-run checks 1–3. Every admin host should redirect to &lt;code&gt;cloudflareaccess.com&lt;/code&gt;, grep
   should come back empty, and the outside nmap should show all ports filtered/closed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why this level:&lt;/strong&gt; Reachable, common and high blast radius: a Proxmox or Portainer panel is the
hypervisor or the Docker socket. That is 3 yes answers, so this-week. It is not higher because the
app&#x27;s own login still stands in the way, and no documented campaign (as of 2026-09-28) targets
unprotected lab tunnel hostnames. The Quick Tunnel abuse is real but targets phishing victims, not
your tunnel. If you run Portainer/Proxmox with default or reused creds behind an unprotected
hostname, treat it as act-tonight.&lt;/p&gt;</content></entry><entry><title>THIS WEEK: MCP servers: unauthenticated dev proxies, backdoored packages, and tool poisoning in your agent setup</title><link href="https://labsec.an201.com/c/2026-09-28-mcp-server-risks"/><id>https://labsec.an201.com/c/2026-09-28-mcp-server-risks</id><published>2026-09-28T00:00:00Z</published><updated>2026-09-28T00:00:00Z</updated><category term="mcp"/><category term="agents"/><category term="supply-chain"/><category term="exposed-api"/><summary>THIS WEEK. Kill any Inspector that&#x27;s running and upgrade.</summary><content type="html">&lt;p&gt;&lt;strong&gt;What:&lt;/strong&gt; An MCP server is code your agent runs with your user&#x27;s permissions, and the model
obeys whatever text it hands back. Four documented ways this goes wrong:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unauthenticated dev tools.&lt;/strong&gt; MCP Inspector below 0.14.1 ran a proxy on port 6277 with no
  auth. It would spawn any command it was told to (CVE-2025-49596, CVSS 9.4). A web page you
  visit can reach it through &lt;code&gt;0.0.0.0&lt;/code&gt; or DNS rebinding, so it doesn&#x27;t have to be port-forwarded.
  Oligo found instances exposed to the internet. mcp-remote 0.0.5 to 0.1.15 executes a shell
  command hidden in a malicious server&#x27;s OAuth &lt;code&gt;authorization_endpoint&lt;/code&gt; (CVE-2025-6514, CVSS 9.6).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Backdoored packages.&lt;/strong&gt; &lt;code&gt;postmark-mcp&lt;/code&gt; on npm was a copy of the real Postmark server. It
  behaved for 15 versions, then 1.0.16 added one line that BCC&#x27;d every sent email to the attacker.
  In February 2026 the SANDWORM_MODE worm (typosquats like &lt;code&gt;claud-code&lt;/code&gt; and &lt;code&gt;cloude-code&lt;/code&gt;)
  wrote a rogue MCP server into Claude Code, Claude Desktop, Cursor, Continue and Windsurf
  configs. That server used prompt injection to pull SSH keys, AWS creds, npm tokens and &lt;code&gt;.env&lt;/code&gt;
  files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tool poisoning and prompt injection.&lt;/strong&gt; Tool descriptions can hide instructions that the
  model reads and the UI doesn&#x27;t show. Invariant Labs used this to make Cursor leak
  &lt;code&gt;~/.cursor/mcp.json&lt;/code&gt; and &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;. A server can also change its descriptions after you
  approve it (a &quot;rug pull&quot;). A README, issue or web page the agent reads works the same way.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Over-broad file and shell servers.&lt;/strong&gt; The official filesystem server could be escaped via
  symlinks and prefix tricks (CVE-2025-53109/53110). The official git server had path traversal
  and argument injection (CVE-2025-68143/68144/68145). Prompt injection can reach those bugs,
  and chained with the filesystem server they give code execution.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Who it hits:&lt;/strong&gt; You&#x27;re affected if any of these is true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You ran &lt;code&gt;npx @modelcontextprotocol/inspector&lt;/code&gt; (or &lt;code&gt;mcp dev&lt;/code&gt;) on an old version, or left it
  running.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your Claude Code, Cursor or Claude Desktop config launches servers with &lt;code&gt;npx -y &amp;lt;pkg&amp;gt;&lt;/code&gt; or
  &lt;code&gt;uvx &amp;lt;pkg&amp;gt;&lt;/code&gt; and no version, so every restart pulls whatever was published last.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You have a filesystem server rooted at &lt;code&gt;~&lt;/code&gt; or &lt;code&gt;/&lt;/code&gt;, or a shell or git server, &lt;strong&gt;and&lt;/strong&gt; you
  auto-approve its tools. It&#x27;s worse if a fetch, browser or email server sits in the same session.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You installed an MCP server from a random npm or PyPI name instead of the vendor&#x27;s own repo.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Check if you&#x27;re affected:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Inspector / MCP HTTP servers listening. Anything on 0.0.0.0, [::] or * is reachable off-box
ss -ltnp | grep -E &#x27;:(6274|6277)\b|node|python|uvx&#x27;
# Every MCP server your clients will launch. Look for unpinned npx/uvx and names you don&#x27;t recognise
claude mcp list
jq &#x27;.mcpServers, (.projects[]?.mcpServers)&#x27; ~/.claude.json 2&amp;gt;/dev/null
jq &#x27;.mcpServers&#x27; ~/.cursor/mcp.json .mcp.json 2&amp;gt;/dev/null
# Installed versions of the known-bad packages, plus the typosquats
npm ls -g --depth=0 2&amp;gt;/dev/null | grep -Ei &#x27;mcp|inspector|postmark|claud-code|cloude&#x27;
uv tool list 2&amp;gt;/dev/null | grep -i mcp; pip list 2&amp;gt;/dev/null | grep -i mcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Do this:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Kill any Inspector that&#x27;s running and upgrade. Use &lt;code&gt;npx @modelcontextprotocol/inspector@latest&lt;/code&gt;
   (0.14.1+ needs a session token and checks Origin). Run it only while you&#x27;re debugging.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Upgrade: mcp-remote to 0.1.16+, server-filesystem to 0.6.3 / 2025.7.01+, mcp-server-git to
   2025.12.18+. If &lt;code&gt;postmark-mcp&lt;/code&gt; (npm) or any SANDWORM_MODE typosquat turns up, remove it and
   rotate everything it could reach: mail, SSH keys, cloud and npm tokens, and LLM API keys.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pin versions in the config (&lt;code&gt;npx -y some-mcp@1.2.3&lt;/code&gt;, &lt;code&gt;uvx some-mcp==1.2.3&lt;/code&gt;) and install from
   the vendor&#x27;s own repo. Re-read the tool descriptions when you bump a version.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Delete servers you don&#x27;t use. Point filesystem servers at one project directory, never &lt;code&gt;~&lt;/code&gt;.
   Keep &lt;code&gt;~/.ssh&lt;/code&gt;, &lt;code&gt;~/.aws&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; outside every allowed root.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don&#x27;t auto-approve shell, filesystem-write, git or email tools. Don&#x27;t put a server that
   fetches untrusted content (web, issues, mail) in the same session as one that can write or send.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Any MCP server that speaks HTTP should bind to &lt;code&gt;127.0.0.1&lt;/code&gt; and never be port-forwarded or
   tunneled without an auth layer in front. Running the agent plus its servers in a container
   or VM with only the project mounted caps the blast radius.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Confirm: &lt;code&gt;ss&lt;/code&gt; shows nothing on 6274/6277, and &lt;code&gt;claude mcp list&lt;/code&gt; shows only servers you meant
   to add, each with a pinned version.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Why this level:&lt;/strong&gt; Three yes. It&#x27;s exploited (a backdoored MCP package and a worm writing rogue
MCP configs in the wild, plus public PoCs). MCP is in the lab AI stack. The blast radius is your
shell, SSH keys and API keys. Reachable is no because most MCP servers are stdio and not exposed.
No-friction is no because most paths need you to install a bad package or have the agent read
attacker content. If you run an old Inspector or a 0.0.0.0-bound MCP server, treat it as
act-tonight.&lt;/p&gt;</content></entry></feed>
