Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦
📦 npm
Not in CISA KEV
MEDIUM severity

CVE-2025-69206 hemmelig

MEDIUMFix: HemmeligOrg/Hemmelig.app@6c909e5

CVE-2025-69206 is a medium-severity (CVSS 4.3) Server-Side Request Forgery (SSRF) vulnerability in hemmelig. A fix is available for hemmelig — see the affected versions and patch details below.

Hemmelig has SSRF Filter bypass in Secret Request functionality

Also known asGHSA-vvxf-wj5w-6gj5
Published
Dec 29, 2025
Updated
Aug 12, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 22, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for CVE-2025-69206.

EPSS Exploitation Probability

via FIRST.org ↗
0.2%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs11th percentile — riskier than 11% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

CVE-2025-69206 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,636 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.

0other npm packages depend on this — each one inherits the vulnerability until it's patched upstream
hemmelignpm
15downloads / week

Description

Summary

A Server-Side Request Forgery (SSRF) filter bypass vulnerability exists in the webhook URL validation of the Secret Requests feature. The application attempts to block internal/private IP addresses but can be bypassed using DNS rebinding (e.g., localtest.me which resolves to 127.0.0.1) or open redirect services (e.g., httpbin.org/redirect-to). This allows an authenticated user to make the server initiate HTTP requests to internal network resources.

Details

The vulnerability exists in the isPublicUrl function located in /api/lib/utils.ts. The function validates webhook URLs against a blocklist of private IP patterns:

export const isPublicUrl = (url: string): boolean => {
    const parsed = new URL(url);
    const hostname = parsed.hostname.toLowerCase();
    
    const blockedPatterns = [
        /^localhost$/,
        /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
        /^192\.168\.\d{1,3}\.\d{1,3}$/,
        // ... other patterns
    ];
    
    return !blockedPatterns.some((pattern) => pattern.test(hostname));
};

The validation is flawed because:

  1. DNS Rebinding Bypass: It only checks the hostname string, not the resolved IP address. Domains like localtest.me pass validation (not matching any blocked pattern) but resolve to 127.0.0.1.

  2. Open Redirect Bypass: External URLs like httpbin.org/redirect-to?url=http://127.0.0.1 pass validation since httpbin.org is a public domain. When the server follows the redirect, it connects to the internal address.

PoC

Optional: On the container that runs Hemmelig application, host a temporary port with the following command:

node -e "require('http').createServer((req,res)=>{console.log(req.method,req.url,req.headers);res.end('ok')}).listen(8080,()=>console.log('Listening on 8080'))"
  1. Log in as an user
  2. Switch to Secret Requests tab and create a new request
  3. When inside the request dialog, there are 2 possible payloads that can be used on the Webhook URL input to bypass SSRF
1. Using domain redirect: http://localtest.me:PORT
2. Using httpbin to perform a redirect: httpbin.org/redirect-to?url=http://127.0.0.1:PORT
  1. Open a new browser/tab and confirm the request by creating a secret. Upon clicking save, the port we hosted we receive a request. <img width="795" height="310" alt="image" src="https://github.com/user-attachments/assets/95d559e5-ead2-4b5d-8e53-9ddec3416953" />

Otherwise, if the port doesn't exist, a similar error in the logs can be found:

Secret request webhook delivery failed after retries: TypeError: fetch failed
    at node:internal/deps/undici/undici:15845:13
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
    at async sendSecretRequestWebhook (/app/api/routes/secret-requests.ts:58:34) {
  [cause]: Error: connect ECONNREFUSED 127.0.0.1:80
      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1637:16) {
    errno: -111,
    code: 'ECONNREFUSED',
    syscall: 'connect',
    address: '127.0.0.1',
    port: 80
  }
}

Impact

While the SSRF filter can be bypassed, the practical impact is limited because this is a Blind SSRF, there is no response reflected. But with certain technique like response-timing, the attackers can still indicate whether or not a port is opened.

Remediation

Replace hostname-based validation with IP resolution checking:

import { isIP } from 'is-ip';
import dns from 'dns/promises';

export const isPublicUrl = async (url: string): Promise<boolean> => {
    const parsed = new URL(url);
    const hostname = parsed.hostname;
    
    // Resolve hostname to IP
    let addresses: string[];
    try {
        if (isIP(hostname)) {
            addresses = [hostname];
        } else {
            addresses = await dns.resolve4(hostname).catch(() => []);
            const ipv6 = await dns.resolve6(hostname).catch(() => []);
            addresses = [...addresses, ...ipv6];
        }
    } catch {
        return false;
    }
    
    // Check resolved IPs against blocklist
    const privateRanges = [
        /^127\./,
        /^10\./,
        /^192\.168\./,
        /^172\.(1[6-9]|2\d|3[0-1])\./,
        /^169\.254\./,
        /^::1$/,
        /^fe80:/i,
        /^fc00:/i,
        /^fd/i,
    ];
    
    return addresses.length > 0 && !addresses.some(ip => 
        privateRanges.some(pattern => pattern.test(ip))
    );
};

Additionally, disable following redirects in the webhook fetch call or re-validate the URL after each redirect.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
📦npmhemmeligall versions7.3.3npm install hemmelig@7.3.3

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for hemmelig, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update hemmelig to 7.3.3 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2025-69206 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2025-69206 can be triaged on real exposure rather than presence alone.

Tailored to CVE-2025-69206. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Summary A Server-Side Request Forgery (SSRF) filter bypass vulnerability exists in the webhook URL validation of the Secret Requests feature. The application attempts to block internal/private IP addresses but can be bypassed using DNS rebinding (e.g., `localtest.me` which resolves to `127.0.0.1`) or open redirect services (e.g., `httpbin.org/redirect-to`). This allows an authenticated user to make the server initiate HTTP requests to internal network resources. ### Details The vulnerability exists in the `isPublicUrl` function located in `/api/lib/utils.ts`. The function validates webhoo
O3 Security · Impact-Aware SCA

Is CVE-2025-69206 in your dependencies?

O3 Security finds CVE-2025-69206 across npm dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

CVE-2025-69206: hemmelig (Medium 4.3) | O3 Security