10 KiB
User
- User: Howard Enos (howard)
- Machine: Howard-Home
- Role: tech
Session Summary
The session focused on verifying and extending the GuruScan pipeline for the GuruRMM platform. The GURUSCAN_RESULT_JSON pipeline was confirmed end-to-end: the launcher script reads results.json after the scan, compresses it into a single-line JSON, and emits it as GURUSCAN_RESULT_JSON:<json> to stdout. The GuruRMM agent captures this in command.stdout. This had been implemented in a prior session and was verified working with scan 2c531967.
AdwCleaner integration was thoroughly tested and found to be incompatible with both GuruRMM dispatch contexts. Dispatching in user_session context failed with ERROR_CANCELLED — the WTS token from WTSQueryUserToken is a non-elevated standard user token, and AdwCleaner's manifest requests administrator; CreateProcess returns the error immediately without showing a UAC dialog. Dispatching in system context caused the process to hang indefinitely before the scan started — AdwCleaner initializes its GUI framework before accepting CLI args, and in Session 0 (SYSTEM, no window station) that initialization blocks forever. Both paths were tested directly against the VM. AdwCleaner was removed from scanners.json pending a future schtasks InteractiveToken + HighestAvailable dispatch mechanism.
A critical 3-hour pipe hang bug was discovered via a background task notification. A full GuruScan command dispatched at 05:51 UTC (scan RMM-TEST-MACHIN-20260526-225146) completed its scan work in 6.5 minutes — results.json was written at 05:58 — but the GuruRMM command stayed running for 3 hours until the server-side reaper killed it. Root cause: NoNewWindow = [bool]$Headless in Invoke-ScanPass caused all scanner processes to share the parent PowerShell's console when running headless. Sharing the console means child processes inherit the parent's stdout/stderr pipe handles. AdwCleaner started (or partially started) in Session 0, inherited the pipe handle, then hung. After PowerShell exited, AdwCleaner still held the write end of the stdout pipe open, so the GuruRMM agent's read side never saw EOF and waited indefinitely.
The fix was to replace NoNewWindow = [bool]$Headless with WindowStyle = 'Hidden' when $Headless is set. This gives each scanner its own window/console, preventing pipe handle inheritance entirely. Any scanner that hangs will be killed by Wait-ProcessWithTimeout at its configured timeout; the overall GuruScan process exits normally regardless. Verification scan 84059ee7 (dispatched post-fix) completed in 7.5 minutes with EICAR test file detected and removed by Emsisoft, GURUSCAN_RESULT_JSON captured in stdout, no pipe hang. Documentation was updated to reflect the 3-scanner chain and the corrected headless behavior description.
Key Decisions
-
Removed AdwCleaner from scanners.json: AdwCleaner needs elevated token AND interactive desktop simultaneously.
systemcontext provides elevation but no desktop (Session 0).user_sessionprovides desktop but a non-elevated WTS token. Neither context satisfies both requirements. Will be re-added when aschtasksInteractiveToken + HighestAvailabledispatch path is implemented in GuruScan.psm1. -
WindowStyle = 'Hidden'instead ofNoNewWindow:NoNewWindowshares the parent console and causes child processes to inherit the PowerShell pipe handles.WindowStyle = 'Hidden'creates an independent process with its own window/console — no handle inheritance. The only visible behavioral difference is that scanners no longer write their output to the RMM agent's pipe (which was never used anyway — GuruScan collects output via log files). -
AdwCleaner re-add path is schtasks with
InteractiveToken: From SYSTEM context, create a task XML with<LogonType>InteractiveToken</LogonType>and<RunLevel>HighestAvailable</RunLevel>. This creates the process as the currently-logged-in user at their highest privilege level, in their desktop session. If the user is a local admin this gets an elevated token with a visible window — no UAC prompt because Task Scheduler handles the elevation internally. -
Kept AdwCleaner exit-code mappings in GuruScan.psm1: The
Get-ExitCodeThreatsandGet-ExitCodeRebootfunctions retain AdwCleaner cases. No harm in keeping them, and they'll be needed when AdwCleaner is re-added.
Problems Encountered
-
AdwCleaner
user_sessiondispatch:ERROR_CANCELLED: PowerShell's&operator usesCreateProcess, which cannot handle elevation requests from a non-elevated token. ReturnsERROR_CANCELLED(0x800704C7) immediately, no UAC dialog. Resolution: documented as architecture limitation; usingStart-Process -Verb RunAswould allow the UAC prompt to appear on the user's desktop, but requires user interaction. Removed AdwCleaner for now. -
AdwCleaner
systemcontext: process hung indefinitely: Even withWindowStyle = 'Hidden'(tested directly), AdwCleaner enters its GUI initialization loop before processing CLI args. In Session 0 there is no window station; the initialization blocks forever. Confirmed by checking VM process list and scan logs — no[S01].txtcreated during today's system-context attempts, and the only existing log ([S00].txtfrom 2026-05-26) was from a prior non-SYSTEM run. Resolution: same as above. -
3-hour pipe hang on GuruRMM command
0d05681f: Background monitor reported the command ran for 180 minutes before server-side reaper. Scan itself completed in 6.5 min (verified via results.json timestamps). Root cause:NoNewWindow = [bool]$Headlesscaused pipe handle inheritance; AdwCleaner (or a residual process from it) held the write end of the stdout pipe after PowerShell exited. Fix:WindowStyle = 'Hidden'. Verified with next scan (84059ee7), completed in 7.5 min with no hang. -
Vault path resolution for JWT token: First attempt used
projects/gururmm/api.sops.yamlwhich doesn't exist. Correct path isinfrastructure/gururmm-server.sops.yaml, fieldcredentials.gururmm-api.admin-password. Used login endpoint/api/auth/loginwithclaude-api@azcomputerguru.comto obtain JWT.
Configuration Changes
projects/msp-tools/guru-scan/GuruScan.psm1— 5 changes:NoNewWindow = [bool]$Headless→WindowStyle = 'Hidden'when$Headless(pipe hang fix)Headlessparam docstring corrected ("WindowStyle=Hidden", not "NoNewWindow")- Whitelist comment: removed AdwCleaner ("Emsisoft and HitmanPro only")
$defenderExclusions: removed'C:\AdwCleaner'Invoke-Remediationexample:AdwCleaner,MSERT→Emsisoft,MSERT
projects/msp-tools/guru-scan/scanners.json— AdwCleaner entry removed entirelyprojects/msp-tools/guru-scan/README.md— full rewrite for 3-scanner chain; added GuruRMM integration section withGURUSCAN_RESULT_JSONstructure; fixed Headless description; removed AdwCleaner from all tables; added note on AdwCleaner re-add path
Credentials & Secrets
No new credentials created or discovered. GuruRMM API access used existing vault entry:
- Vault:
infrastructure/gururmm-server.sops.yaml→credentials.gururmm-api.admin-email/admin-password - Login endpoint:
POST http://172.16.3.30:3001/api/auth/login
Infrastructure & Servers
- GuruRMM server:
172.16.3.30:3001 - RMM-TEST-MACHINE: agent ID
7d3456f5-d811-4eac-8629-d88e9d1c594a, VM at172.20.12.192, online - HTTP deploy server:
172.20.0.1:8888(Python, servingC:/claudetools/projects/msp-tools/guru-scan/)
Commands & Outputs
# GuruRMM dispatch (system context, post-fix)
C:\GuruScan\Invoke-GuruScan.ps1 -Headless
# → completed in 7.5 min, GURUSCAN_RESULT_JSON captured, no pipe hang
# → EICAR test file C:\Users\User\Downloads\eicar.com_.txt detected by Emsisoft, removed
Verify fix deployed on VM:
Get-Content 'C:\GuruScan\GuruScan.psm1' | Select-String 'WindowStyle|NoNewWindow'
# Output: WindowStyle = 'Hidden' (no NoNewWindow line in scanner launch block)
Scan result from verification run (command 84059ee7):
{
"scan_id": "RMM-TEST-MACHIN-20260527-015912",
"total_threats": 1,
"scanners": [
{ "name": "RKill", "status": "completed", "exit_code": 1, "duration_min": 0.09 },
{ "name": "AdwCleaner","status": "SKIPPED (requires user session)" },
{ "name": "Emsisoft", "status": "completed", "exit_code": 1, "duration_min": 4.86 },
{ "name": "HitmanPro","status": "completed", "exit_code": 0, "duration_min": 2.25 }
]
}
(AdwCleaner was still in scanners.json at time of this scan; removed immediately after.)
Pending / Incomplete Tasks
- AdwCleaner re-add via schtasks
InteractiveToken: Implement inGuruScan.psm1. Whensession0_compatible: falseand running as SYSTEM, instead of skipping, create a task XML with<LogonType>InteractiveToken</LogonType>+<RunLevel>HighestAvailable</RunLevel>, register + trigger, poll for log file as completion signal, unregister. No GuruRMM agent changes required. - GuruRMM dashboard UI: Parse
GURUSCAN_RESULT_JSONfromcommand.stdoutand display per-scanner results on the agent detail page. Separate GuruRMM feature. - GURUSCAN_RESULT_JSON when SYSTEM detection causes old version mismatch: The 2026-05-26 22:51 scan showed AdwCleaner as
FAILED(notSKIPPED), suggesting the VM had a module version without thesession0_compatiblecheck at that time. Monitor for recurrence; current deployed version is correct.
Reference Information
- Commits this session:
a980ef1— fix: use WindowStyle=Hidden instead of NoNewWindow in headless scanner dispatcha655b52— chore: remove AdwCleaner from scanner chain47517e9— docs: update GuruScan README and module comments for current state
- GuruRMM API base:
http://172.16.3.30:3001/api - GuruRMM login endpoint:
POST /api/auth/login(body:email,password) - Test machine agent ID:
7d3456f5-d811-4eac-8629-d88e9d1c594a - Scan log dir:
C:\ScanLogs\on VM - EICAR test file on VM:
C:\Users\User\Downloads\eicar.com_.txt - AdwCleaner CLI reference: https://help.malwarebytes.com/hc/en-us/articles/31589247152027-Command-line-options-for-AdwCleaner
Wait-ProcessWithTimeoutin GuruScan.psm1:69 — timeout-kills processes exceedingtimeout_minTest-RunningAsSystemin GuruScan.psm1:684 — usesWindowsIdentity.IsSystem