The door you forgot to close: the default-enabled web interface as a durable self-hosting attack surface
Access date: 2026-10-19 Format: Evergreen deep-dive (durable synthesis, evidence-first) Status: Publication-ready draft Pillar: Self-hosted infrastructure security
What this is
A pattern synthesis. It names a recurring 2026 pattern in self-hosted software, explains the shared structural root causes across seven named incidents, argues why the pattern is durable and structural, keeps it distinct from two other durable self-hosting patterns (the remote-access appliance cluster and the self-hosted AI inference stack), and hands the reader a five-item operator checklist. Every load-bearing claim is tied to a named product, a CVE, and a source. This is a synthesis built on evidence, not a rewrite of the news.
1. Why it matters: the recurring 2026 pattern
Across 2026, the same shape keeps showing up in self-hosted software. A web interface ships active by default. The operator never intended to expose it. Then it becomes the breach vector or the crash vector.
Seven named products exhibit it, all verified in the source table (section 3):
- Metabase (CVE-2026-103928, 9.3): the sample H2 database is active by default, and H2's host-facing built-in functions (FILE_READ, FILE_WRITE, CSVREAD, CSVWRITE, LINK_SCHEMA) pass through unrestricted to any user with native-query rights.
- ZITADEL (10 CVEs, top CVE-2026-105211 at 9.2, three at 9.3): a single HTTP/2 port serves gRPC, the HTTP API, and the management console, and the login flow itself is the flaw surface.
- phpMyFAQ (CVE-2026-111911, 9.8, plus 27 others): the WebAuthn registration endpoint attaches a passkey to any account without checking authentication or validating the challenge.
- Vikunja (GHSA-m687-p538-r5hp, 7.2, plus 28 others): the CORS default is localhost wildcards with credentials allowed, so any localhost page can steal the session token.
- Znuny (CVE-2026-93483, 9.5, and CVE-2026-93484, 8.6): an HTTP header reaches the login path unchecked, and a Content-Type header is concatenated into a database query.
- Open WebUI (CVE-2026-54020, 6.3): first-boot registration is open, and an outbound fetch path is unvalidated, enabling DNS-rebinding SSRF.
- SolarWinds Serv-U (CVE-2026-28318, 7.5, KEV-listed): the web interface is enabled by default alongside FTP, and an unauthenticated POST with
Content-Encoding: deflatecrashes the service.
Four of the seven bind all interfaces in Docker (ZITADEL 8080, Vikunja 3456, Open WebUI 3000, Metabase 3000). Three sit on standard web-server ports (Znuny, phpMyFAQ, Serv-U, all 80/443). Only Serv-U is KEV-listed. The other six have no confirmed in-the-wild exploitation as of 2026-10-19. All seven have released fixes.
Why the pattern is durable, not exotic
An exotic bug is a rare configuration, a one-off coding error, or a version-specific defect that a patch removes and that does not recur. The default-door pattern is none of these. It is durable and structural for five reasons.
The default is the exposure. The Metabase post says it plainly: "the default is the exposure." An out-of-the-box install carries the exposed surface. No operator error is required to put the door on the network. A pattern built on the baseline state of the product is durable by construction, because every fresh install re-creates the exposure.
The ingredients are the default state of the category. Every web service ships a web UI. Most self-hosted tools ship it enabled. Most run in Docker (all-interfaces bind) or on standard ports (80/443). Auth is often present, but the flow is the flaw, or the trigger needs no auth, or first-boot registration is open. These are the normal defaults of the self-hosted web tool. A pattern assembled from the category's defaults is durable because the defaults are durable.
The flaw lives in application code, not a single config flag. ZITADEL's ten flaws are "one trust-model problem remediated in stages" across five versions over six weeks (fix waves 4.16.2/3.4.14 on 2026-07-29, 4.17.1/3.4.15 on 2026-08-14, 4.17.3 on 2026-09-04). Vikunja had 29 advisories over three months. phpMyFAQ shipped 28 fixes. The fixes are per-flaw and per-version. The underlying default posture (web UI active, wide bind, trust the input) persists, because it is the product's design. You can patch the flaw. You cannot turn off the web UI without changing how you run the product.
The closure burden falls on the operator, individually, by platform default. Docker's all-interfaces binding is standard Docker behavior, not a ZITADEL-specific default. The container binds all interfaces. The operator must restrict via port mapping or firewall. The same finding holds across the whole self-hosted stack (the inference-stack post states it identically: "the onus to close it falls on every operator individually"). The product does not encode the operator's intent (a LAN tool). It ships the default (reachable). The gap between intent and default is structural, and it re-appears on every fresh install.
It re-appears across products and across versions. In 2026 alone, the seven named products each shipped the pattern. Fixes cascade across versions (ZITADEL: 4.16.2 to 4.17.3 over six weeks). The next product in the category will ship the same default. And the exploitability is not gated on a rare precondition. Several triggers need no auth at all (Serv-U unauth DoS; Open WebUI first-boot open registration; Metabase native-query access is a common analyst-dashboard config). When the trigger is a normal configuration or an unauthenticated request, the exposure is live the moment the door is reachable.
The one-line contrast with exotic: an exotic bug is a rare config or a one-off defect a patch removes. The default-door pattern is the baseline state of the category, it is in the design, it re-appears on every install and across the category, and its triggers are normal configurations or unauthenticated requests. That is why it is a durable structural pattern, and why the operator checklist (section 4) is a standing discipline rather than a one-time fix.
2. The shared structural root cause
Four structural conditions recur across all seven. They are four layers of the same default posture, and most incidents stack two or more of them.
2.1 Default-on UI (the door ships open)
Every one of the seven ships a web interface that is active by default. The exposure is the baseline state. An out-of-the-box install carries the exposed surface, and no operator error is required to put the door on the network.
- Docker all-interfaces bind (4): ZITADEL 8080, Vikunja 3456, Open WebUI 3000, Metabase 3000. The container binds all interfaces; the operator must restrict via port mapping or firewall.
- Standard web-server ports (3): Znuny 80/443 (Apache), phpMyFAQ 80/443 (PHP/PHP-FPM), Serv-U 80/443 (HTTP/HTTPS alongside FTP).
- Native localhost bind, lost in Docker (1): Metabase's native JVM binds localhost by default, but the Docker deployment (the dominant self-hosting path) maps 3000 to all interfaces, so the mitigation is lost in the common case.
2.2 Weak or absent auth (the lock is the flaw, or there is no lock)
Auth is present in most of the seven. But the auth flow itself is the vulnerability surface, or the trigger needs no auth at all, or first-boot registration is open.
- Auth flow is the flaw surface: ZITADEL (identify-only state lets privileged actions run before the primary factor is verified), Znuny (HTTPBasicAuth header manipulation), phpMyFAQ (WebAuthn registration endpoint bypasses password and TOTP).
- Auth required for the vulnerable operation, but the default widens it: Metabase (native-query access is a common analyst-dashboard config; the sample DB is the default), Vikunja (CORS default lets any localhost page steal session tokens).
- Auth absent for the trigger: Serv-U (unauthenticated POST with
Content-Encoding: deflate). - First-boot registration open: Open WebUI (anyone can create an admin account on first boot).
The structural point: "auth is present" and "auth is enforced for the operation that matters" are two different things. In these cases the lock exists but is the thing that breaks, or the lock is not on the door that gets knocked on.
2.3 Unscoped trust (the app trusts the input or the default beyond what it should)
A default configuration value, or an input path, is trusted further than it should be.
- A default config value is the exposure: Vikunja (
cors.originsdefaults to localhost wildcards withAccess-Control-Allow-Credentials: true;service.publicurlis appended to the defaults rather than replacing them), Metabase (H2 sample DB active by default, and H2 host-facing built-ins passed through unrestricted to native-query users). - Unvalidated input reaches privileged code: Znuny (a single HTTP request header reaches the login path unchecked; a
Content-Typeheader is concatenated into the database query without parameterization), ZITADEL (a client reaches an identify-only state and runs privileged actions before primary-factor verification). - An outbound fetch path is unvalidated: Open WebUI (TOCTOU in URL validation; the single-resolve check passes, then the HTTP client re-resolves the hostname at connect time, enabling DNS rebinding to cloud metadata, loopback admin APIs, and internal services).
The shared shape: the application grants a capability (file read/write, a privileged action, a session token, an outbound fetch, a passkey) to a caller or input it has not scoped. The default is the scope, and the default is too wide.
2.4 Missing network boundary (the door is reachable by a stranger)
The service binds wide (all-interfaces in Docker, or standard web ports) and the operator has not restricted reach, so the network position lets a stranger reach the door. This is the layer that turns a default-on UI into an exposed default-on UI. The homelab audit (section 3, cross-link 8) is the hands-on evidence for this layer: a UDM Pro management dashboard directly reachable with no second auth layer, Gitea publicly reachable with a live login form, an unidentified listener on port 8080 answering with a Tomcat-style 400. The five-agency joint advisory PP-26-3374 (2026-08-19) frames the threat model: actors use Censys/ZoomEye scanning plus AI-generated exploit scripts and default credentials (T1596.005 + T1694), and "the same playbook works against a homelab that exposes a router dashboard, a git server, or an inference API the way it works against a PLC rack."
The structural point: the network position is what makes the default door a stranger-reachable door. A default-on UI on an isolated LAN is a latent exposure. The same default-on UI on a wide bind is a live one. The missing boundary is the difference between "the door exists" and "a stranger can reach the door."
How the four stack (per incident)
| Product | Default-on UI | Weak/absent auth | Unscoped trust | Missing network boundary |
|---|---|---|---|---|
| Metabase | Docker 3000 all-interfaces | Native-query access is a common config | H2 sample DB + built-ins unscoped | Wide bind makes it reachable |
| ZITADEL | Docker 8080 all-interfaces | Auth flow is the flaw surface | Identify-only privileged actions | Wide bind makes it reachable |
| phpMyFAQ | 80/443 standard ports | WebAuthn registration bypass | Credential attached to any account | Standard port makes it reachable |
| Vikunja | Docker 3456 all-interfaces | Session theft via CORS default | CORS localhost-wildcard-with-credentials | Wide bind + shared host makes it reachable |
| Znuny | 80/443 standard ports | Header reaches login path unchecked | Header input reaches privileged code | Standard port makes it reachable |
| Open WebUI | Docker 3000 all-interfaces | First-boot registration open | Unvalidated outbound fetch path | Wide bind + IMDSv1 makes it reachable |
| Serv-U | 80/443 all-interfaces | Trigger needs no auth | Safety check wired to the wrong condition | Wide bind makes it reachable |
Every incident stacks at least two of the four. That stacking is what makes the pattern structural: it is not one layer failing, it is the default posture failing across layers at once.
3. Which products most commonly ship a default-enabled web interface, and their default state
The seven-row source table, verified in t_260cf499 (access 2026-10-19). Every row is "Confirmed."
| # | Product | Default web interface state | Vulnerability type | CVE / advisory | Citation | Claim status |
|---|---|---|---|---|---|---|
| 1 | Metabase | Web UI on 3000; native JVM binds localhost by default, but Docker maps 3000 to all interfaces. Sample H2 database active by default. Auth required for native-query access. | File read/write via H2 built-in functions (FILE_READ, FILE_WRITE, CSVREAD, CSVWRITE, LINK_SCHEMA) | CVE-2026-103928 (9.3); GHSA-cwxq-fmxq-jv8h | Malwlab post (2026-10-15); vendor GHSA; NVD | Confirmed |
| 2 | ZITADEL | Single HTTP/2 port 8080 serves gRPC, HTTP API, and management console. Docker compose maps 8080 to host; all-interfaces binding in container. Auth required, but the login flow is the vulnerability surface. | Trust-model error: "identify-only" state allows privileged actions before primary factor verification. 10 CVEs. | CVE-2026-105211 (9.2), CVE-2026-105209 (9.3), CVE-2026-105207 (9.3), CVE-2026-105215 (9.3), + 6 others; 7 GHSAs | Malwlab post (2026-10-07); vendor advisories; NVD | Confirmed |
| 3 | phpMyFAQ | Web app on standard web-server port (80/443). Public FAQ area accessible without auth; admin requires login. First login uses "admin" with operator-chosen password. | WebAuthn registration endpoint bypass: forged fmt=none attestation attaches passkey to any account, bypassing password and TOTP. 28 CVEs total. | CVE-2026-111911 (9.8); GHSA-768h-xwv2-fwp3; + 27 others | Malwlab post (2026-10-18); vendor GHSA; NVD; changelog | Confirmed |
| 4 | Vikunja | Web UI on 3456 (Docker). CORS enabled by default with localhost wildcards and Access-Control-Allow-Credentials: true. Auth required for API, but CORS default allows any localhost page to steal session tokens. |
Permissive cross-domain security policy (CWE-942): any localhost page can make credentialed request to token-refresh endpoint and read bearer JWT. 29 advisories total. | GHSA-m687-p538-r5hp (7.2, CWE-942); CVE-2026-68582 (9.3, BOLA); + 7 others | Malwlab post (2026-10-15); vendor advisories; NVD; release v2.7.0 | Confirmed |
| 5 | Znuny | Web UI via Apache on 80/443 (standard web-server ports). Agent and customer interfaces require login, but HTTPBasicAuth modules are the vulnerability surface. | Auth bypass via HTTP header manipulation (9.5) + SQL injection in ticket search via Content-Type header (8.6). Shared root cause: unvalidated HTTP header input reaching privileged code paths. | CVE-2026-93483 (9.5), CVE-2026-93484 (8.6); ZSA-2026-14, ZSA-2026-15 | Malwlab post (2026-10-16); vendor ZSA-2026-14, ZSA-2026-15; NVD | Confirmed |
| 6 | Open WebUI | Web UI on 3000 (Docker maps to container 8080). All-interfaces binding in container. First boot: registration open (anyone can create admin account). ENABLE_LOCAL_WEB_FETCH=False by default (private-IP rejection active, but defeatable via DNS rebinding). |
DNS-rebinding SSRF (CWE-367 + CWE-918): TOCTOU in URL validation. Single-resolve check passes, then HTTP client re-resolves hostname at connect time. 5 fetch paths; OAuth picture path forwards Bearer token. | CVE-2026-54020 (6.3); GHSA-h6x2-583h-x99r; CISA SB26-222 | Malwlab post (draft, 2026-08-25); vendor GHSA; NVD | Confirmed |
| 7 | SolarWinds Serv-U | Web interface enabled by default. Serves HTTP/HTTPS alongside FTP/FTPS/SFTP. All-interfaces binding. Auth required for file operations, but the DoS trigger is unauthenticated. | Uncontrolled resource consumption (CWE-400): unauthenticated POST with Content-Encoding: deflate crashes service. Root cause: deflate rejection check only runs when HTTP compression is disabled; compression on by default, so body reaches decompressor with invalid free (heap corruption, DoS-only). |
CVE-2026-28318 (7.5); GHSA-4vx4-vp54-mwmc; CISA KEV (added 2026-06-05, due 2026-06-19) | Malwlab coverage (homelab file-transfer pattern, 2026-10-19); vendor advisory; release notes; NVD; CISA KEV; Bishop Fox | Confirmed |
The three default-state groups
- All-interfaces binding (Docker): ZITADEL (8080), Vikunja (3456), Open WebUI (3000), Metabase (3000 via Docker). The container binds all interfaces; the operator must restrict via port mapping or firewall.
- Standard web-server ports: Znuny (80/443 via Apache), phpMyFAQ (80/443 via PHP/PHP-FPM), Serv-U (80/443 HTTP/HTTPS alongside FTP).
- Native localhost binding, lost in Docker: Metabase (JVM binds localhost by default, but the Docker deployment overrides this).
Patch status (all seven, as of 2026-10-19)
Metabase 1.58.16 / 1.59.13 / 1.60.9 / 1.61.4 (plus 0.x tags). ZITADEL 4.17.3 (or 3.4.15 on 3.x). phpMyFAQ 4.1.9. Vikunja v2.7.0. Znuny 7.3.7 (or LTS 6.5.25). Open WebUI v0.11.0. Serv-U 15.5.4 Hotfix 1 (build 15.5.4.125).
KEV status
Serv-U (CVE-2026-28318) is KEV-listed (added 2026-06-05, due 2026-06-19, active exploitation confirmed). The other six have no KEV listing and no confirmed in-the-wild exploitation as of 2026-10-19. (The inference-stack wave separately has MLflow CVE-2026-64849 on KEV; that is the inference stack, not one of the seven.)
4. The operator checklist (five items)
The checklist is the standing discipline for finding and closing a forgotten web interface. It is grounded in the homelab audit's reproducible method and reduction playbook (section 3, cross-link 8) and the per-product operator lessons. The five items follow the "which doors are open, who can reach them, what happens when a stranger knocks" mental model.
1. Inventory the default ports (know which doors are open). Enumerate every default-enabled web interface in the stack and its default port: the 3000/3456/8080/80/443 set, plus anything not in the product's default list. For each, record the default bind (Docker all-interfaces vs standard web port) and whether it was ever intended to be reachable. This is the audit's domain resolution (dig), direct port probes (nmap -Pn -sV, curl -sI), and per-service decision step. The output is a list of doors with their default state, so the operator can see which doors are open before a stranger does. The homelab audit's own open item (port 8080 answering with a Tomcat-style 400, unidentified listener) is the failure mode this item prevents: an unknown door is a door the operator cannot decide about.
2. Run the Shodan/Censys exposure check (know who can already reach them). Check the scanner databases (Shodan InternetDB, Censys, ZoomEye) for the public IP, plus certificate-transparency logs (crt.sh), BGP/subdomain enumerators, and response-header capture of every public web service. This is the audit's scanner-database and CT-log steps, and it is the exact reconnaissance the threat model uses (PP-26-3374: actors use Censys/ZoomEye scanning plus AI-generated exploit scripts and default credentials, T1596.005 + T1694). The output answers "who can reach them." If the IP and the door are already indexed by a scanner, a stranger can reach the door today, not hypothetically. The homelab audit's negative finding (Shodan does not index the IP, SSH refused externally) is the target state; a positive hit is the trigger to run items 3-5 on that door.
3. Enforce reverse-proxy default-deny (close the door at the boundary, in front of the login). Put an auth layer at the reverse proxy, in front of the login, not just on the login. Default-deny anything not explicitly allowed; suppress banners and server headers at the proxy. This is the audit's reduction playbook step 5 ("put an auth layer at the reverse proxy, in front of the login, not just on the login") and step 4 (suppress banners and server headers). The structural point: the app's own auth is the flaw surface in this pattern (ZITADEL, Znuny, phpMyFAQ), so the boundary in front of the app is where the door actually closes. A default-deny proxy means a stranger who knocks gets denied before they reach the app's flawed login.
4. Enforce auth for the operation that matters (make the lock real, not just present). For each door, verify auth is enforced for the vulnerable operation, not merely present. Concretely: audit who holds native-query rights and close the H2 file-read/write primitive (Metabase); check the auth flow is not the flaw surface (ZITADEL identify-only, Znuny HTTPBasicAuth header, phpMyFAQ WebAuthn registration); close first-boot registration (Open WebUI); ensure the trigger is not unauthenticated (Serv-U). This is the per-product operator lessons from the parent briefs, turned into a standing check. The output is a confirmation that the lock is on the door that gets knocked on, not just that a login form exists.
5. Segment the network (keep the door off the stranger's reach). Restrict the wide bind. For each service, make a per-service decision: tailnet-only, or Cloudflare Tunnel + Access. This is the audit's reduction playbook steps 1-2 (pull the UDM Pro dashboard off the public internet, tailnet-only; per-service decision: tailnet-only vs Cloudflare Tunnel + Access). The structural point: this is the missing-network-boundary layer (section 2.4). A default-on UI on an isolated LAN is a latent exposure. The same default-on UI on a wide bind is a live one. Segmentation is what keeps a door that must exist (the app is designed to be reachable) from being reachable by a stranger.
The mental model the checklist implements: every service you run ships a default door. Item 1 finds the doors. Item 2 finds which ones a stranger can already reach. Item 3 closes the door at the boundary. Item 4 makes the lock real. Item 5 keeps the door off the stranger's reach. Run it on a schedule, not once, because the default posture re-appears on every fresh install and across the category (section 1).
5. How this differs from the remote-access appliance cluster and the inference stack
All three surfaces are "a self-hosted service reachable from the network." They are structurally different in where the trust boundary sits and what the default configuration exposes. The shared underlying failure across all three is the same: running a default configuration without deciding the trust boundary. What differs is the layer where the default bites, and therefore the fix.
Pattern 1: the default-web-interface pattern (the seven products)
The flaw is in the application's own trust handling of its default UI. The network position is secondary; the flaw is in the app. The default that bites is the web UI being active by default, and the vulnerability lives in how the app handles that UI: the login/auth flow itself (ZITADEL identify-only, Znuny HTTPBasicAuth header, phpMyFAQ WebAuthn registration), a default configuration value (Vikunja CORS, Metabase H2 sample DB), an unauthenticated trigger (Serv-U deflate DoS), an open first-boot registration (Open WebUI), or an unvalidated outbound fetch (Open WebUI DNS-rebinding SSRF).
- One-line: "the default door in the app is open" (application trust-handling flaw).
- Fix: audit the app's default config and auth; patch the trust-handling flaw; restrict the bind.
Pattern 2: the remote-access appliance cluster
Purpose-built remote-access/management appliances (VPN, SD-WAN, MFT, conferencing) whose entire product is a management surface. The appliance's own remote-access function is the attack surface; the flaws are in the remote-management protocol or the default network posture (default-enabled remote management interfaces, appliance-level auth bypasses, recovery-sabotage chains). The trust boundary is the appliance's own perimeter function: the device is both the boundary and the target. This pattern is distinct from pattern 1 because the flaw is in the remote-access mechanism itself, and the exposure is the appliance's designed function (remote reach) rather than a default UI.
- One-line: "the device's own remote function is the target" (appliance perimeter flaw).
- Fix: remove the appliance's remote function from the public internet (tailnet-only, or Cloudflare Tunnel + Access).
Pattern 3: the self-hosted AI inference stack ("Six Runtimes, One Pattern")
Untrusted-input processors (model loaders, serving engines, MCP tool servers) where the trust boundary is the input itself: a model file, a webhook URL, an MCP session. The shared shape is five flaws: (1) the unauthenticated 0.0.0.0 bind plus the operator token that sits next to it (argocd-mcp 0.8.0 CVSS 10.0; vLLM KV-cache TCPStore; Ollama Probllama CVE-2024-37032 8.8; Triton CVE-2026-24207 9.8); (2) untrusted input into a deserializer or model-file parser (vLLM CVE-2025-47277 9.8; llama.cpp CVE-2026-34159 9.8 GGUF parser); (3) SSRF to internal and cloud metadata from an unvalidated fetch path (MLflow CVE-2026-64849 9.3, the only one in the wave on KEV); (4) token and credential reuse in MCP where the transport has no session auth; (5) artifact-boundary isolation failure in multi-user serving (vLLM CVE-2026-47155, dormant on a single-operator setup). The default that bites is the 0.0.0.0 bind plus the operator token in the environment. This pattern is distinct from pattern 1 because the flaw is in how the runtime treats untrusted input (deserialization, fetch, credential reuse, artifact boundary), and the exposure is a network-wide bind the runtime does not enforce. It is distinct from pattern 2 because the "appliance" is a data-plane processor an operator runs without the trust boundary the input needs, not a management surface at all.
- One-line: "the runtime trusts the input and binds wide" (input-trust + bind flaw).
- Fix: force-bind to localhost, move the proxy in front, validate the input.
The one-line distinction
Pattern 1 is "the default door in the app is open" (application trust-handling flaw). Pattern 2 is "the device's own remote function is the target" (appliance perimeter flaw). Pattern 3 is "the runtime trusts the input and binds wide" (input-trust + bind flaw). All three share the operator's underlying failure (running a default configuration without deciding the trust boundary), but the layer where the default bites is different, and the fix is different.
Why the three must stay distinct: collapsing them into one "self-hosted services are exposed" piece loses the fix. The operator's action for a default-door web app (audit the app's default config and auth) is different from the action for a remote-access appliance (pull the remote function off the public internet) and different from the action for an inference runtime (force-bind to localhost and validate the input). The pattern stays useful only if the layer is named.
6. The Glenn angle (durable mental model)
Glenn self-hosts a large stack and knows exactly how many services he has running that he has not thought about in months. The pattern is his standing condition: a web interface that ships enabled by default, that he never intended to expose, that becomes the breach or crash vector.
The durable mental model, in one line: every service you run ships a default door; the job is to know which doors are open, who can reach them, and what happens when a stranger knocks.
- "Which doors are open" is item 1 (inventory the default ports).
- "Who can reach them" is item 2 (Shodan/Censys exposure check) and item 5 (network segmentation).
- "What happens when a stranger knocks" is items 3 and 4 (reverse-proxy default-deny, auth enforcement for the operation that matters).
The angle is not "patch the seven CVEs." It is the standing discipline of knowing the doors, because the default posture is durable (section 1): the next service Glenn adds ships the same default door, and the one he forgot about three months ago is the one a stranger is already reaching. The checklist is how Glenn keeps the stack from becoming a list of doors he no longer remembers are open.
Claim status (key assertions)
| Assertion | Status | Evidence |
|---|---|---|
| All seven products ship a default-enabled web interface | Confirmed | t_260cf499 source_table.md (all rows "Confirmed") |
| Four bind all interfaces in Docker (ZITADEL 8080, Vikunja 3456, Open WebUI 3000, Metabase 3000) | Confirmed | t_260cf499 source_table.md |
| Three sit on standard web-server ports (Znuny, phpMyFAQ, Serv-U, 80/443) | Confirmed | t_260cf499 source_table.md |
| The four structural root causes (default-on UI, weak/absent auth, unscoped trust, missing network boundary) recur across all seven | Confirmed (synthesis) | Mapped per-incident in section 2 table, grounded in t_260cf499 + t_794ac52f |
| The pattern is durable and structural, not exotic | Confirmed (synthesis) | Five reasons in section 1, each grounded in a named product + parent brief |
| The pattern is distinct from the appliance cluster and the inference stack | Confirmed | t_794ac52f section C (distinctness note), carried into section 5 |
| The five-item operator checklist is grounded in the homelab audit's method + reduction playbook | Confirmed | t_794ac52f entry 8 (homelab audit method + 5-step reduction playbook) + per-product operator lessons |
| Only Serv-U (CVE-2026-28318) is KEV-listed of the seven | Confirmed | t_260cf499 source_table.md (KEV status) + t_794ac52f entry 7 |
| All seven have released fixes as of 2026-10-19 | Confirmed | t_260cf499 source_table.md (patch status) |
| The Glenn angle (every service ships a default door; know which are open, who can reach them, what happens when a stranger knocks) | Confirmed (framing) | Task brief Glenn angle + section 6, grounded in the durable-posture finding (section 1) |
Caveats (carried into the publication)
- Serv-U has no standalone Malwlab post. The 7th product's blog-level evidence is the homelab file-transfer pattern work (t_4f8537fa) plus the verified source table (t_260cf499, row 7), not a published post. This brief cites the board artifacts and the primary records they cite (vendor advisory, NVD, CISA KEV, Bishop Fox secondary), not "the Malwlab Serv-U post."
- Open WebUI post is a CMS draft as of 2026-10-19 (created 2026-09-02, header-dated 2026-08-25). It is not yet published; the "2026-09-02" date is the creation date, not a publication date.
- phpMyFAQ CVE-2026-108851 fixed status is unconfirmed (no stable 4.1.x release after 4.1.10 visible in the changelog; the 4.2.0-beta.2 MCP-server migration may resolve it, but that is a beta).
- Znuny NVD records are status Deferred (9.5/8.6 are vendor-scored, not yet NIST-confirmed); no independent third-party reproduction yet; no KEV.
- KEV status across the seven: only Serv-U (CVE-2026-28318) is KEV-listed (added 2026-06-05, due 2026-06-19, active exploitation confirmed). The other six have no KEV listing and no confirmed in-the-wild exploitation as of the access date. (The six-runtimes wave separately has MLflow CVE-2026-64849 on KEV; that is the inference stack, not one of the seven.)
- Metabase default binding nuance: the native JVM binds localhost by default, but the Docker deployment (the dominant self-hosting path) maps 3000 to all interfaces. The "default-enabled" claim is accurate for Docker deployments.
- All nine sources are AI-drafted and human-reviewed per the Malwlab transparency notes; all factual claims in each post are checked against the named public records cited in-post.
Sources
Malwlab posts (primary evidence, all 2026)
- Metabase's sample database was a file read/write back door (published 2026-10-15)
- ZITADEL's ten flaws: how a self-hosted identity provider let anyone sign in as anyone (published 2026-10-07)
- phpMyFAQ ships 28 security fixes, then publishes the receipts (published 2026-10-18)
- Vikunja's patch streak: 29 advisories, four releases, one default that bites (published 2026-10-15)
- Znuny ships a patch for a 9.5 auth bypass and a SQL injection that share one root cause (published 2026-10-16)
- Open WebUI Vulnerability Assessment: CVE-2026-54020 DNS-Rebinding SSRF via Top2 Endpoint Bypass (CMS draft, created 2026-09-02, header-dated 2026-08-25)
- SolarWinds Serv-U: covered in the homelab file-transfer exposure pattern (t_4f8537fa, 2026-10-19); no standalone Malwlab post. CVE-2026-28318 verification in t_b965b0ee.
Cross-link sources
- How Exposed Is Your Homelab? A Hands-On External Attack-Surface Audit of a Self-Hosted Stack (published 2026-09-05)
- Six Runtimes, One Pattern: The 2026 CVE Wave Across the Self-Hosted AI Inference Stack (published 2026-09-07)
Vendor advisories, NVD, CISA KEV
- Metabase: GHSA-cwxq-fmxq-jv8h (2026-07-12); NVD CVE-2026-103928 (2026-10-13)
- ZITADEL: vendor advisories (2026-07-29 through 2026-09-28); NVD CVE-2026-105211 (2026-10-04)
- phpMyFAQ: GHSA-768h-xwv2-fwp3 (2026-10-17); NVD CVE-2026-111911 (2026-10-17); changelog
- Vikunja: vendor advisories (29 advisories, bulk published 2026-10-09); NVD CVE-2026-68582 (2026-10-09); release v2.7.0
- Znuny: ZSA-2026-14, ZSA-2026-15 (2026-10-15); NVD CVE-2026-93483 (2026-10-15, status Deferred)
- Open WebUI: GHSA-h6x2-583h-x99r (2026-08-25); NVD CVE-2026-54020 (2026-08-25)
- Serv-U: vendor advisory (2026-06-04); NVD CVE-2026-28318 (2026-06-04, last modified 2026-07-22); CISA KEV (added 2026-06-05, due 2026-06-19); Bishop Fox (secondary)