| CVE | Vulnerability name | Date | Responsible Security Disclosure by | Vulnerabilities |
|---|---|---|---|---|
|
GHSA-66m2-4wfr-c45p
|
DnsBleed |
2026-07-01 |
4n207 (coordinated disclosure) and xet7 (fix)
![]() Did send detailed report with PoC! |
|
Wekan's outgoing-webhook (board Integrations) URLs are validated by a synchronous validator in
models/integrations.js that blocks private / loopback / link-local addresses by regex-matching the
URL hostname string — it never resolves DNS. An attacker can therefore supply a public hostname that
resolves to a blocked address and slip past the blocklist:
169-254-169-254.nip.io → 169.254.169.254 (cloud instance metadata / IMDS)127-0-0-1.nip.io → 127.0.0.1 (loopback)
nip.io / sslip.io are public wildcard-DNS services that require no attacker
infrastructure. The report's core point is correct and is the reason this class of bug keeps recurring:
matching the hostname string is not an SSRF boundary, because the string can't see the IP the name
actually resolves to.
The advisory quotes the models/integrations.js string blocklist and states that the webhook send
path does not re-validate the resolved IP. That was true before, but the delivery path had already been fixed:
fetchSafe
(server/lib/ssrfGuard.js, added in the IntegrationBleed
RebindBleed fix), which resolves the hostname, validates the resolved IP, pins the TCP connection to it
(closing DNS-rebinding) and blocks redirects. 169-254-169-254.nip.io resolves to
169.254.169.254 and is rejected before any request is sent.
POST / PUT /api/boards/:boardId/integrations
were hardened in WebhookBleed to run the DNS-aware
validateAttachmentUrl(), which resolves the hostname and rejects any blocked resolved IP.
This release closes the remaining gap and, more importantly, the drift risk the advisory highlights. The
delivery guard previously resolved only IPv4 A-records (dns.resolve4), leaving it blind
to AAAA (an IPv6-only internal target was merely fail-closed, and legitimate IPv6 webhooks were
unreachable) and kept a second, less-complete private-range block-list that could drift out of sync with
the input-time one.
fetchSafe now resolves both address families via dns.lookup({ all: true }) —
the same resolver call the input-time validator uses — and validates every resolved address.
isIpBlocked in models/lib/attachmentUrlValidation.js, so the input
and delivery layers can no longer disagree about what is private.
url validator is documented in code as a non-authoritative first-line UI check
only, so the regex is never again mistaken for the SSRF boundary.
| Timeline | Details |
|---|---|
| 2026-07-01 | Reported by 4n207 (GHSA-66m2-4wfr-c45p) and fixed at the upcoming WeKan release. |