From 793abffc07e5120341f04d2f101a7aa16b047b03 Mon Sep 17 00:00:00 2001 From: FeelTheFonk Date: Thu, 19 Feb 2026 15:05:26 +0100 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20final=20polish=20=E2=80=94=20handle?= =?UTF-8?q?=20leak,=20double-log,=20exit=20codes,=20summaries,=20checksums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix(acl): add RegKey.Close() before second OpenSubKey in Grant-RegistryAccess (handle leak) - fix(log): Start-Transcript now writes to killSlop_transcript.txt (separate from structured log) - fix(ci): CI now triggers on push to dev AND main for immediate feedback - fix(prepare): all error Exit → Exit 1; user-cancel → Exit 0; platform (23H2/24H2) → 24H2 - fix(kill): add comment explaining SilentlyContinue; add SYNC-REQUIRED comment on service list - feat(verify): human-readable StartType mapping (Disabled/Manual/Automatic); summary stats (X/11 DISABLED) - fix(verify): PS5.1-compatible StartType mapping (no ?? operator); SYNC-REQUIRED comment - fix(release): add SHA256 checksum generation + attach .sha256 to GitHub Release - fix(settings): add rationale comments for excluded rules - fix(gitignore): artefacts → artifacts; add *.tmp, *~, .env, *.secret - fix(claude): GPO count 12 vals → 10 vals (factual); update staging dir note Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 11 ++++++++++- .gitignore | 8 +++++++- 1_prepare_safemode.ps1 | 14 +++++++------- 2_kill_defender.ps1 | 8 +++++++- 3_verify_status.ps1 | 25 ++++++++++++++++++++++--- CLAUDE.md | 4 ++-- PSScriptAnalyzerSettings.psd1 | 2 ++ 8 files changed, 58 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3bbe16e..658a840 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: branches: [ "main" ] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc07531..2cb33e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,6 +45,13 @@ jobs: zip -r ../killSlop_${{ steps.tag_version.outputs.new_tag }}.zip . cd .. + - name: Generate SHA256 Checksum + if: steps.tag_version.outputs.new_tag + run: | + sha256sum killSlop_${{ steps.tag_version.outputs.new_tag }}.zip \ + > killSlop_${{ steps.tag_version.outputs.new_tag }}.zip.sha256 + cat killSlop_${{ steps.tag_version.outputs.new_tag }}.zip.sha256 + - name: Create GitHub Release if: steps.tag_version.outputs.new_tag # Only run if a new tag was created uses: softprops/action-gh-release@v2 @@ -52,5 +59,7 @@ jobs: tag_name: ${{ steps.tag_version.outputs.new_tag }} name: Release ${{ steps.tag_version.outputs.new_tag }} body: ${{ steps.tag_version.outputs.changelog }} - files: killSlop_${{ steps.tag_version.outputs.new_tag }}.zip + files: | + killSlop_${{ steps.tag_version.outputs.new_tag }}.zip + killSlop_${{ steps.tag_version.outputs.new_tag }}.zip.sha256 token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 616221e..7c41557 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,15 @@ # Claude Code local settings (machine-specific) .claude/ -# Artifacts & Staging (never commit operational artefacts) +# Artifacts & Staging (never commit operational artifacts) *.log *.zip +*.tmp +*~ + +# Credentials & Secrets +.env +*.secret # Windows artifacts Thumbs.db diff --git a/1_prepare_safemode.ps1 b/1_prepare_safemode.ps1 index a0e7a28..2860e7e 100644 --- a/1_prepare_safemode.ps1 +++ b/1_prepare_safemode.ps1 @@ -15,7 +15,7 @@ .NOTES PROJECT: killSlop VERSION: 0.0.3 - PLATFORM: Windows 11 (23H2 / 24H2) + PLATFORM: Windows 11 24H2 #> # CONFIGURATION @@ -27,7 +27,7 @@ $StagingPath = Join-Path $StagingDir "2_kill_defender.ps1" # 0. PRIVILEGE CHECK if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Error "FATAL: Administrative privileges required." - Exit + Exit 1 } Clear-Host @@ -47,7 +47,7 @@ $UserAck = Read-Host "TYPE 'I HAVE MY PASSWORD' TO CONFIRM PRE-REQUISITES" if ($UserAck -ne "I HAVE MY PASSWORD") { Write-Warning "ABORTED: Safety interlock triggered. Code 0xUSER_CANCEL." - Exit + Exit 0 } # 1.1 SID RESOLUTION CHECK (Pre-Flight) @@ -57,7 +57,7 @@ try { Write-Host "[PASS] SID RESOLUTION CHECK: $TestGroup" -ForegroundColor Green } catch { Write-Error "FATAL: Unable to resolve Administrator SID. System Localization issue?" - Exit + Exit 1 } # 2. TAMPER PROTECTION VERIFICATION @@ -67,7 +67,7 @@ try { if ($MpStatus.IsTamperProtected -eq $true) { Write-Host "[FAIL] TAMPER PROTECTION IS ACTIVE." -ForegroundColor Red Write-Host "ACTION: Disable manually in Windows Security > Virus & Threat Protection." -ForegroundColor Red - Exit + Exit 1 } Write-Host "[PASS] TAMPER PROTECTION IS DISABLED." -ForegroundColor Green } @@ -89,7 +89,7 @@ catch { Write-Host "[INFO] DEPLOYING PAYLOAD..." -ForegroundColor Gray if (!(Test-Path $PayloadSource)) { Write-Error "FATAL: Payload source not found: $PayloadSource" - Exit + Exit 1 } if (!(Test-Path $StagingDir)) { New-Item -ItemType Directory -Path $StagingDir | Out-Null } Copy-Item -Path $PayloadSource -Destination $StagingPath -Force @@ -122,7 +122,7 @@ Write-Host "[INFO] CONFIGURING BOOT SEQUENCE (SAFEMODE_NETWORK)..." -ForegroundC if ($LASTEXITCODE -ne 0) { Write-Host "[FAIL] BCD MODIFICATION FAILED." -ForegroundColor Red - Exit + Exit 1 } Write-Host "" diff --git a/2_kill_defender.ps1 b/2_kill_defender.ps1 index 211e60b..e583387 100644 --- a/2_kill_defender.ps1 +++ b/2_kill_defender.ps1 @@ -16,6 +16,8 @@ PLATFORM: Windows 11 (Safe Mode) #> +# SilentlyContinue required: Safe Mode environment limits service/WMI availability. +# Critical operations use explicit try/catch blocks for targeted error handling. $ErrorActionPreference = "SilentlyContinue" $LogPath = "C:\DefenderKill\killSlop_log.txt" @@ -46,6 +48,7 @@ function Grant-RegistryAccess { # Take Ownership $ACL.SetOwner($Admin) $RegKey.SetAccessControl($ACL) + $RegKey.Close() # Grant Full Control $RegKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($KeyPath.Replace("HKLM:\", ""), [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, [System.Security.AccessControl.RegistryRights]::ChangePermissions) @@ -64,7 +67,8 @@ try { if (!(Test-Path (Split-Path $LogPath))) { New-Item -ItemType Directory -Path (Split-Path $LogPath) -Force | Out-Null } - Start-Transcript -Path $LogPath -Append | Out-Null + $TranscriptPath = "C:\DefenderKill\killSlop_transcript.txt" + Start-Transcript -Path $TranscriptPath -Append | Out-Null # 0. PRIVILEGE ESCALATION $Definition = @" @@ -126,6 +130,8 @@ try { # 1. SERVICE CONFIGURATION Write-KillSlopLog "Configuring Services..." "PROC" "Cyan" + # SYNC-REQUIRED: This list is mirrored in 3_verify_status.ps1. + # Any modification here MUST be reflected there and vice versa. $TargetServices = @( "WinDefend", # Antivirus Service "Sense", # Advanced Threat Protection diff --git a/3_verify_status.ps1 b/3_verify_status.ps1 index b80cc1b..5462f24 100644 --- a/3_verify_status.ps1 +++ b/3_verify_status.ps1 @@ -55,7 +55,8 @@ if ($Procs) { # 3. SERVICE CONFIGURATION AUDIT Write-Host "`n[INFO] AUDITING KERNEL SERVICE CONFIGURATION..." -ForegroundColor Gray -# Synchronized List with 2_kill_defender.ps1 +# SYNC-REQUIRED: This list is mirrored in 2_kill_defender.ps1. +# Any modification there MUST be reflected here and vice versa. $Services = @( "WinDefend", # Antivirus Service "Sense", # Advanced Threat Protection @@ -70,6 +71,11 @@ $Services = @( "SenseCncProxy" # Defender for Endpoint C&C (24H2) ) +$StartTypeMap = @{ 0 = 'Boot'; 1 = 'System'; 2 = 'Automatic'; 3 = 'Manual'; 4 = 'Disabled' } +$CountDisabled = 0 +$CountRunning = 0 +$CountMissing = 0 + foreach ($SvcName in $Services) { $Svc = Get-Service -Name $SvcName -ErrorAction SilentlyContinue @@ -82,7 +88,10 @@ foreach ($SvcName in $Services) { try { # Direct Registry Query for Truth regarding Start Type $RegStart = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\$SvcName" -ErrorAction Stop).Start - $StartType = if ($null -ne $RegStart) { $RegStart.ToString() } else { "MISSING" } + if ($null -ne $RegStart) { + $mapped = $StartTypeMap[[int]$RegStart] + $StartType = if ($null -ne $mapped) { $mapped } else { "UNKNOWN($RegStart)" } + } else { $StartType = "MISSING" } } catch { $StartType = "ACCESS_DENIED/MISSING" } @@ -91,14 +100,24 @@ foreach ($SvcName in $Services) { if ($Svc.Status -eq 'Running' -or ($null -ne $RegStart -and $RegStart -ne 4)) { $Color = "Red" # Failed state + if ($Svc.Status -eq 'Running') { $CountRunning++ } } elseif ($RegStart -eq 4) { $Color = "Green" # Compliance + $CountDisabled++ } + } else { + $CountMissing++ } - Write-Host (" {0,-20} | STATE: {1,-10} | START_TYPE: {2}" -f $SvcName, $StatusStr, $StartType) -ForegroundColor $Color + Write-Host (" {0,-22} | STATE: {1,-10} | START_TYPE: {2}" -f $SvcName, $StatusStr, $StartType) -ForegroundColor $Color } +$Total = $Services.Count +$SummaryColor = if ($CountRunning -gt 0) { "Red" } else { "Green" } +Write-Host "" +Write-Host ("=" * 70) -ForegroundColor Cyan +Write-Host (" SUMMARY: {0}/{1} DISABLED | {2} RUNNING (ALERT) | {3} MISSING" -f $CountDisabled, $Total, $CountRunning, $CountMissing) -ForegroundColor $SummaryColor +Write-Host ("=" * 70) -ForegroundColor Cyan Write-Host "" Write-Host "VERIFICATION CYCLE COMPLETE." -ForegroundColor Cyan Pause diff --git a/CLAUDE.md b/CLAUDE.md index 8f61baa..35a297f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -31,7 +31,7 @@ Normal Mode (Admin) Safe Mode (Networking) Normal Mode (Post-Op) - Payload staging - Registry ACL takeover - Service registry check - RunOnce* injection - Service disable (11x) - BCD safeboot set - Task disable -- Forced reboot - GPO injection (12 vals) +- Forced reboot - GPO injection (10 vals) - BCD safeboot removed - Forced reboot ``` @@ -40,7 +40,7 @@ Normal Mode (Admin) Safe Mode (Networking) Normal Mode (Post-Op) **Privilege escalation in Phase 2:** An inline C# `TokenManipulator` class is compiled at runtime via `Add-Type` to call `advapi32.dll` directly and enable `SeTakeOwnershipPrivilege`/`SeRestorePrivilege` on the process token. -**Staging directory:** `C:\DefenderKill\` — contains the staged payload and the log file `killSlop_log.txt`. +**Staging directory:** `C:\DefenderKill\` — contains the staged payload, the structured log `killSlop_log.txt`, and the raw PowerShell transcript `killSlop_transcript.txt`. ## Commit conventions diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index 3815262..9b093f0 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,4 +1,6 @@ @{ + # PSAvoidUsingWriteHost — intentional: killSlop is an interactive CLI tool; Write-Host is required for colored output. + # PSAvoidUsingPositionalParameters — intentional: used deliberately for conciseness in tightly scoped scripts. ExcludeRules = @( 'PSAvoidUsingWriteHost', 'PSAvoidUsingPositionalParameters' From 520d20e0914c35374a70699e2025df22b2fcb983 Mon Sep 17 00:00:00 2001 From: FeelTheFonk Date: Thu, 19 Feb 2026 15:11:10 +0100 Subject: [PATCH 2/2] fix(lint): replace em dashes with ASCII dashes in PSScriptAnalyzerSettings.psd1 Non-ASCII characters (U+2014 em dash) triggered PSUseBOMForUnicodeEncodedFile on PowerShell 7 (GitHub Actions CI). Replaced with standard ASCII hyphens. Lint now passes cleanly on both PS5.1 and PS7. Co-Authored-By: Claude Sonnet 4.6 --- PSScriptAnalyzerSettings.psd1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index 9b093f0..78a720e 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,6 +1,6 @@ @{ - # PSAvoidUsingWriteHost — intentional: killSlop is an interactive CLI tool; Write-Host is required for colored output. - # PSAvoidUsingPositionalParameters — intentional: used deliberately for conciseness in tightly scoped scripts. + # PSAvoidUsingWriteHost - intentional: killSlop is an interactive CLI tool; Write-Host is required for colored output. + # PSAvoidUsingPositionalParameters - intentional: used deliberately for conciseness in tightly scoped scripts. ExcludeRules = @( 'PSAvoidUsingWriteHost', 'PSAvoidUsingPositionalParameters'