# WordPress Production Security Investigation — READ-ONLY Checklist

**Scope.** The customer-facing WordPress/WooCommerce site is a **separate system** from
the NH POS backend (this repo). NH POS only exchanges signed, tenant-scoped webhooks and
connection-authenticated reads with it — no NH POS code runs on the WordPress host. This
checklist is a **read-only forensic triage** to be run by an operator with production
access; it is NOT part of the NH POS codebase and cannot be executed from here.

## Rules of engagement (MANDATORY)

- **READ-ONLY.** Do not edit, move, delete, quarantine, or "clean" any file, user, row,
  option, or cron entry during investigation. Collect evidence first.
- **Preserve evidence.** Take a full filesystem + database snapshot (and copies of access/
  error logs) BEFORE anything else, to immutable/off-host storage. Record timestamps and
  checksums.
- **No remediation without written approval.** Containment (taking the site offline,
  rotating credentials, removing a plugin/user) is a separate, approved step — see
  "Containment (only after approval)".
- **Assume compromise, prove cleanliness.** Do **not** declare the site clean without
  positive production evidence (core checksums match, no unknown admins, no unexpected
  cron, no unexplained file changes, logs consistent).
- Work from a **copy** where feasible (restore the snapshot into an isolated environment)
  so investigation cannot alter production state.

## 0. Evidence capture (do this first)

- [ ] Full file snapshot of the web root (e.g. `tar`/`rsync` to off-host, read-only).
- [ ] Full DB dump (`mysqldump --single-transaction`) to off-host storage.
- [ ] Copy web-server access + error logs, PHP-FPM logs, and any WAF logs for the window
      of interest.
- [ ] Record `wp core version`, PHP version, active theme, and the full plugin list with
      versions (`wp plugin list`, `wp theme list`) — via WP-CLI **read** commands only.

## 1. Malicious `lucid-relay-jet` plugin

- [ ] Confirm presence: `wp plugin list --fields=name,status,version` and
      `ls -la wp-content/plugins/lucid-relay-jet/` (does it exist? active?).
- [ ] It is **not** a known/legitimate plugin — treat as malicious until proven otherwise.
      Do NOT delete yet; capture the full directory to evidence storage.
- [ ] Inspect its PHP for backdoor markers (read-only `grep -rinE`):
      `eval\(|base64_decode|gzinflate|str_rot13|assert\(|create_function|preg_replace.*/e|system\(|exec\(|shell_exec|passthru|\$_(GET|POST|REQUEST|COOKIE)\s*\[|file_put_contents|fwrite|curl_exec|wp_remote_(get|post)|add_action\(\s*['"]init|register_rest_route|update_option\(`.
- [ ] Look for data-exfil / "relay" behaviour (the name implies forwarding): outbound
      HTTP to unknown hosts, hooks on checkout/payment/order/user events, REST routes it
      registers, and any scheduled events it adds.
- [ ] Search the whole tree for sibling implants dropped by it:
      `grep -rinE "eval\(|base64_decode|gzinflate" wp-content/ wp-includes/ wp-admin/`
      and list files by mtime around the plugin's install time.
- [ ] Check `wp_options` for options it created (`SELECT option_name,length(option_value)
      FROM wp_options WHERE option_name LIKE '%relay%' OR option_name LIKE '%lucid%';`).

## 2. Unauthorized administrator accounts

- [ ] List all admins:
      `SELECT u.ID,u.user_login,u.user_email,u.user_registered FROM wp_users u
       JOIN wp_usermeta m ON m.user_id=u.ID
       WHERE m.meta_key='wp_capabilities' AND m.meta_value LIKE '%administrator%';`
      (adjust the `wp_` table prefix). Cross-check every row against the known operator list.
- [ ] Flag accounts registered during/after the suspected intrusion window, with
      disposable-domain emails, or logins like `admin`, `wpuser`, `support`, random strings.
- [ ] Check for **privilege escalation** hidden in usermeta (capabilities set directly) and
      for users with no email / no registered date.
- [ ] Check `wp_users` for accounts created via SQL (no corresponding registration log entry).
- [ ] Review application passwords / REST API keys (`wp_usermeta` `_application_passwords`).

## 3. Injected WP-Cron hooks

- [ ] `wp cron event list --fields=hook,next_run,recurrence` (read-only). Compare against
      the expected set for core + known plugins/WooCommerce.
- [ ] Inspect the raw schedule: `SELECT option_value FROM wp_options WHERE
      option_name='cron';` — decode and look for unknown hook names, especially ones that
      fire `eval`/remote fetches or re-create admin users / re-install a plugin.
- [ ] Check whether real cron calls `wp-cron.php` or if `DISABLE_WP_CRON` is set; verify no
      rogue system crontab entries (`crontab -l`, `/etc/cron*`) hitting the web root.

## 4. Suspicious core / theme / plugin modifications

- [ ] **Core integrity** (authoritative): `wp core verify-checksums`. Any "does not match"
      or unexpected file is a strong compromise signal.
- [ ] **Plugin/theme integrity**: `wp plugin verify-checksums --all` (for plugins on
      wordpress.org). Note which plugins/themes are NOT verifiable (premium/custom) and
      review those by hand.
- [ ] Find recently-modified PHP (read-only): `find wp-content wp-includes wp-admin -name
      "*.php" -mtime -30 -printf "%TY-%Tm-%Td %p\n" | sort` and justify every entry.
- [ ] Classic implant locations: `wp-content/uploads/**/*.php` (uploads should contain NO
      PHP), `mu-plugins/`, modified `wp-config.php`, `.htaccess`, `index.php`,
      `wp-load.php`, `wp-settings.php`, and fake files like `wp-vcd.php`, `class.theme-
      modules.php`, `wp-admin.php`.
- [ ] Scan for obfuscation across the tree (same regex set as §1) and for `auto_prepend_file`
      / `auto_append_file` in `php.ini`/`.user.ini`.
- [ ] If available, run an external scanner against the **snapshot copy** (not production):
      e.g. a malware/YARA pass. Treat results as leads, not verdicts.

## 5. Exposed SQL backups / sensitive files

- [ ] Search web-accessible paths for dumps and archives: `find . -maxdepth 3 -regextype
      posix-extended -iregex ".*\.(sql|sql\.gz|zip|tar\.gz|bak|old|swp)$"` and anything
      under the docroot that a browser could fetch.
- [ ] Verify none are reachable over HTTP (e.g. `/backup.sql`, `/wp-content/backup*`,
      `/db.sql`, `/*.zip`) — check from an external client, read-only.
- [ ] Check for exposed `wp-config.php` backups (`wp-config.php.bak`, `wp-config.save`,
      `.wp-config.php.swp`) and VCS/debug leftovers (`.git/`, `.env`, `debug.log` with
      secrets).
- [ ] Confirm `WP_DEBUG`/`WP_DEBUG_LOG` are off in production and that `debug.log` is not
      web-served.

## 6. Credentials requiring rotation (inventory now; rotate only after approval)

Assume any credential reachable from the WordPress host is compromised. Inventory:

- [ ] DB credentials in `wp-config.php` (`DB_USER`/`DB_PASSWORD`).
- [ ] WordPress salts/keys (`AUTH_KEY` … `NONCE_SALT`).
- [ ] All admin + application passwords; any SMTP, S3/backup, CDN, and API keys stored in
      options or plugin settings.
- [ ] **Payment / NH POS integration secrets**: the storefront-connection service token
      and any webhook secret used with NH POS. If the WP host is compromised, rotate the
      NH POS-side connection token and payment-gateway `webhook_secret` for its location —
      NH POS keeps these encrypted + write-only (see PaymentGatewayConfig), so rotation is
      a config write, not a code change.
- [ ] Hosting/control-panel, SSH, and SFTP credentials.

## 7. WordPress core integrity & environment

- [ ] `wp core verify-checksums` (again, as the definitive integrity check) + confirm the
      running version is current and supported.
- [ ] Review `wp-config.php` for injected constants (`auto_prepend_file`, unexpected
      `WP_CONTENT_URL`, remote `WP_SITEURL`/`home`), and confirm file perms (no 777, no
      world-writable PHP).
- [ ] Check `wp_options` `siteurl`/`home` for unexpected values (redirect hijack).
- [ ] Review user roles/capabilities for tampering (`wp_user_roles` option).
- [ ] Correlate access logs for admin logins, `xmlrpc.php` abuse, and POSTs to the
      plugin/upload paths around the intrusion window.

## Containment (ONLY after written approval — not part of investigation)

In priority order, once evidence is preserved and approval granted:
1. Rotate every credential in §6 (and the NH POS connection token + gateway webhook secret).
2. Remove `lucid-relay-jet` and any confirmed implants; restore core/plugins/themes from
   known-good checksummed sources.
3. Delete unauthorized admin users; force a global password reset; invalidate sessions and
   application passwords.
4. Purge rogue cron entries.
5. Patch the entry vector (vulnerable/nulled plugin, weak admin password, exposed backup),
   then re-scan from a clean snapshot.

## Reporting

Produce a findings report with, per item: evidence (paths, checksums, SQL rows, log
lines), assessment (confirmed / suspected / clean-with-evidence), and recommended action.
**Do not mark any item "clean" without the positive evidence that proves it.**

## Relationship to NH POS

NH POS is unaffected at the code layer by a WordPress compromise (separate host; signed,
tenant-scoped webhooks; connection-token reads; card data via hosted fields only). The one
cross-system action is **rotating the NH POS-side storefront-connection token and the
location's gateway `webhook_secret`** if the WP host is implicated — a configuration
change, performed only after approval.
