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.
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:
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.
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.
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'))"
Secret Requests tab and create a new request7.3.3Exploitability
AV:NAC:LPR:LUI:NScope
S:UImpact
C:LI:NA:NCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N