Skip to main content
CVE-2026-77463VulnerabilityPublished

github.com/umputun/remark42/backend · Remark42: Rate limit bypass via unconditional trust of X-Forwarded-For/X-Real-IP headers in direct-internet deployment

Affects github.com/umputun/remark42/backendSource GCVE Database

0
Compare
Authoritative record:CVE-2026-77463· Medium 6.5

Not known to be exploited · CVSS 6.5 Medium : upgrade to 1.16.2 when convenient.

Data as of 9 Nov 2026, 23:24 UTC

CVSS 3.1 base score
6.5Medium
How severe the flaw is on the 0–10 CVSS scale.

Thin evidence

One scorer or none
CVSS scores by source
CNA (GitHub_M)CVSS 3.16.5
EPSS
0.2% chance of exploitation in 30 days – higher than 12% of CVEsExploit Prediction Scoring System: the likelihood attackers will use this flaw soon.
Patch
Available – fixed in 1.16.2
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 5 Nov 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponized: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 9 Nov 2026.

What to do

Upgrade github.com/umputun/remark42/backend to 1.16.2.

Affected software

ProductAffectedFixed in
Gogithub.com/umputun/remark42/backendAffected
  • >=1.0.0 <1.16.2
Fixed in
  • 1.16.2
Gogithub.com/umputun/remark42Affected
  • >=1.0.0 <=1.2.0
Fixed in–

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

Description

### Summary Remark42 applied chi's `middleware.RealIP` globally, trusting `True-Client-IP` / `X-Real-IP` / `X-Forwarded-For` headers without verifying the request came from a trusted proxy. The per-IP rate limiters key on the overwritten `r.RemoteAddr`, so any client can bypass every per-IP rate limiter by rotating spoofed IP headers. The same spoofed IP also drives other per-IP controls (stored comment IP, anonymous identity derivation, and `--votes-ip` vote de-duplication). This affects deployments reachable directly from the internet (Remark42's built-in HTTP, static TLS, and ACME modes are all documented and supported), as well as any proxied deployment where the proxy forwards an attacker-controlled recognised header. ### Details The router applies `middleware.RealIP` globally, ahead of the per-route limiters, without any check that the server is behind a trusted proxy (in v1.16.1, `backend/app/rest/api/rest.go:207`). The `rateLimiter` function (v1.16.1, `backend/app/rest/api/rest.go:769-779`) builds a tollbooth limiter with `IPLookup{Name: "RemoteAddr"}`, which reads `r.RemoteAddr`. Since chi's `middleware.RealIP` (v1.16.1, `backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go:42-55`) unconditionally overwrites `r.RemoteAddr` from `True-Client-IP > X-Real-IP > first X-Forwarded-For` (accepting any syntactically valid IP), any client can set its apparent IP to an arbitrary value. This bypasses every per-IP rate limiter that runs after `RealIP`: `/auth` (2/s), avatars (100/s), the image proxy (10/s), open / protected / admin route groups (10/s), root routes (50/s), and `/web/*` (20/s). Comment and vote creation go through the update limiter, whose server default is `UPDATE_LIMIT=0.5`/s (burst 1), not 10/s. The global 1,000 concurrent-request throttle still applies, so this is a per-IP-limit bypass rather than truly unlimited throughput. chi's own documentation warns that `middleware.RealIP` should be used only behind a trusted proxy. ### PoC ```bash # Deploy Remark42 reachable directly from the internet (built-in HTTP / TLS / ACME, no proxy that overwrites the headers). # Comment creation is limited to UPDATE_LIMIT (default 0.5/s, burst 1) per IP. # Rotate X-Real-IP across many requests; each distinct value gets its own token bucket. for i in $(seq 1 100); do curl -s -o /dev/null -w '%{http_code}\n' \ -H "X-Real-IP: 192.0.2.$i" \ -X POST 'https://remark42.example.com/api/v1/comment?site=site1' \ -H 'Authorization: Bearer YOUR_JWT' \ -H 'Content-Type: application/json' \ -d '{"text":"spam","locator":{"site":"site1","url":"https://example.com/post"}}' done | sort | uniq -c # With rotating headers the responses stay 201; without them, requests beyond the burst return 429. ``` ### Impact By rotating the spoofed client IP an attacker bypasses the per-IP token buckets on all rate-limited endpoints and defeats other per-IP controls, enabling: (a) spam comment creation beyond the configured per-IP limit, (b) confirmation-email / notification flooding to supplied addresses, (c) high-volume outbound image fetching and resource consumption via the image proxy (its transport still blocks private/reserved destinations and DNS rebinding, so this is amplification, not new SSRF reach), (d) authentication-endpoint abuse, (e) where `--votes-ip` is enabled, defeating per-IP vote de-duplication (ballot stuffing), and (f) minting fresh anonymous identities and poisoning the stored comment IP and request logs, since the anonymous user ID is derived from `name + RemoteAddr`. The global concurrency ceiling and per-request timeouts remain, so availability impact is limited. ### Resolution Mitigation support was added in v1.16.2 (umputun/remark42#2116, follow-up in #2118). **Upgrading alone does not remediate the issue: `--trusted-proxy` must be configured.** The fix adds `--trusted-proxy` / `TRUSTED_PROXY`. When set, the forwarding headers (`X-Real-IP`, then `CF-Connecting-IP`, then the leftmost public `X-Forwarded-For`) are honoured only when the direct TCP peer falls inside a trusted-proxy network; for a request from any other peer those headers are dropped and the real socket IP is used. When it is **unset**, the previous trust-all behaviour is preserved for backward compatibility and a startup warning is logged, so an un-configured v1.16.2 remains exploitable. `True-Client-IP` is no longer recognised. `TRUSTED_PROXY` takes a comma-separated list; on the command line, repeat `--trusted-proxy` per entry. **Required configuration:** - **Reachable directly from the internet:** set a value that no real client matches so the forwarding headers are always ignored, e.g. `--trusted-proxy=127.0.0.1/32` (only when Remark42 is not actually reached over loopback). - **Behind a reverse proxy:** point `--trusted-proxy` at the proxy's network only, block direct access to Remark42, and make the proxy **overwrite** `X-Real-IP` with the real client and discard client-supplied forwarding headers. A proxy

References

Community reviews

Loading reviews…