Skip to content

Ech0 has Unauthenticated Server-Side Request Forgery in Website Preview Feature

High severity GitHub Reviewed Published Apr 1, 2026 in lin-snow/Ech0 • Updated Apr 3, 2026

Package

gomod github.com/lin-snow/ech0 (Go)

Affected versions

< 1.4.8-0.20260401031029-4ca56fea5ba4

Patched versions

1.4.8-0.20260401031029-4ca56fea5ba4

Description

Summary

Ech0 implements link preview (editor fetches a page title) through GET /api/website/title. That is legitimate product behavior, but the implementation is unsafe: the route is unauthenticated, accepts a fully attacker-controlled URL, performs a server-side GET, reads the entire response body into memory (io.ReadAll). There is no host allowlist, no SSRF filter, and InsecureSkipVerify: true on the outbound client.

Attacker outcome : Anyone who can reach the instance can force the Ech0 server to open HTTP/HTTPS URLs of their choice as seen from the server’s network position (Docker bridge, VPC, localhost from the process view).
Go’s default http.Client follows redirects (unless disabled). Redirect chains can move the server-side request from an allowed-looking host to an internal target; the code does not disable this in SendRequest.

Affected Components

Ech0 codebase:

  • internal/handler/common/common.go
    Handles the /api/website/title endpoint and accepts user-controlled URL input.

  • internal/service/common/common.go
    Processes the request and invokes the outbound HTTP fetch (GetWebsiteTitle).

  • internal/util/http/http.go
    Performs the HTTP request (SendRequest) with the following insecure configurations:

    • No URL validation or allowlist
    • Redirects enabled (default client behavior)
    • InsecureSkipVerify: true

PoC

Environment: Ech0 listening on http://127.0.0.1:6277 (e.g. Docker image sn0wl1n/ech0:latest). No cookies or Authorization header.

Step 1 — baseline: unauthenticated server-side fetch (public URL):

curl.exe -sS -m 20 "http://127.0.0.1:6277/api/website/title?website_url=https://example.com"

Observed result (verified): HTTP 200, JSON with code: 1 and data Example Domain — proves the Ech0 process performed an outbound GET without any client auth.

Step 2 — impact: host-bound page + recorded leak (repo PoC file)
Committed PoC page: poc_ssrf_proof.html

  1. From poc file directory, listen on 0.0.0.0 (port 9999):
python -m http.server 9999 --bind 0.0.0.0
  1. Docker Desktop (Windows / macOS): Ech0 in Docker fetches the host via host.docker.internal:
curl.exe -sS -m 20 "http://127.0.0.1:6277/api/website/title?website_url=http://host.docker.internal:9999/poc_ssrf_proof.html"

Recorded response (verified this workspace, Ech0 4.2.2 in Docker):

{"code":1,"msg":"获取网站标题成功","data":"ECH0_SSRF_POC_LEAK_2026"}

Python server log: GET /poc_ssrf_proof.html200 (proves the server/container pulled the page from your host).

Leak channel: the backend reads the full HTML body before parsing (see io.ReadAll in SendRequest).

Impact

  • Verified: Unauthenticated callers can make the Ech0 process issue server-side HTTP(S) requests to internal/reserved targets reachable from that process (PoC Step 2: host-reachable listener reflected in JSON).
  • Code-level: The full response is read into memory (io.ReadAll); only the title string is returned. Combined with default HTTP redirect following (standard http.Client behavior; not disabled here), the effective request graph is larger than a single URL.
  • TLS: InsecureSkipVerify: true means misissued or intercepted TLS to internal HTTPS services is still accepted from the server’s perspective.
  • Deployment-dependent: Where routing allows (typical cloud VMs), 169.254.169.254-class endpoints are in scope for the same code path; treat as *high.
  • DOS(Denial of Service): reading the whole body into memory with io.ReadAll is a DoS vector if you point it at a massive file.

Remediation

  • Enforce SSRF-safe URL policy: allow only needed schemes/hosts; block link-local, metadata, and loopback unless explicitly required.
  • Remove InsecureSkipVerify; use normal TLS verification.
  • Limit redirects (disable or cap hops; re-validate each target).
  • Add response size / timeout limits; optionally restrict egress at the network layer.

References

@lin-snow lin-snow published to lin-snow/Ech0 Apr 1, 2026
Published to the GitHub Advisory Database Apr 3, 2026
Reviewed Apr 3, 2026
Last updated Apr 3, 2026

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

EPSS score

Weaknesses

Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination. Learn more on MITRE.

CVE ID

CVE-2026-35036

GHSA ID

GHSA-wc4h-2348-jc3p

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.