October 25, 2026
A Fake reCAPTCHA Appeared on Our Admin Portal. Here’s What We Learned
A real incident from a Node.js project: how this attack works, how to respond, and how to stop it from happening to you.

By Alamgir Hossen
7 min read
I opened our admin portal to work on the bank sync page, and instead of the usual dashboard, the screen was blurred out with a reCAPTCHA box sitting right in the middle: "I'm not a robot."
That alone was strange. We had never added a CAPTCHA to that internal page. Suspicious yet curious, I clicked it. A second modal popped up titled "Verification Steps":
- Press
Windows key + Xto open the Power User menu. - Press
Ito open Windows PowerShell or Terminal. - In the window, press
Ctrl + V. - Press
Enterto finish.
It even displayed a reassuring line with a fake "reCAPTCHA Verification ID".
In that split second, my stomach dropped. No legitimate CAPTCHA will ever ask a user to open a terminal and execute arbitrary commands. This was a textbook ClickFix attack — and it was executing directly inside our own production admin panel handling sensitive financial data.
(Image Placeholder: Add a screenshot of the blurred admin portal/fake CAPTCHA here with confidential URLs and company logos blurred out./'
My immediate realization was alarming: this wasn't just a UI glitch. Somehow, unauthorized JavaScript was running inside our trusted domain.
This post walks through what this attack actually is, what to do in the critical first hour, how to trace the malicious script back to its origin, and the exact security layers that prevent it. If you maintain any Node.js or Next.js app in production, I hope this saves you from the emergency panic I experienced.
How a ClickFix Attack Works
The mechanics are deceptively simple, which is precisely why it works so effectively:
- Injection: Malicious JavaScript gets injected into a trusted web page. It overlays a blurred backdrop and displays a fake CAPTCHA visually identical to Google reCAPTCHA or Cloudflare Turnstile.
- Clipboard Hijacking: Clicking the checkbox silently copies a malicious PowerShell/Bash script directly into your system clipboard without triggering browser warnings.
- Social Engineering: The fake "verification steps" trick the user into executing the command. Pressing
Win + XthenIlaunches PowerShell.Ctrl + Vpastes the hidden payload, andEnterexecutes it. - Execution: The command fetches and executes malware — typically an InfoStealer designed to harvest browser passwords, active session tokens, crypto keys, and local environment variables.
Why is this vector so dangerous?
- Bypasses Browser Sandboxing: Browsers prevent web pages from silently installing executable files on a host system. However, operating systems allow users to execute commands freely. The attack cleverly tricks you into executing the payload.
- Exploits Muscle Memory: Developer workflows involve clicking CAPTCHAs constantly. Three quick keystrokes feel like routine verification rather than a compromise.
- Leverages Domain Trust: Internal users never suspect an admin portal hosted on their own organization's domain.
This is far from an isolated threat. Attackers have previously used SQL injections to hijack over 700 Ghost CMS sites to inject fake verification prompts, and security researchers continuously find malicious packages on npm registries leveraging CDNs like unpkg to host fake CAPTCHA lures.
Key takeaway:_ The fake CAPTCHA is merely the visual symptom. The core vulnerability is that an attacker managed to execute untrusted JavaScript in your web app context._
The First Hour: Stopping the Damage
When you discover an active exploit on production, the immediate developer impulse is to dive into code. Don't. Your first priority is containment and protecting users.
1. Alert All Active Users Immediately
Send a clear notification across your team and client channels: Do not follow any terminal or verification steps if prompted on the admin portal. A quick broadcast in Slack or Teams can prevent an immediate host compromise.
2. Identify Compromised Machines
Ask immediately if anyone followed the prompt. Anyone who executed the command must:
- Immediately disconnect their machine from the network (unplug Ethernet / turn off Wi-Fi).
- Treat the device as compromised.
- From a separate, clean device, rotate all passwords saved in the browser and revoke active auth sessions globally.
3. Take the Affected Endpoint Offline
A temporary maintenance mode is infinitely better than leaving an active trap running on production. For an admin portal, an hour of scheduled downtime costs far less than compromised session tokens or leaked database credentials.
4. Notify Stakeholders & IT Security
Inform your engineering lead, manager, or security team. If your platform processes financial or personally identifiable information (PII), regulatory or compliance reporting requirements may apply.
5. Preserve Forensics
Before deploying fixes or pulling down logs, capture evidence: take screenshots, copy raw HTML DOM dumps, preserve browser console logs, and note exact timestamps.
Tracing the Origin of the Malicious Script
Step 1: Inspect the Payload Safely
On an isolated, non-production machine, click the fake CAPTCHA box, open a plain text editor like Notepad (never a terminal/shell), and press Ctrl + V. Inspect the string to identify C2 (Command & Control) domain names, IP addresses, or remote script URLs.
Step 2: Inspect the DOM & Network Activity
Open Chrome DevTools (F12):
- Elements Tab: Search (
Ctrl + F) for<script>or<iframe>tags pointing to unknown external origin domains. - Network Tab: Filter by
JS, trigger a hard reload (Ctrl + Shift + R), and review every loaded script source. - Sources Tab: Search across bundled source code for references to
navigator.clipboardor the C2 domain identified in Step 1.
Step 3: Audit the 4 Primary Attack Vectors
- Compromised
npmDependencies: If the malicious code is compiled directly into your build bundle, a compromised third-party package is the most likely culprit. Inspect recent lockfile changes and run supply-chain audits:
git log -p --since="30 days ago" -- package-lock.json npm auditgit log -p --since="30 days ago" -- package-lock.json npm auditNote: Use enhanced supply-chain security tools like OSV-Scanner or Socket.dev, which identify malicious package behavior beyond standard CVE database matches.
2. Third-Party CDN & Tracking Scripts: Scripts loaded via Google Tag Manager, analytics, or external CDNs can be modified upstream without changes to your Git repository.
3. Stored XSS via Database Content: If your application dynamically renders rich text or HTML content from your database (e.g., using dangerouslySetInnerHTML), verify whether an attacker injected scripts into database records.
4. Compromised Build Pipeline / Hosting Infrastructure: Diff your live production assets against a fresh build generated locally from a known clean Git commit. If the compiled bundles differ, your CI/CD pipeline or deployment keys may be compromised.
Remediation and Cleanup
Simply deleting the bad script is insufficient. You must revoke compromised access points and rebuild from a verified state.
- Rebuild from Clean Source: Clean your local and CI environments, remove vulnerable dependencies, run
npm ci(to ensure strict adherence to reviewed lockfiles), and deploy fresh builds. - Rotate Application & Pipeline Secrets: Assume any environment variables present on the server or build runner were exposed. Rotate:
- Database credentials
- Third-party API keys (Stripe, OpenAI, AWS, etc.)
- CI/CD deployment keys and NPM publishing tokens
- JWT signing secrets (this instantly invalidates all existing user sessions)
3. Enforce Multi-Factor Authentication (MFA): Ensure MFA is active across GitHub, npm, and hosting provider accounts.
4. Monitor Access Logs: Keep a close eye on administrative login logs, unexpected API requests, and user privilege escalations over the following weeks.
6 Layers of Defense Against Script Injection
No single security control guarantees total protection. Defense-in-depth ensures that if one layer fails, another halts the attack.
Layer 1: Secure Your Dependencies
- Always commit
package-lock.jsonand usenpm ciin CI/CD environments instead ofnpm install. - Disable automatic post-install scripts across dependencies:
npm config set ignore-scripts truenpm config set ignore-scripts true- Enforce lockfile security scanning in pull requests.
Layer 2: Implement a Strict Content Security Policy (CSP)
A robust CSP dictates which scripts browsers are allowed to execute. In modern frameworks like Next.js, you can implement dynamic nonce-based CSP headers via proxy/middleware:
TypeScript
// proxy.ts (or middleware.ts in Next.js)
import { NextRequest, NextResponse } from "next/server";
export function proxy(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
].join("; ");
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-nonce", nonce);
requestHeaders.set("Content-Security-Policy", csp);
const response = NextResponse.next({ request: { headers: requestHeaders } });
response.headers.set("Content-Security-Policy", csp);
return response;
}// proxy.ts (or middleware.ts in Next.js)
import { NextRequest, NextResponse } from "next/server";
export function proxy(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
].join("; ");
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-nonce", nonce);
requestHeaders.set("Content-Security-Policy", csp);
const response = NextResponse.next({ request: { headers: requestHeaders } });
response.headers.set("Content-Security-Policy", csp);
return response;
}Layer 3: Subresource Integrity (SRI) for External CDN Scripts
Always include integrity hashes for third-party scripts to ensure browsers reject modified external files:
HTML
<script
src="https://cdn.example.com/library.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous">
</script><script
src="https://cdn.example.com/library.min.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous">
</script>Layer 4: Sanitize Dynamic HTML Output
Avoid using dangerouslySetInnerHTML whenever possible. If rendering raw user HTML is mandatory, pass all content through strict sanitization libraries like DOMPurify.
Layer 5: Enforce Pipeline & Environment Least Privilege
- Enable branch protection rules requiring mandatory pull request reviews before merging into production.
- Restrict deployment tokens to short-lived session lifetimes.
Layer 6: User Awareness & Operational Security
Train developers, administrators, and non-technical staff on a single core rule:
No genuine web application, CAPTCHA service, or IT administrator will ever ask you to open Terminal/PowerShell and paste executed commands.
Security Checklist for Web Applications
package-lock.jsoncommitted; CI pipelines usenpm ci.- Supply-chain scanning (
Socket/OSV-Scanner) enabled in CI. - Strict Content Security Policy (CSP) active or monitored in report-only mode.
- Subresource Integrity (SRI) hashes applied to all third-party CDN scripts.
- Input sanitization (
DOMPurify) enforced on dynamic HTML rendering. - MFA enabled across all GitHub, npm, and cloud infrastructure accounts.
- Session secrets rotated post-incident to terminate hijacked sessions.
Final Thoughts
The most eye-opening part of this incident wasn't just seeing the fake CAPTCHA — it was realizing how easily authority bias and routine friction can trick users into running untrusted code on their machines.
Security isn't a single feature you turn on; it's a series of overlapping layers. When one layer breaks, the next layer should step in to catch the fall.
Have you encountered ClickFix attacks or fake verification lures in production? Share your experiences and security strategies in the comments below!