# Root Cause Analysis — Recurring Site Failure After ~6-7 Days

**Date of audit:** 2026-08-05
**Symptom reported:** Site works after a fresh deploy, then after roughly a week the page "gets stuck" / stops working correctly. Deleting `.next` and `node_modules` and rebuilding fixes it — temporarily. The problem returns days later.

**Bottom line up front: this is very likely an active, unauthenticated backdoor on the server, not a Next.js bug.** A full-featured PHP web shell was found sitting in the project, live on the production docroot. That explains exactly the pattern described: wiping `.next`/`node_modules` clears out whatever the attacker tampered with, but does not remove the backdoor itself, so the attacker simply comes back days later and the cycle repeats.

---

## 1. Primary finding: unauthenticated PHP web shell in the project

**File:** `kamley1337.php` — present in **two locations**:
- project root: `/kamley1337.php`
- `public/kamley1337.php` (this one is served as a static asset by Next.js, i.e. reachable over HTTP at `https://topgunshootingacademy.com/kamley1337.php`)

This file is a self-contained file-manager backdoor ("Bypass Manager Pro"). With **no authentication of any kind**, it lets whoever requests the URL:
- browse the entire filesystem the web server user can reach (`$_GET['p']`)
- create, rename, delete, `chmod` any file or folder
- read and overwrite the contents of any file (`edit` / `save`)
- upload a file directly (multipart upload)
- **download and save arbitrary remote files onto the server via cURL** (`upload_url` action) — i.e. a one-request "fetch and drop any payload" primitive
- suppresses PHP errors (`error_reporting(0)`) so its activity doesn't show up in logs the way a crashing script would

This is not obfuscated malware needing analysis — it is a plainly-labeled, ready-made admin panel for an attacker, written in Indonesian-language variable comments/strings, a very common signature of mass web-shell campaigns that scan for exposed CMS/PHP boxes and drop this kind of tool as persistent, self-service access.

### Why this explains the exact symptom pattern
1. Site is freshly deployed → works fine.
2. At some point the attacker uses the shell (or another access path — see §4) to tamper with the running server: modify files inside `.next/` build output, drop/alter files in `node_modules`, change static assets, add scheduled tasks, etc. This degrades or breaks the site ("page stuck").
3. You run the standard fix: delete `.next` and `node_modules`, rebuild from source. This wipes out whatever the attacker planted **inside those two directories** — so the site works again.
4. The web shell itself lives in the **project root and `public/`**, neither of which your cleanup step touches. It survives every rebuild.
5. Days later, the attacker uses the still-present shell to re-tamper, and the symptom returns.

This is a self-reinforcing loop: every "fix" looks successful because it clears the visible symptom, while the actual cause is never removed.

---

## 2. How is a `.php` file even being executed on a Next.js/Node site?

The two Apache vhost files found in the project (`topgunshootingacademy.com.conf`, `topgunshootingacademy.com-le-ssl.conf`) both do:

```
DocumentRoot /var/www/html/topgun
ProxyPass / http://localhost:3046/
ProxyPassReverse / http://localhost:3046/
```

On the face of it, *every* request should be proxied straight to the Node process on port 3046 — a request for `/kamley1337.php` shouldn't reach PHP at all through this config alone. Two explanations are possible, and only access to the live server can tell us which:

- **(a) There is PHP execution configured somewhere else that overlaps this docroot** — e.g. a global `mod_php`/PHP-FPM handler enabled server-wide (common on boxes managed via cPanel/EasyApache or shared hosting panels — note the `ServerAdmin webmaster@tezcommerce.com` in the vhosts, suggesting a hosting/agency-managed box), a second vhost/alias pointing at the same `DocumentRoot`, or a rewrite rule elsewhere (outside these two files) that serves existing on-disk files before falling through to `ProxyPass`. This is the most common real-world cause of "Node app on Apache proxy, yet a random `.php` file executes" reports.
- **(b) The attacker never needed HTTP access to the shell to make it useful.** They may have gotten in through another route entirely (compromised SSH/FTP/cPanel credentials, a vulnerable admin/CMS login, a leaked secret — see §3, or a vulnerable dependency), and simply **dropped this file as a convenience/persistence tool** for future file access via SCP/SFTP/File Manager rather than via a browser. Either way, its presence on disk proves the attacker already has (or had) write access to the server.

**This needs to be confirmed directly on the live server** — see the action list in `FIX_TASKS.md`.

---

## 3. Contributing / secondary issues found

These don't by themselves explain the specific "6-7 days then stuck" cycle, but they are real weaknesses that either widen the attack surface, could independently cause visitor-facing breakage, or will make recovery harder. Full detail and remediation steps are in `FIX_TASKS.md`.

### 3.1 Unsanitized CMS content rendered as raw HTML (stored XSS surface)
`isomorphic-dompurify` is listed in `package.json` dependencies but **is never imported or used anywhere in the codebase**. Meanwhile, at least these components inject CMS-provided HTML directly into the DOM with **no sanitization**:
- `components/about/CoachingTeamSection.tsx`
- `components/about/FounderSection.tsx`
- `components/about/TopgunAboutSection.tsx`
- `components/branches/BranchesSection.tsx`

All use `dangerouslySetInnerHTML={{ __html: <field from CMS API> }}` directly. If the headless CMS (`wip.tezcommerce.com`) or any content-editor account is ever compromised, phished, or has a stored-XSS bug of its own, arbitrary JavaScript can be injected into every page view. This is a plausible **independent** explanation for visitor-facing "page freezes / behaves oddly" reports (e.g. injected redirect loops, cryptomining scripts, or intentionally broken markup), separate from the server-side compromise in §1 — and it is also a channel an attacker who already controls the CMS credentials could use without ever touching the server filesystem.

### 3.2 CMS API secret duplicated into a `NEXT_PUBLIC_` variable
`.env` defines **both**:
```
CMS_API_SECRET=...
NEXT_PUBLIC_CMS_API_SECRET=...   (same secret, public-prefixed)
```
Anything prefixed `NEXT_PUBLIC_` is inlined into the client-side JS bundle by Next.js at build time. It's not currently read from a client component, but `services/fetchData.service.js` and `services/fetchFormFields.js` both fall back to it (`process.env.CMS_API_SECRET || process.env.NEXT_PUBLIC_CMS_API_SECRET`), and `fetchData.service.js` — unlike its sibling service files — has **no `"use server"` / `server-only` guard**, so it's one accidental import (e.g. from a `"use client"` component) away from shipping the HMAC signing secret to every visitor's browser, which would let anyone forge valid requests to the CMS API.

### 3.3 No version control
There is no `.git` directory anywhere in the project. There is no way to:
- diff the current server state against a known-good baseline to see exactly what the attacker changed or added,
- confirm whether `kamley1337.php` (or anything else) was ever part of a legitimate commit,
- roll back cleanly to a trusted state.
This also means the "delete `.next`/`node_modules`, rebuild" recovery step is the *only* recovery tool available — there's no way to verify the rest of the source tree (`app/`, `components/`, `public/`, `services/`) is itself clean.

### 3.4 Dependency/version hygiene
- `"crypto": "^1.0.1"` is listed as a dependency. This is the officially deprecated npm placeholder package (empty stub pointing back to Node's built-in `crypto` module). It's not itself malicious, but it's dead weight and a common object of typosquatting confusion — Node's built-in module takes priority so `import crypto from "crypto"` in `services/*.js` still resolves to the real thing today, but this is bundler/runtime-dependent and worth cleaning up rather than relying on.
- `next: ^16.1.6` (very recent major version) alongside `eslint-config-next: 15.5.4` — a version mismatch that means lint rules lag behind the actual framework version in use.

### 3.5 Housekeeping / evidence of a rushed or unmonitored deployment
- `public/images/sh.f48a1a04fe8dbf021b4cda1d.html` — a leftover AddThis widget iframe file from the original purchased HTML template (dexignlab "SportsZone" template). Unused, harmless, but indicates the template was dropped in wholesale without cleanup, which is generally how extra risk surface (like unused plugin endpoints) sneaks in.
- `IPtablesbackup.txt` is present but **empty (0 bytes)** in this copy. Worth checking the real file on the live server — if it once held actual firewall rules, it's a sign someone was already investigating/adjusting firewall config around a prior incident.
- `app/layout.tsx` still has the default `create-next-app` metadata (`title: "Create Next App"`), and the README is entirely unmodified boilerplate — there is no documented deployment or operations process for this project at all.

---

## 4. Most likely overall narrative

1. At some point (timeline unknown from this local copy — see `FIX_TASKS.md` for how to establish it from server logs), an attacker gained write access to the server — either through a hosting/panel-level compromise, a leaked credential, or exploitation of the reverse-proxy/PHP overlap described in §2.
2. They dropped `kamley1337.php` in both the project root and `public/` as a persistent, self-service backdoor.
3. Periodically (roughly matching the ~6-7 day cadence reported) they use it — or an equivalent access path — to modify server state in a way that breaks the site for visitors.
4. `rm -rf .next node_modules && rebuild` clears the symptom because it happens to blow away whatever was tampered with in those directories, but leaves the actual backdoor untouched in the root/`public/`.
5. The cycle repeats until the backdoor itself is removed and the original entry point is closed.

This narrative fits the evidence well, but items 1 and 2 (exact entry point, exact timeline, whether other backdoors exist elsewhere on the server) **cannot be confirmed from a local project checkout** — they require direct forensic access to the live server (logs, crontab, running processes, SSH keys). See `FIX_TASKS.md` §"Forensics — do this first" for the specific commands.
