Session log: cPanel CVE-2026-41940 IOC scan + remediation on IX/WebSvr
Both servers were already patched (11.110.0.97 and 11.134.0.20) via daily auto-update. IOC scan found 16 flagged sessions across both plus 4 uncommented SSH keys on IX. Critical remediation: - Forensic evidence preserved before any deletion - 4 uncommented SSH keys removed from IX (server-side backup retained) - 16 flagged sessions purged across both servers - Root passwords rotated via chpasswd - New WHM API tokens created; 3 stale transfer-* tokens revoked - Vault entries + 1Password Infrastructure items updated Forensic deep-dive verdict: patch held. All 7 actual CVE exploit attempts (botnet IPs hitting /json-api/version) returned HTTP 403. The "multi-line pass" IOC hits on user sessions were false positives. Unidentified 76.18.103.222 root session traced to routine SSL maintenance (zero sensitive endpoints touched). Skill hardening: - Added MANDATORY service-token directive to .claude/commands/1password.md enforcing OP_SERVICE_ACCOUNT_TOKEN from SOPS for all op CLI calls - Per Mike: memory files alone don't reliably bind agent behavior; baking governance into skill content loaded at moment of use. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,16 +34,61 @@ Never ask users to paste API keys, passwords, or tokens into:
|
||||
|
||||
---
|
||||
|
||||
## Setup Check
|
||||
## ⚠️ MANDATORY: Use the SOPS-vaulted service account token, never the desktop session
|
||||
|
||||
Always verify the CLI is ready before any operation:
|
||||
**Every `op` invocation in agent flows must run with `OP_SERVICE_ACCOUNT_TOKEN` set.** The desktop-app integration prompts to unlock the app, which interrupts the agent flow and is unacceptable. The service token is in the SOPS vault at `infrastructure/1password-service-account.sops.yaml` (vault entry kind=`api-key`, name=`1Password Service Account (Agentic-RW)`).
|
||||
|
||||
### Load the token at the start of any 1Password work
|
||||
|
||||
```bash
|
||||
# Decrypt the service token from SOPS (uses the machine's age key)
|
||||
export OP_SERVICE_ACCOUNT_TOKEN=$(sops -d /c/Users/guru/vault/infrastructure/1password-service-account.sops.yaml 2>/dev/null \
|
||||
| grep -E '^\s*credential:' | sed -E 's/^\s*credential:\s*//' | head -1)
|
||||
|
||||
# Verify
|
||||
op whoami # expect "User Type: SERVICE_ACCOUNT"
|
||||
```
|
||||
|
||||
After `export`, every subsequent `op` call in the same bash invocation inherits the token. For one-off calls without exporting:
|
||||
|
||||
```bash
|
||||
SVC=$(sops -d /c/Users/guru/vault/infrastructure/1password-service-account.sops.yaml 2>/dev/null | grep -E '^\s*credential:' | sed -E 's/^\s*credential:\s*//' | head -1)
|
||||
OP_SERVICE_ACCOUNT_TOKEN="$SVC" op item get "Item Name" --vault Infrastructure
|
||||
```
|
||||
|
||||
### Vault path resolution
|
||||
|
||||
The vault lives wherever `.claude/identity.json` says (`vault_path`). On the current Windows workstation it's `C:/Users/guru/vault`, but other machines (Howard's, future workstations) may differ. Resolve dynamically when needed:
|
||||
|
||||
```bash
|
||||
VAULT_DIR=$(python -c "import json; print(json.load(open('/c/Users/guru/ClaudeTools/.claude/identity.json'))['vault_path'])")
|
||||
SVC=$(sops -d "$VAULT_DIR/infrastructure/1password-service-account.sops.yaml" 2>/dev/null | grep -E '^\s*credential:' | sed -E 's/^\s*credential:\s*//' | head -1)
|
||||
export OP_SERVICE_ACCOUNT_TOKEN="$SVC"
|
||||
```
|
||||
|
||||
### Service account scope (verified 2026-04-30)
|
||||
|
||||
The Agentic-RW service account has access to: **Clients, Infrastructure, Internal Sites, Managed Websites, MSP Tools, Projects, Sorting**. The Private vault is intentionally NOT shared with the service account — if you need to read from Private, that's a different conversation, not a fallback to desktop session.
|
||||
|
||||
### When the token fails
|
||||
|
||||
- `op vault list` returns "account is not signed in" with the token set → token is malformed or revoked. Decrypt directly via `sops -d` and inspect.
|
||||
- `vault.sh get-field` may fail with "PyYAML not installed" — use direct `sops -d` + grep instead until that wrapper bug is fixed.
|
||||
- Never fall back to the desktop-app session in agent flows. If the service token is unrecoverable, stop and tell Mike.
|
||||
|
||||
---
|
||||
|
||||
## Setup Check (only for net-new machine onboarding)
|
||||
|
||||
For a fresh workstation that doesn't have the service token wired up yet:
|
||||
|
||||
```bash
|
||||
bash scripts/check_setup.sh
|
||||
```
|
||||
|
||||
If not installed: https://developer.1password.com/docs/cli/get-started/
|
||||
If not signed in: unlock the **1Password desktop app** (after Mac restart, the app must be unlocked before the CLI works)
|
||||
|
||||
The desktop-app sign-in flow is for **interactive human use**, not agent flows — those go through the service account above.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
- [Ollama Tier-0 Routing](feedback_ollama_tier0_routing.md) - Route drafts/summaries/classifications through Ollama (qwen3:14b). Mike designed ClaudeTools this way — not optional.
|
||||
- [Syncro Emergency Billing](feedback_syncro_emergency_billing.md) — Emergency = 1.5× multiplier, not additive. Branch by `customer.prepay_hours`: no-prepaid → `26184` at actual hrs; prepaid → `26118` at hrs×1.5. Never stack. Always set `price_retail`.
|
||||
- [Identity precedence](feedback_identity_precedence.md) — Trust `.claude/identity.json` over the system-reminder `userEmail` hint when they disagree (shared-login machines).
|
||||
- [1Password — always use service token](feedback_1password_service_token.md) — Source OP_SERVICE_ACCOUNT_TOKEN from SOPS for every `op` call. Desktop-app integration prompts are unacceptable in agent flows.
|
||||
|
||||
## Machine
|
||||
- [ACG-5070 Workstation Setup](reference_workstation_setup.md) - Windows 11 Pro clean install 2026-03-30, replaced CachyOS. All tools installed.
|
||||
|
||||
26
.claude/memory/feedback_1password_service_token.md
Normal file
26
.claude/memory/feedback_1password_service_token.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: 1Password — always use service account token
|
||||
description: Use the SOPS-vaulted OP_SERVICE_ACCOUNT_TOKEN for all op CLI calls; the desktop-app integration prompts are unacceptable in agent flows
|
||||
type: feedback
|
||||
---
|
||||
|
||||
For every `op` CLI invocation, source `OP_SERVICE_ACCOUNT_TOKEN` from `infrastructure/1password-service-account.sops.yaml` first. Without it, `op` falls back to the desktop-app integration which interrupts the workflow with "unlock the app" prompts.
|
||||
|
||||
**Why:** Mike confirmed 2026-04-30 — "the prompts are infuriating." Service account auth is the standard CI/agent pattern documented in the 1password skill but I had been defaulting to the desktop session.
|
||||
|
||||
**How to apply:**
|
||||
```bash
|
||||
SVC_TOKEN=$(sops -d /c/Users/guru/vault/infrastructure/1password-service-account.sops.yaml 2>/dev/null \
|
||||
| grep -E '^\s*credential:' | sed -E 's/^\s*credential:\s*//' | head -1)
|
||||
|
||||
# Pass through env var to every op call
|
||||
OP_SERVICE_ACCOUNT_TOKEN="$SVC_TOKEN" op item get ...
|
||||
# Or export once at the top of a script
|
||||
export OP_SERVICE_ACCOUNT_TOKEN="$SVC_TOKEN"
|
||||
```
|
||||
|
||||
The `vault.sh get-field` wrapper currently fails on this entry due to a missing PyYAML dependency in the wrapper's fallback parser — use direct `sops -d` + grep until that's fixed.
|
||||
|
||||
**Vaults the service account can see** (per 2026-04-30 test): Clients, Infrastructure, Internal Sites, Managed Websites, MSP Tools, Projects, Sorting. (The Private vault is intentionally not shared with the service account.)
|
||||
|
||||
**When to skip:** Never. If the desktop session also happens to be authed, that's fine, but the service token path must be the one the agent reaches for.
|
||||
224
session-logs/2026-04-30-session.md
Normal file
224
session-logs/2026-04-30-session.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# 2026-04-30 — cPanel CVE-2026-41940 incident response on IX + WebSvr + 1Password skill hardening
|
||||
|
||||
## User
|
||||
- **User:** Mike Swanson (mike)
|
||||
- **Machine:** GURU-BEAST-ROG
|
||||
- **Role:** admin
|
||||
- **Session span:** 2026-04-29 ~14:30 PT rolling into 2026-04-30 ~07:15 PT (~6 hours of active engagement)
|
||||
|
||||
## Session Summary
|
||||
|
||||
The session began with a follow-up email to Michelle Sora, pitching a migration from GoDaddy-resold M365 to direct billing with consolidation of `pro-techservices.co` and `pro-techhelps.com` under one tenant. The tone was adjusted to avoid technical jargon and emphasize minimal user impact, with a soft close and call-to-action for a quick call. Final draft was saved to a temp file and opened in Notepad for Mike's review and send.
|
||||
|
||||
The bulk of the session focused on responding to **cPanel CVE-2026-41940**, a CRLF injection authentication bypass with CVSS 9.8 actively exploited in the wild since approximately 2026-02-23 per public security research. After verifying both ACG cPanel servers (WebSvr on CentOS 7 + cPanel 11.110.0.97; IX on CloudLinux 9 + cPanel 11.134.0.20) were already patched via daily auto-update, the cPanel-provided IOC detection script was run on both servers. Initial findings showed 7 of 7 flagged sessions on WebSvr and 11 of 16 on IX, including root sessions, plus four uncommented RSA keys in IX's `/root/.ssh/authorized_keys` — the classic attacker-persistence fingerprint.
|
||||
|
||||
Critical remediation followed Mike's authorization: forensic preservation of all flagged session files + access logs + last/wtmp output, removal of the four uncommented SSH keys with server-side backup, purge of all 16 flagged session files, root password rotation via `chpasswd`, and creation of new WHM API tokens. SOPS vault entries were updated with the new credentials, committed and pushed (`abfa955`). Per Mike's directive, three `transfer-*` tokens (leftover from past account migrations) were revoked from WebSvr; clustering tokens (`reverse_trust_*`, `NS2DNS`, `IX_DNS_Token`, `WEBSVR_DNS`, `ConfigCluster`, `PARENT-DO_NOT_DELETE-*`) and undocumented `Claude`/`ClaudeToken` tokens were kept.
|
||||
|
||||
A subsequent forensic deep-dive cleared the picture significantly. **All seven actual CVE exploit attempts across both servers returned HTTP 403** (the source IPs were DigitalOcean and similar cloud-VPS botnet scanners hitting `/json-api/version` with the injected token) — the patch is working as designed. The "multi-line pass" IOC hits on user sessions turned out to be false positives — those sessions had `method=handle_form_login` origins with normal cPanel UI traffic flagged by an IOC check that has poor specificity on cPanel 134. The unidentified `76.18.103.222` root session on IX (1203 hits Apr 29) was traced to routine SSL maintenance work — 1113 dashboard auto-refresh polls plus one `installssl` call, zero sensitive endpoints touched. Verdict: **patch held, all CVE attempts blocked at the HTTP layer; credential rotation served as defense-in-depth, not breach response.**
|
||||
|
||||
The session closed with two pieces of process improvement. First, the new credentials were synced to 1Password's Infrastructure vault — but Mike pushed back on my use of the desktop-app-integrated `op` session, which prompts to unlock the app in agent flows. He pointed out the SOPS-vaulted service account token (`infrastructure/1password-service-account.sops.yaml`) that should be used. After verifying the service token works prompt-free, I saved a feedback memory entry — and Mike pushed back again that memory files alone don't bind agent behavior reliably ("you ignore memory files"). The directive was then baked directly into `.claude/commands/1password.md` as a MANDATORY section at the top of the skill, with exact commands, vault path resolution from `identity.json`, scope details, and failure-mode guidance. Skill is in the synced ClaudeTools repo so when Howard syncs, his workstation gets the same enforcement.
|
||||
|
||||
A late report from Mike that the new IX password "doesn't seem to work" was investigated and confirmed to be a copy error on his end — server-side SSH and WHM web login both succeed with the rotated password.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- **WHM Transfer Tool migration over in-place ELevate** for any future WebSvr CentOS 7 → AlmaLinux move. Lower risk profile, parallel testing, easy rollback. ELevate makes more sense for AL8→AL9 single-hop later.
|
||||
- **Preserve forensic evidence before purging sessions** — downloaded raw session files locally before any rm operation. Without that, the deep-dive analysis (which proved exploits were blocked) wouldn't have been possible.
|
||||
- **Remove uncommented SSH keys despite uncertainty about Rob's identity.** Mike accepted the risk of accidentally cutting Rob off SSH on the basis that re-adding a known key is trivial, while leaving an attacker-style persistence vector in place is not.
|
||||
- **Keep clustering tokens (reverse_trust_*, NS2DNS, IX_DNS_Token, WEBSVR_DNS, ConfigCluster, PARENT-DO_NOT_DELETE-*).** These are load-bearing for inter-server DNS clustering and account transfers between IX and WebSvr. The initial JSON parse error that turned the bulk-revoke into a no-op was, on reflection, the correct outcome.
|
||||
- **Kill transfer-\* tokens** (transfer-1749689378, transfer-1765466491, transfer-1766779535) on WebSvr per Mike's directive. These are leftover from past T2T account migrations and serve no current purpose.
|
||||
- **Howard granted Owner on ACG Azure subscription** (carried forward from prior session, but the rationale stands): matches CLAUDE.md trust model; one-time grant with `gururmm-signing-rg` resource lock + cost alert as guardrails removes Mike as a permanent bottleneck.
|
||||
- **Use the SOPS-vaulted 1Password service account token for all `op` invocations**, never the desktop-app session. The desktop integration's unlock prompts are unacceptable in agent flows.
|
||||
- **Bake directives that govern agent behavior into the SKILL files, not memory.** Memory entries are advisory; skill content is loaded at the moment of use and harder to ignore. Confirmed by Mike — "you ignore memory files."
|
||||
|
||||
## Problems Encountered
|
||||
|
||||
- **`whmapi1 api_token_list_v2` returned "Unknown app" error.** cPanel's API method name was different from what the skill docs implied. Worked around by reading `/var/cpanel/authn/api_tokens_v2/whostmgr/root.json` directly via SFTP+Python.
|
||||
- **JSON parse error in initial token-revocation script.** I iterated the outer `tokens` key as if it were an item rather than a container of hashed-key items. The result was a no-op revoke (sent the literal name "tokens" to `api_token_revoke`, which silently succeeded as nothing-matched). On reflection this was the safe outcome — a correct parse would have revoked the legitimate clustering tokens and broken the IX↔WebSvr cluster. Caught and re-implemented correctly later.
|
||||
- **`sops set` flags `--value-stdin` and `--value-file` are not implemented in sops 3.12.2 on Windows** despite being documented in `--help`. Worked around by using the `EDITOR` env var pattern with a small Python script that performs the YAML field replacement, then sops re-encrypts on close.
|
||||
- **EDITOR path mangling in Git Bash.** Both backslash (`C:\path\script.py`) and forward-slash (`C:/path/script.py`) had different failure modes; forward slashes ultimately worked because Python on Windows accepts them and Git Bash didn't translate them mid-argument.
|
||||
- **Git Bash MSYS path translation of `/cpsess...` arguments.** When passing a cPanel session token (which begins with `/`) as a command-line argument, Git Bash interpreted it as a path needing translation. Fixed by passing the token without leading slash.
|
||||
- **`vault.sh get-field` requires PyYAML** which is missing in the wrapper's Python fallback path. Worked around by direct `sops -d` + grep + sed for the rest of the session. Filed mentally as a follow-up to fix the wrapper.
|
||||
- **Memory file alone wasn't enough.** Mike confirmed I had been ignoring memory entries that documented preferred patterns. Real fix was baking the directive into the skill content itself so it loads at the moment the skill is invoked.
|
||||
- **IX password "doesn't seem to work" alarm** — investigated end-to-end (SOPS, 1Password, SSH login, WHM HTTP login all verified working with the rotated password). Resolved as a copy-paste error on Mike's end.
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
### Files created
|
||||
- `.claude/memory/feedback_1password_service_token.md` — feedback memory entry on always using OP_SERVICE_ACCOUNT_TOKEN
|
||||
- `session-logs/2026-04-30-session.md` — this file
|
||||
|
||||
### Files modified (ClaudeTools repo)
|
||||
- `.claude/commands/1password.md` — added MANDATORY service-token section near the top, with vault resolution patterns and failure-mode guidance
|
||||
- `.claude/memory/MEMORY.md` — added pointer to the new feedback entry
|
||||
|
||||
### Files modified (vault repo) — committed `f4d3554` rebased to `abfa955`, pushed
|
||||
- `infrastructure/ix-server.sops.yaml` — root password updated to rotated value
|
||||
- `infrastructure/websvr-legacy-hosting.sops.yaml` — root password updated, api-token replaced with new value
|
||||
|
||||
### 1Password Infrastructure vault items modified
|
||||
- `WebSvr (Legacy Hosting)` (id `7tv3sgyhzbfpyhld6pyt5gn4li`): `password` and `API Token` fields updated
|
||||
- `IX Server` (id `brsoqhoalrb4d53jn4lxcj4xdq`): `password` updated, **new `API Token` field added** (didn't exist before)
|
||||
|
||||
### Server-side changes (IX, 172.16.3.10)
|
||||
- 9 flagged session files purged from `/var/cpanel/sessions/raw/`
|
||||
- 4 uncommented SSH keys removed from `/root/.ssh/authorized_keys` (server-side backup at `/root/.ssh/authorized_keys.bak.20260430T132232Z`)
|
||||
- Root password rotated via `chpasswd`
|
||||
- New WHM API token created: `acg_post_cve_20260430T132232Z` = `PA42FSUXASFC0IO9MKH1DUHQ7L5G67PQ`
|
||||
|
||||
### Server-side changes (WebSvr, websvr.acghosting.com)
|
||||
- 7 flagged session files purged from `/var/cpanel/sessions/raw/`
|
||||
- Root password rotated via `chpasswd`
|
||||
- New WHM API token created: `acg_post_cve_20260430T132423Z` = `81YBBUPPHZ2EEMWJ42WUN2VIOS01X9U5`
|
||||
- Three transfer-* tokens revoked: `transfer-1749689378`, `transfer-1765466491`, `transfer-1766779535`
|
||||
|
||||
### Temp files (forensic evidence — preserved on local workstation)
|
||||
- `C:/Users/guru/AppData/Local/Temp/cpanel-ioc-evidence/ix/` — 9 raw session files, access_log snapshot (~110 MB), last/wtmp dump, authorized_keys backup, NEW_ROOT_PASSWORD + NEW_API_TOKEN value files, IOC + forensic logs
|
||||
- `C:/Users/guru/AppData/Local/Temp/cpanel-ioc-evidence/websvr/` — same structure, 7 session files, ~253 MB access_log
|
||||
- `C:/Users/guru/AppData/Local/Temp/cpanel-ioc-evidence/session_76.18.103.222_dump.log` — extracted activity timeline for the unidentified IX root session
|
||||
|
||||
### Tooling artifacts (also preserved in temp)
|
||||
- `ssh_check.py` — paramiko-based IOC scan + triage runner
|
||||
- `ssh_remediate.py` — preserves evidence + removes keys + purges sessions + rotates root + creates new API token
|
||||
- `fix_api_tokens.py` — secondary token enumeration via direct `root.json` read
|
||||
- `revoke_transfer_tokens.py` — targeted whmapi1 revoke for specific token names
|
||||
- `forensic_pass.py` — read-only deep-dive (cp_security_token usage analysis, suspect IP access logs, /etc/passwd + sudoers + SUID + system mod audit, webshell heuristic)
|
||||
- `session_activity_dive.py` — full access_log extraction for a specific cpsess+IP combo with endpoint clustering and sensitive-keyword flagging
|
||||
- `op_sync_creds.py` — subprocess-based 1Password item updater (avoids shell quoting issues)
|
||||
- `sops_editor.py` — EDITOR-mode YAML field setter for SOPS-vaulted files
|
||||
|
||||
## Credentials & Secrets (UNREDACTED)
|
||||
|
||||
### Rotated 2026-04-30
|
||||
|
||||
| Service | Username | Value |
|
||||
|---|---|---|
|
||||
| IX server SSH/WHM root | root | `t4qygLl7{1zJcUj#022W^FBQ>}qYp-Od` |
|
||||
| WebSvr SSH/WHM root | root | `[3H+_f.Yh4c0>@egH[6L!?u]S3s[9C82` |
|
||||
| IX WHM API token (`acg_post_cve_20260430T132232Z`) | n/a | `PA42FSUXASFC0IO9MKH1DUHQ7L5G67PQ` |
|
||||
| WebSvr WHM API token (`acg_post_cve_20260430T132423Z`) | n/a | `81YBBUPPHZ2EEMWJ42WUN2VIOS01X9U5` |
|
||||
|
||||
All four are stored in 1Password Infrastructure vault and SOPS vault entries. The IX vault entry doesn't currently have an `api-token` field structure — the IX API token is in 1Password but not yet in SOPS.
|
||||
|
||||
### Existing references confirmed (not rotated this session)
|
||||
- `infrastructure/1password-service-account.sops.yaml` — Agentic-RW service token, kind=`api-key`. Used for prompt-free `op` CLI access. Scope: Clients, Infrastructure, Internal Sites, Managed Websites, MSP Tools, Projects, Sorting (Private intentionally excluded).
|
||||
- WebSvr stale API token (now revoked from server, no longer in vault): `8ZPYVM6R0RGOHII7EFF533MX6EQ17M7O`
|
||||
|
||||
## Infrastructure & Servers
|
||||
|
||||
### IX Server (172.16.3.10 / `ix.azcomputerguru.com` / public 72.194.62.5)
|
||||
- OS: CloudLinux 9.7 (TuxCare ELS kernel)
|
||||
- cPanel: 11.134.0 build 20 (patched for CVE-2026-41940)
|
||||
- 72 hosting accounts (per `/etc/trueuserdomains`)
|
||||
- Auth: PAM via /etc/shadow (single password store; SSH and WHM share)
|
||||
- WHM port 2087, cPanel port 2083, SSH port 22
|
||||
|
||||
### WebSvr (`websvr.acghosting.com`)
|
||||
- External IPs: **162.248.93.78, 162.248.93.81, 162.248.93.233** (the vault entry shows .81; the public-IP probe returned .233)
|
||||
- OS: CloudLinux 7.9 (CentOS 7 base — past EOL)
|
||||
- cPanel: 11.110.0 build 97 (patched for CVE-2026-41940)
|
||||
- 26 hosting accounts
|
||||
- Same auth model as IX
|
||||
|
||||
### Suspect IPs encountered (history captured for future reference)
|
||||
- `129.222.129.230` — confirmed Mike (today's WHM session on IX)
|
||||
- `129.222.143.18` — likely Rob (web guy), Dec 15 2025 SSH burst on IX, no WHM activity
|
||||
- `76.18.103.222` — unidentified WHM root session Apr 29 on IX (1203 hits, all benign — SSL maintenance), historical TPS support workflows match support pubkey ticket IDs (95714774, 95758605); could be Howard, Mike on different network, or a cPanel support tech
|
||||
- `64.139.88.249` — WebSvr Feb 24 2026 root login from unfamiliar Cox AZ IP (5h 11m), logs rotated out, cannot characterize
|
||||
- `23.180.120.132`, `143.198.113.39`, `159.65.217.152`, `149.102.229.144`, `195.177.94.161` — botnet scanner IPs (DigitalOcean, etc.) hitting `/json-api/version` with injected tokens. **All HTTP 403, all blocked.**
|
||||
|
||||
## Commands & Outputs
|
||||
|
||||
### CVE patched-version verification
|
||||
```bash
|
||||
/usr/local/cpanel/cpanel -V
|
||||
# IX: 134.0 (build 20) <-- patched build for v134 stream
|
||||
# WebSvr: 110.0 (build 97) <-- patched build for v110 stream
|
||||
cat /etc/cpupdate.conf
|
||||
# Both: UPDATES=daily <-- patches arrived automatically 2026-04-28
|
||||
```
|
||||
|
||||
### IOC scan (script provided by cPanel)
|
||||
- Saved at `/tmp/ioc_checksessions_files.sh` on each server (and locally in `cpanel-ioc-evidence/`)
|
||||
- Output: 9 flagged on IX, 7 on WebSvr; 4 of the 16 were pre-auth `badpass` injection attempts that all later returned HTTP 403 in access_log
|
||||
|
||||
### Critical remediation key commands
|
||||
```bash
|
||||
# Backup + key removal (IX)
|
||||
cp -p /root/.ssh/authorized_keys /root/.ssh/authorized_keys.bak.<TS>
|
||||
sed -i.removed-<TS> '<line_nums>d' /root/.ssh/authorized_keys
|
||||
|
||||
# Session purge
|
||||
for f in <flagged_files>; do rm -f "/var/cpanel/sessions/raw/$f"; done
|
||||
|
||||
# Password rotation
|
||||
echo 'root:<NEWPW>' | chpasswd
|
||||
|
||||
# WHM API token rotation
|
||||
whmapi1 api_token_create token_name='acg_post_cve_<TS>'
|
||||
whmapi1 api_token_revoke token_name='<old_name>'
|
||||
```
|
||||
|
||||
### 1Password CLI prompt-free auth (the new pattern)
|
||||
```bash
|
||||
SVC=$(sops -d /c/Users/guru/vault/infrastructure/1password-service-account.sops.yaml 2>/dev/null \
|
||||
| grep -E '^\s*credential:' | sed -E 's/^\s*credential:\s*//' | head -1)
|
||||
export OP_SERVICE_ACCOUNT_TOKEN="$SVC"
|
||||
op whoami # User Type: SERVICE_ACCOUNT — no prompts
|
||||
op item get "<id>" --vault Infrastructure --format json # NB: --vault required for service accounts
|
||||
```
|
||||
|
||||
### Verification of rotated IX password (after Mike's "not working" alarm)
|
||||
```
|
||||
SSH (paramiko): OK — connected as root, /etc/shadow shows hash dated 20573
|
||||
WHM web login: HTTP 200, status:1, /cpsess3383026374 issued
|
||||
SOPS vault value: exact match
|
||||
1Password value: exact match
|
||||
```
|
||||
→ password is correct end-to-end; Mike's "not working" was a copy error on his side.
|
||||
|
||||
## Pending / Incomplete Tasks
|
||||
|
||||
### Mike's outstanding items
|
||||
- [ ] Send the Michelle Sora email when ready (`C:/Users/guru/AppData/Local/Temp/michelle-email-draft.txt`)
|
||||
- [ ] Verify identity of `76.18.103.222` (likely Howard or Mike on a different network — once confirmed, case fully closed on IX)
|
||||
- [ ] Decide whether to add `api-token` field structure to `infrastructure/ix-server.sops.yaml` and back-fill the new IX WHM API token there (it's in 1Password already)
|
||||
- [ ] Decide whether to document the `Claude` and `ClaudeToken` WHM API tokens in SOPS or revoke them (currently undocumented + broad ACLs, kept per directive but flagged)
|
||||
- [ ] Re-add Rob's SSH key to IX once he confirms which of the 4 removed keys was his (server-side backup at `/root/.ssh/authorized_keys.bak.20260430T132232Z`)
|
||||
|
||||
### Tracked TODOs (not blocking)
|
||||
- [ ] Fix `vault.sh get-field` PyYAML dependency in the Python fallback path so the wrapper works for service-account-style entries
|
||||
- [ ] Eventually run a fuller webshell/integrity scan across all hosted sites (the quick heuristic this session was minimal — only flagged WP core files)
|
||||
- [ ] Long-term: WebSvr CentOS 7 → AlmaLinux migration via WHM Transfer Tool (separate project, not blocked)
|
||||
|
||||
### Items completed this session that close prior threads
|
||||
- [x] CVE-2026-41940 IOC scan + remediation (both servers)
|
||||
- [x] Credentials synced to 1Password Infrastructure vault
|
||||
- [x] 1Password skill hardened with mandatory service-token directive
|
||||
- [x] Memory entry added for service-token preference
|
||||
- [x] Vault entries (SOPS) updated and pushed
|
||||
|
||||
## Reference Information
|
||||
|
||||
### Files / paths
|
||||
- 1Password skill: `.claude/commands/1password.md` (project-local, takes precedence over the npm openclaw skill)
|
||||
- Memory entry: `.claude/memory/feedback_1password_service_token.md`
|
||||
- Memory index: `.claude/memory/MEMORY.md`
|
||||
- Service account vault entry: `infrastructure/1password-service-account.sops.yaml`
|
||||
- IX vault entry: `infrastructure/ix-server.sops.yaml`
|
||||
- WebSvr vault entry: `infrastructure/websvr-legacy-hosting.sops.yaml`
|
||||
- Forensic evidence directory: `C:/Users/guru/AppData/Local/Temp/cpanel-ioc-evidence/`
|
||||
|
||||
### Vault scope (Agentic-RW service account, verified 2026-04-30)
|
||||
Visible: Clients, Infrastructure, Internal Sites, Managed Websites, MSP Tools, Projects, Sorting
|
||||
Excluded: Private (intentional)
|
||||
|
||||
### CVE-2026-41940 reference
|
||||
- CVSS 9.8, CRLF injection authentication bypass via session loading
|
||||
- Affects all cPanel versions after 11.40 including DNSOnly
|
||||
- Patched in: 11.110.0.97, 11.118.0.63, 11.126.0.54, 11.132.0.29, 11.134.0.20, 11.136.0.5
|
||||
- Public PoC at watchTowr; active exploitation since ~2026-02-23
|
||||
- Fix command: `/scripts/upcp --force`
|
||||
|
||||
### Why the deep-dive verdict was "patch held"
|
||||
- Each of the 7 actual exploit attempts had distinct cp_security_tokens that, when grep'd against access_log, appeared exactly once each with HTTP 403 against `/json-api/version` (and /applist, /listwwwacctconf, /get_tweaksetting on one). No HTTP 200 with an injected token from any external IP. The patch's session-validation logic is doing its job.
|
||||
Reference in New Issue
Block a user