Files
claudetools/clients/glaztech/session-logs/2026-06-04-session.md
Mike Swanson b93c9d9e94 sync: auto-sync from GURU-5070 at 2026-06-04 19:29:23
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-04 19:29:23
2026-06-04 19:29:28 -07:00

183 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Glaztech — Session Log 2026-06-04
## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070
- **Role:** admin
## Session Summary
Performed a read-only intrusion / brute-force log review on the Glaztech web server (`WWW`, agent `455a1bc7-1c29-42bc-b597-fa1e64f08eec`, Windows Server 2019, agent v0.6.54) via GuruRMM, following up on the 2026-06-03 website security assessment. Question: is there evidence anyone tried to brute-force the website logins or the server itself.
Analyzed 7 days of IIS logs (`C:\inetpub\logs\LogFiles\W3SVC4`, ~52,000 requests, May 29 Jun 4). Identified the two login endpoints (`/customer_login.aspx`, `/emp/employee-login.aspx`), then broke POSTs down per source IP with HTTP status codes. An initial pass flagged the employee portal as suspicious (381 "failures"/HTTP 200 vs 77 "successes"/302 — an inverted ratio vs the customer login). Pulling full per-IP request timelines corrected this: the employee login returns **HTTP 200 on both success and failure** (no redirect-on-success), so status code alone does not indicate a failed staff login. The top "suspect" IP `160.3.157.9` proved to be a single legitimate employee on an iPhone checking timecards. No brute-force or credential-stuffing signature exists on either endpoint.
Also reviewed the Windows Security event log (4625 failed logons / 4624 successes, 7-day window): only 13 failed logons, all LogonType 3 (SMB) from internal LAN IPs, zero external, no RDP failures, and no successful remote logons from public IPs — indicating RDP/SMB are not internet-exposed and nothing got in at the OS auth layer.
Folded the findings into the security record: added **Appendix A — Intrusion / Brute-Force Log Review (2026-06-04)** to `clients/glaztech/reports/2026-06-03-website-security-assessment.md`, and extended finding **H5** with a detection-blind-spot note (200-on-both + no lockout + no failed-login logging = slow guessing would be invisible).
## Key Decisions
- **Used IIS request logs as the primary evidence source, not the Windows Security log**, because the app's custom session-based auth never generates Windows logon events. The Security log was used only to rule out RDP/SMB brute force against the host.
- **Did not treat HTTP 200 on the employee login as a failure** once the timelines showed 200-on-success. Reported the corrected interpretation rather than the alarming-but-wrong intermediate read.
- **Flagged the HTTP 500 bursts** as worth an app-side look (possible SQLi error path per C3) but explicitly classified them as not-a-brute-force, to avoid overstating the threat.
- **Recorded the detection gap as the actionable outcome** — the review found no attacker, but confirmed that a slow guessing attack would currently be undetectable; reinforces the existing #32378 remediation (add lockout + failed-login logging).
## Problems Encountered
- **`jq -n --arg` heredoc capture glitched** when dispatching the Security-log script (parse error / empty command). Resolved by writing the PowerShell to `.claude/tmp/seclog.ps1` and passing it with `jq --rawfile`, which is robust against embedded quoting.
- **IIS logs do not record POST bodies**, so attempted usernames/passwords are not recoverable — noted as a limitation; confirming which accounts (if any) were targeted would require app-level auth logging that does not exist.
## Configuration Changes
- Modified: `clients/glaztech/reports/2026-06-03-website-security-assessment.md` — added Appendix A (2026-06-04 log review); extended H5 with detection-blind-spot note + failed-login-logging fix; updated Status line.
- Created: `clients/glaztech/session-logs/2026-06-04-session.md` (this file).
- Local-only (gitignored): `.claude/current-mode` set to `infra`; transient `.claude/tmp/seclog.ps1`, `.claude/tmp/rmm.token`, `.claude/tmp/rmm.cmd`.
## Credentials & Secrets
- None discovered or created. RMM admin credential read from vault `infrastructure/gururmm-server.sops.yaml` (unchanged).
## Infrastructure & Servers
- **WWW** (Glaztech web server) — internal `192.168.8.72`, public `65.113.52.88`, Windows Server 2019 (build 17763), GuruRMM agent `455a1bc7-1c29-42bc-b597-fa1e64f08eec` (v0.6.54), client "Glaztech Industries" / site "TUS - Tucson".
- Active IIS site: `W3SVC4` (log dir `C:\inetpub\logs\LogFiles\W3SVC4`; daily `u_exYYMMDD.log`). Older sites W3SVC1/2/3 are stale (last writes 20182022).
- SQL backend (context only, not touched): `192.168.8.62,3436` (`GTI-INV-SQL`).
## Commands & Outputs
- IIS analysis dispatched as PowerShell via GuruRMM (`POST /api/agents/<id>/command`), parsing `#Fields`-keyed log lines for `cs-method`, `cs-uri-stem`, `c-ip`, `sc-status`, `date`, `time`, `cs(User-Agent)`.
- Key counts: customer login 2,547×302 / 78×200 / 5×500; employee login 77×302 / 381×200 / 6×500; 740 distinct IPs hit the login endpoints; 241 distinct IPs hit the employee login.
- Security log: span 2026-03-31 .. 2026-06-04; 4625 in last 7d = 13, all type 3, all internal, usernames = 12 blank + 1 `tomabens`; 4624 external type 3/8/10 = none.
## Pending / Incomplete Tasks
- **App-side investigation of the HTTP 500 bursts** on post-login pages (possible SQLi error path) — IPs `201.146.179.166`, `64.178.182.162`, `205.185.107.49`, `172.87.137.60`. Not yet handed to Tom.
- **#32378 (Waiting on Customer)** unchanged: add account lockout + failed-login logging (now explicitly tied to this review), fix SQLi, least-privilege the `tom`/`sysadmin` DB login, separate website DB from GTIware, stop storing cards / never CVV, encrypt at rest.
- Optional confirming check offered but not run: enumerate listening/forwarded ports on `WWW` to turn "RDP almost certainly not exposed" into "confirmed."
- Carried over: `corp` DB `cc_file` "Invalid object name" anomaly; confirm Payrilla payment-flow scope.
## Reference Information
- Report: `clients/glaztech/reports/2026-06-03-website-security-assessment.md` (Appendix A)
- Companion: `clients/glaztech/reports/2026-06-03-pci-cardholder-data-finding.md`
- Wiki: `wiki/clients/glaztech.md`
- Ticket: Syncro **#32378** (Glaztech website security — Waiting on Customer)
- RMM API: `http://172.16.3.30:3001` | agent id `455a1bc7-1c29-42bc-b597-fa1e64f08eec`
---
## Update: 09:38 PT — Deep host + SQL-infrastructure recon (Grok gap-analysis loop)
### Session Summary
Ran a collaborative gap-analysis loop (Claude + Grok CLI, 4 Grok turns on session `019e9351-…`) plus 3 read-only GuruRMM pulls on the `WWW` host to find what the website security assessment had not yet checked. Grok identified overlooked surfaces; Claude pulled the requested read-only host telemetry + SQL **metadata**; results were fed back to Grok each turn. Findings were folded into the assessment report. All read-only; no CHD and no secret values intentionally retrieved.
### Key Findings (new vs. prior report)
- **GTIware card engine is co-resident in the public IIS worker on the `tom` sysadmin connection.** `gt_auto_process_2020.dll` / `glaztech_utilities_2020.dll` live in `D:\web\glaztech_4\Bin\`; their `.dll.config` carry no own connection strings → they use the website's `tom` connections. `w3wp` PID 13752 = `IIS APPPOOL\glaztech_new`.
- **`xp_cmdshell = 1` (enabled) on `GTI-INV-SQL`** → instant OS RCE from a `tom` SQLi (`clr`/`Ole`/`Ad Hoc`/`remote admin` all 0).
- **SQL Agent service runs as `Administrator@glaztech.com`** (domain-admin class).
- **Cleartext `glaztech\administrator` domain-admin password embedded in `msdb` SQL Agent backup-copy job steps** (`xp_cmdshell 'net use … /user:glaztech\administrator <pw>'`) — readable by any sysadmin/`tom` connection. **Value redacted from all artifacts (sed-scrubbed from the recon output); flagged for rotation.**
- **7 linked servers** (remote-login + data-access) across `192.168.0.x` and `192.168.8.x` (`mas_gti`, `qqest`/TimeForce, backup hosts, `GTI-INV-SQL\GTISQL`).
- **`sa` login enabled; all 47 DBs `is_encrypted=False` (no TDE);** CHD `.bak` backups copied to `\\192.168.8.52\sql_backup\`.
- **`Web.config` holds two processors' secrets in cleartext** (CyberSource `cybs.*` + on-disk signing keys; PNC `pnc_key/pin/token`); **`Everyone:(R)` ACL** on web root + `bin` + `Web.config`.
- Host firewall Domain=On, **Private/Public=Off**; **TLS 1.0 explicitly `Enabled=1`**; new endpoints `/webhooks` (Samsara receiver) + `/webhooks1`.
- Server-side billing engine `d:\sql_jobs\bin\gt_console_apps.exe` (`cp`/`is`/`oa`/`lo`) run by the Agent (domain-admin).
- Extra hosts surfaced: `\\192.168.0.147\web\glaztech_4`, backups `8.52`/`8.212`, MAS `0.55`, linked `0.54`.
### Corrections / retractions
- An initial pass mis-flagged the `kgc7jt` ScreenConnect Cloud instance as "foreign/unrecognized third-party access" (draft finding **C6**). Per Mike: ScreenConnect (both), Datto RMM/EDR, Syncro, Splashtop are **ACG's sanctioned management stack**. **C6 fully retracted.** RealVNC is **Steve's** tool, kept as **H2** strictly for its end-of-life age (RealVNC 4.x ≈2009, listening 5900/5800).
- `machineKey` is AutoGenerate (not hardcoded); the app-pool identity is least-privilege locally — the privilege issue is the SQL `tom` login, not the Windows identity.
### Key Decisions
- Folded findings into the existing report as a new **C0-Extended** section + findings **C5/H9/H10** + an **Emergency-containment** roadmap bucket (E1E5) ahead of all other work, rather than a separate report.
- Domain-admin password returned incidentally in a job-step preview was scrubbed from the on-disk recon artifact and never written to the report (no-secrets constraint).
### Configuration Changes
- Edited `clients/glaztech/reports/2026-06-03-website-security-assessment.md` (347 → 415 lines): method/date, Exec summary, findings table (+C5/H9/H10, C6), C0 attack chain, new **C0-Extended** section, H2 rewrite, "Why the Cards Are Stored" nuance, Assumptions, PCI mapping, Emergency-containment bucket + structural items 2226, status footer.
### Commands & Outputs
- GuruRMM read-only PowerShell to `WWW` (agent `455a1bc7…`): **recon1** (IIS/app-pool/tasks/services/GTIware hunt/ACLs/net/firewall/TLS/certs), **recon2** (w3wp identity, GTIware `.dll.config` safe-parse, SQL metadata via the app's `tom` connection — `sys.configurations`/`dm_server_services`/`sys.servers`/`msdb` jobs+backups/principals/`sys.databases`), **recon3** (ScreenConnect probe).
- Grok CLI session `019e9351-ed1c-7bc3-b171-b4cf4b53745d` (4 turns) for gap analysis + corrected remediation sequencing.
### Pending / Incomplete Tasks
- Coord todos filed: **6d15fc88** (rotate `glaztech\administrator` + purge `msdb` cleartext use; disable `xp_cmdshell`/`sa`), **aebaf751** (least-privilege `tom` migration).
- Recon the second host `\\192.168.0.147\web\glaztech_4`; lock down `/webhooks`.
- Carried: `corp` DB `cc_file` anomaly; IDOR on `get_cc_data`; Payrilla scope.
### Reference Information
- Grok session `019e9351-ed1c-7bc3-b171-b4cf4b53745d`; SQL host `GTI-INV-SQL` `192.168.8.62,3436` (instance `GTISQL`).
- Coord todos `6d15fc88-db4f-4a35-a76a-a5a6a9f50795`, `aebaf751-d778-423f-a84b-314fbb294f30`.
---
## Update: 19:07 PT — Glaztech infra remediation blitz via RMM (dev-tool removal, web hardening, domain time fix, ACL, sa)
### Session Summary
Executed a large batch of Glaztech remediation through GuruRMM. WWW was already enrolled; mid-session Mike enrolled the **DCs + SQL server**, which unlocked the domain/SQL-side work. All actions were RMM-driven, verified, and caused **no outages**. Several changes were scheduled (off-hours) with backup + health-check + auto-rollback. One acute item (msdb plaintext domain-admin removal) is **paused awaiting method approval**.
### Completed (all verified, no outage)
- **Dev tooling removed from WWW (H1):** VS 2015 + 2022 (~15.6 GB reclaimed, via bootstrapper `/uninstall /quiet /norestart`), IIS Express, Notepad++, OpenSSL, RealDownloader (+ scheduled task). Archived D7x (`D:\d7`, `D:\d7x Resources`), `D:\3rd Party Tools`, `D:\Scripts`, `D:\bin` (CyberSource SDK sample), and web-root `Old_code`/`Old_bin`/26 `.pdb` to **`D:\_removed_devtools_2026-06-04\`** (reversible). One-time reboot finalized the VS `PendingFileRenameOperations` (cleared).
- **WWW Web.config hardening** (scheduled task `ACG-WebConfigHarden-20260604` @ box-17:05, applied + survived the 17:15 reboot): `debug=false`; security headers `X-Content-Type-Options: nosniff`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy: strict-origin-when-cross-origin`, `Strict-Transport-Security: max-age=31536000`; `httpCookies httpOnlyCookies=true requireSSL=true`; **CORS scoped to `<location path="emails">` (Origin:* Methods:GET)** and the site-wide wildcard CORS removed. Backup `D:\web\glaztech_4\Web.config.bak-20260604-170500`. Live headers verified.
- **Domain time fixed end-to-end:** PDC **GTI-INV-DC** was syncing from the Hyper-V host (VM IC provider) and drifting → re-pointed to **external NTP (pool.ntp.org)**, `VMICTimeProvider` disabled, marked reliable. **GTI-INV-DC1** → follows PDC. **WWW** (was `Local CMOS Clock`/free-running, ~8 min slow) re-registered (`w32tm /unregister`+`/register`) → PDC, clock **stepped +8 min**. **GTI-INV-SQL** → DC1. All four converged within ~3 s. Kerberos-skew resolved.
- **WWW `Everyone:(R)` ACL (E1):** removed from **`Web.config` + `bin`** (granted `IIS_IUSRS` + `IIS APPPOOL\glaztech_new` RX first; site stayed HTTP 301). Public static content (`emails/`,`images/`) left as a low-priority slower sweep.
- **GTI-INV-SQL: built-in `sa` disabled** (re-check showed **0 real user sessions**; the 29 "active" sessions were all `is_user_process=0` system sessions). Done via WWW's app `tom` connection (SYSTEM-on-SQL is not sysadmin).
### Key Findings
- **WWW clock** was never syncing (free-running) — ~68 min slow; surfaced when Mike noticed it. **PDC** itself was VM-host-timed, not NTP.
- **Forest = `glaztech.local` (root) + `glaztech.com` (child).** **NS4.glaztech.local holds the Schema-master FSMO but is a DEAD server** (per Mike) → orphaned FSMO; external NTP on GTI-INV-DC is correct (can't chain to dead root).
- **CORS:** the wildcard `Access-Control-Allow-Origin: *` was only used by cross-origin loads of `/emails/` assets (IIS logs: 188 OPTIONS, 181 → `/emails/`; none to the API/payment surface) → scoped to `/emails`.
- **msdb cleartext cred:** **11 TSQL backup-copy job steps** embed `net use \\192.168.8.52|.212\sql_backup\... /user:glaztech\administrator <pw> /persistent:yes` + a `copy`. They run as the SQL **engine** service account (machine acct, no share access) → can't just blank the creds. **0 existing SQL credentials/proxies; SQL Agent service account = `Administrator@glaztech.com` (domain admin).**
### Key Decisions
- **Web.config / ACL health checks must hit the real binding `http://192.168.8.72/` (Host: www.glaztech.com), NOT `127.0.0.1`** — the site binds to the LAN IP only. Caught + fixed the scheduled apply's health check at box-17:03, ~90 s before the 17:05 run (the 127.0.0.1 check would have false-rolled-back the change). **Reusable rule for future WWW scripts.**
- ACL fix scoped to `Web.config`+`bin` (the secrets/assemblies) instead of a slow full-tree `/T` (static content is public anyway).
- All scheduled/unattended changes built with backup + post-change health-check + auto-rollback; reachability-gated for the PDC NTP change (rollback to host time if NTP unreachable — it was reachable).
### PENDING — pick up next session
- **msdb plaintext removal — AWAITING GO on method.** Recommended: **SQL Credential + Agent CmdExec proxy** (encrypt the pw in `sys.credentials`, convert the 11 steps to CmdExec-under-proxy, drop inline creds; decoupled from Agent privilege; `ALTER CREDENTIAL` after rotation). Alt: `cmdkey` + strip inline. Test-first + snapshot originals to admin-only file (deleted after) + verify a copy works.
- **Rotate `glaztech\administrator`** — Mike coordinating with **Steve** (deferred). Identify all consumers first.
- Gated/heavier: **disable `xp_cmdshell`** (blocked until the 11 backup-copy steps are reworked — they depend on it); **disable TLS 1.0/1.1 on WWW** (needs reboot); **full web-root `Everyone` sweep** (low pri); **seize/clean Schema-master FSMO off dead NS4**; de-privilege the SQL Agent account.
- WWW one-time scheduled tasks `ACG-WebConfigHarden-20260604` + `ACG-Reboot-VSCleanup-20260604` both fired Result=0 (can be deleted or left).
### Infrastructure / Reference
- **Glaztech RMM agents** (client "Glaztech Industries"): `WWW` 455a1bc7 (site TUS-Tucson), `GTI-INV-DC` 0337e973 (**PDC**, INV-Involta), `GTI-INV-DC1` ffcaafac, `GTI-INV-SQL` 869e56b4. (NOT Glaztech: `SAGE-SQL`=Dataforth, `ACG-DC16`=ACG, `VWP-DC1`=VWP.)
- Domain `glaztech.com` (member servers); forest root `glaztech.local` (NS4, dead). Backup file servers `\\192.168.8.52\sql_backup`, `\\192.168.8.212\sql_backup`. SQL instance `GTISQL` @ `192.168.8.62,3436`.
- On-WWW logs: `C:\temp\{vs_uninstall, devtools_groupB, groupC, acl_fix, acl_fix2, sa_via_tom, webconfig_apply}.log`; on DCs/SQL: `C:\temp\timefix_*.log`.
- Local scripts (this machine): `C:\Users\guru\AppData\Local\Temp\grok_glaztech\*.ps1`.
- Coord locks held: `clients/glaztech:glaztech/domain-time` (61cd25f2), `clients/glaztech:WWW/devtools-removal` (c4226bac).
---
## Update: 19:28 PT — msdb plaintext-cred removal (Credential+Proxy) — PAUSED mid-flight (stopping for the night)
### Summary
Began removing the plaintext `glaztech\administrator` password from the **11 msdb backup-copy job steps** via the user-approved **SQL Credential + Agent CmdExec proxy** method. Phase 1 (create credential + proxy, test, snapshot, generate proposed commands) ran — but the **proxy authentication failed**. Decision on how to finish is deferred to next session. **The 11 production backup steps were NOT modified; backups run exactly as before.** User chose to leave the staged objects in place tonight (not cleaned up).
### EXACT STATE on GTI-INV-SQL (resume point)
- **11/11 backup-copy steps UNCHANGED** (still inline `net use ... /user:glaztech\administrator <pw>`); **0 converted**. Backups functional, untouched.
- **3 inert objects created in Phase 1 (NOT wired to any job, no effect on backups) — LEFT IN PLACE:**
- Credential **`glaztech-backup-share`** (IDENTITY=`glaztech\administrator`, pw encrypted) — UNUSED.
- Proxy **`ACG-BackupShareProxy`** (granted to CmdExec) — UNUSED.
- Rollback table **`msdb.dbo.acg_jobstep_backup_20260604`** (11 rows = original step defs, **contains the plaintext pw**).
- Temp job `ACG-ProxyTest` already deleted.
- **NEXT SESSION FIRST STEP:** decide method (below), then either reuse these or **DROP all 3** (`DROP CREDENTIAL [glaztech-backup-share]`; `sp_delete_proxy 'ACG-BackupShareProxy'`; `DROP TABLE msdb.dbo.acg_jobstep_backup_20260604`) to return to pristine. The snapshot table holds plaintext — drop it once we no longer need rollback.
### Key Findings
- **Embedded backup password is LIVE, not stale** — `net use \\192.168.8.52\sql_backup /user:glaztech\administrator <pw>` returned "command completed successfully"; share reachable. Current copies work.
- **Proxy auth fails with system error 1326 ("user name or password is incorrect")** despite the password being correct → a **logon-type mismatch**: `net use` = network logon (validated by the remote file server); a SQL Agent proxy does a **batch logon locally on GTI-INV-SQL**, resolving `glaztech\administrator` differently. NOT stale-pw, NOT a logon-right error (that'd be 1385).
- **The backup steps already "Executed as user: GLAZTECH\administrator"** — the **SQL Agent service account IS the domain admin**. So the simplest fix needs no stored credential at all (see decision).
- **Pre-existing bug (separate):** job **"Glaz Prod Differential (Hourly) to 8.62"** — **steps 13 & 14 FAIL** with `Incorrect syntax near 'EXEC [192.168.8.212,3436].msdb.dbo.sp_start_job N'` / `[192.168.8.52,3436]...` (broken linked-server job trigger). Backup-copy steps 11/12 succeed, but the job reports failure on 13/14. Worth investigating.
### DECISION PENDING (resume here) — how to remove the plaintext
- **RECOMMENDED: CmdExec-as-Agent.** Convert the 11 steps to CmdExec running the `copy` only (no `net use`, no creds). **Removes plaintext, stores NO password anywhere.** Works because the Agent = `GLAZTECH\administrator` (domain admin w/ share access). Generated commands already validated (copy cmds extract cleanly; **disabled step 1 → `rem` no-op**). Caveat: leans on the Agent's domain-admin privilege — but backups already depend on that today (zero regression); redo with a dedicated account + credential when the Agent is de-privileged.
- **ALT: troubleshoot the proxy** (try IDENTITY `administrator@glaztech.com` UPN, or batch-logon nuance) to keep the decoupled credential/proxy design.
- The generated new per-step commands (no secrets) are in the Phase-1 output / `C:\temp\msdb_cred_phase1.log` on WWW.
### Broader pending (Glaztech remediation backlog)
- **Rotate `glaztech\administrator`** — Mike coordinating with **Steve** (deferred). After rotation: `ALTER CREDENTIAL`/cmdkey or re-do CmdExec auth as needed.
- After plaintext removal → **disable `xp_cmdshell`** (was gated on these jobs).
- **Disable TLS 1.0/1.1 on WWW** (needs reboot); **full web-root `Everyone` sweep** (static, low pri); **seize/clean Schema-master FSMO off dead NS4**; **de-privilege the SQL Agent account**; investigate the hourly-job steps 13/14 bug.
### Reference
- Scripts (this machine): `C:\Users\guru\AppData\Local\Temp\grok_glaztech\{fix_msdb_phase1, diag_netuse, fix_www_acl2, sa_via_tom, fix_pdc_time, fix_dc1_time, fix_member_time, sched_webconfig, fix_apply}.ps1`.
- On GTI-INV-SQL (reach via WWW `tom` connection from `D:\web\glaztech_4\Web.config`, server `192.168.8.62`): cred `glaztech-backup-share`, proxy `ACG-BackupShareProxy`, table `msdb.dbo.acg_jobstep_backup_20260604`.