mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-10-26 06:58:12 +01:00
Integrate the reviewed recovery and native probe batch
This commit is contained in:
commit
8fed328442
36 files changed
+2077
-2
No files matched your search
@@ -0,0 +1,34 @@
|
||||
# Fixed local CAPI2 certificate-chain probe
|
||||
|
||||
`capi2-probe` measures one built-in source on Windows Server 2022/2025: an offline native chain build for a newly generated ephemeral self-signed certificate, followed by one matching CAPI2 Operational event 11. The expected chain outcome is an untrusted root. Success means that this local operation and event were observed; it does not mean that the certificate is trusted.
|
||||
|
||||
```powershell
|
||||
./WELA.ps1 capi2-probe -Capi2ProbeAction Plan
|
||||
./WELA.ps1 capi2-probe -Capi2ProbeAction Run -Capi2ProbeOutputPath C:\Evidence\new-capi2-probe
|
||||
```
|
||||
|
||||
Plan reads prerequisites and creates no files. Run requires a new directory on a local fixed drive; its evidence directory blocks inherited broad access. Use an existing token that can read `Microsoft-Windows-CAPI2/Operational`. The channel must already be enabled. Winmgmt, CryptSvc and EventLog must already be running; observing the host or building the chain may not implicitly start them. The probe does not change channel configuration, audit policy, services, certificate stores, trust settings or reader permissions. Existing native-channel configuration commands remain separate.
|
||||
|
||||
Run launches the same PowerShell executable in a fresh worker with a parent-generated nonce and a twenty-second process deadline. The worker creates an unnamed ephemeral Microsoft Software Key Storage Provider RSA-2048 key, signs an in-memory certificate with `CN=WelaCapi2Probe_<nonce>` and a ten-minute validity interval, then calls `CertGetCertificateChain` once. The certificate has no AIA, CRL or other extensions. The key is disposed and never exported; the retained PEM/DER contains only the public certificate.
|
||||
|
||||
The fixed native flags are `0x80002104`: `CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL`, `CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY`, `CERT_CHAIN_DISABLE_AIA` and `CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE`. No revocation-check request, additional store, custom trust engine or end-certificate caching is selected. These per-call flags prevent network retrieval by the chain operation; no machine-wide network or trust policy is altered.
|
||||
|
||||
Evidence must agree on the actual worker PID, caller SID/logon/group context, before/after token observations, generated DER/subject/thumbprint/SHA-256 and nonce. A native precise UTC interval surrounds the chain call; the event must fall within those exact inclusive bounds and after the observed channel record boundary. The matcher requires provider GUID, channel, event11 version0, native task/opcode/keywords, source computer, security SID, certificate references, offline flags, one certificate element and the expected untrusted-root result. Incomplete, ambiguous, capped or changed-context evidence remains `Unverified` and exits nonzero.
|
||||
|
||||
Collection waits up to 15 seconds by default (`-Capi2ProbeTimeoutSeconds 1..30`), queries at most 64 candidates and requires exactly one match. The bundle retains before/after context, worker operation including public certificate DER, public certificate PEM, raw matched event XML and artifact hashes. Failed matching retains up to four bounded candidate XML records. These local hashes detect altered artifacts; they are not a remote attestation or signed chain of custody.
|
||||
|
||||
This probe grants no ready-rule credit. It does not exercise TLS, remote connections, revocation retrieval, certificate enrollment, WEF delivery, the existing CAPI2 pack's event70 mapping, a Sigma rule or backend translation. Issues #386 and #367 have broader remaining acceptance criteria. Sysmon and external telemetry are excluded.
|
||||
|
||||
## Validation
|
||||
|
||||
`tests/Capi2Probe.Tests.ps1` validates certificate binding, native-result constraints, prerequisite guards, exact XML source/field checks and UTC boundaries with portable fixtures. `tests/Capi2Probe.Cli.Tests.ps1` checks public option isolation. Synthetic fixtures do not prove Windows telemetry.
|
||||
|
||||
`tests/Capi2Probe.Windows.Tests.ps1 -AllowDisposableChannelWrite` is restricted to opted-in disposable GitHub-hosted standalone Server 2022/2025. It invokes three independent public probes under Windows PowerShell 5.1 and PowerShell 7. Only the fixture may temporarily enable CAPI2; it retains the original and restored channel configuration, checks CurrentUser/LocalMachine My, Root and CA inventories, preserves all probe bundles and writes cleanup evidence even on failure. Native results must be assessed from the current workflow artifacts. The first complete native checkpoint at `a5f674e` passed all four matrix jobs with 40 assertions and three independent public probes per job ([workflow evidence](https://github.com/Shirofune-Security/WELA/actions/runs/35580490435)). All twelve public certificate identities, sixty artifact hashes and four original/restored channel and selected-store inventories were independently checked.
|
||||
|
||||
## Microsoft API references
|
||||
|
||||
- [CertGetCertificateChain flags and ownership](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certgetcertificatechain)
|
||||
- [CERT_CHAIN_PARA](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_para), [CERT_CHAIN_CONTEXT](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_context), [CERT_SIMPLE_CHAIN](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_simple_chain) and [CERT_TRUST_STATUS](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_trust_status)
|
||||
- [Unnamed CngKey creation is ephemeral](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cngkey.create)
|
||||
- [CertificateRequest.Create with a signature generator](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.certificaterequest.create)
|
||||
- [GetSystemTimePreciseAsFileTime](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime)
|
||||
@@ -0,0 +1,32 @@
|
||||
# Reviewed event-log size and retention recovery
|
||||
|
||||
`eventlog-recovery` restores the size and retention mode immediately before one completed WELA profile operation. It supports administrative and operational channels in the bundled event-log profiles on reviewed Windows 11 / Server 2022 and 2025 builds. This is part of issues #379 and #365; it does not recover records already lost.
|
||||
|
||||
Use the original `before.jsonl` and final results from `configure-eventlogs` or the same profile helper used by `configure`. The selected result must be `Applied`, with both the initial and `ImmediatePreWrite` journal entries and matching final `BeforeWrite`. Failed, overridden, incomplete, legacy scalar writes and unexplained changes require manual investigation. Other journaled controls are not restored.
|
||||
|
||||
```powershell
|
||||
./WELA.ps1 eventlog-recovery -EventRecoveryJournalPath C:\Evidence\original\before.jsonl `
|
||||
-EventRecoveryOriginalResultsPath C:\Evidence\original-results.json `
|
||||
-EventRecoveryLog ForwardedEvents -EventRecoveryOutputPath C:\Evidence\recovery-plan
|
||||
|
||||
# Inspect plan.json: current and original sizes, modes, channel guard and consent flags.
|
||||
# Supply the exact PlanHash shown by Plan after reviewing that file.
|
||||
./WELA.ps1 eventlog-recovery -EventRecoveryAction Restore `
|
||||
-EventRecoveryPlanPath C:\Evidence\recovery-plan\plan.json `
|
||||
-EventRecoveryPlanHash '<reviewed SHA256>' -EventRecoveryOutputPath C:\Evidence\recovery-run `
|
||||
-EventRecoveryAllowShrink -EventRecoveryAllowRetentionChange
|
||||
```
|
||||
|
||||
The last two switches are separate consent for the effects actually identified by the plan. Omit them when inapplicable. **Shrinking can discard existing events.** Changing to Circular allows older records to be overwritten; changing to Retain can discard incoming records when full; leaving AutoBackup stops automatic archival. Review storage and recovery requirements before consenting. Plan writes review evidence but changes no Windows settings. Restore does not export or clear logs, restore an archive, alter channel enablement/ACL/path/provider settings or restart services.
|
||||
|
||||
Each output must be a fresh directory on a local fixed drive, with an existing parent. Evidence is protected for the current operator, Administrators and SYSTEM. The plan is bound to the actual current host/MachineGuid, operator logon, original input bytes and implementation/catalog hashes. Use the same checkout and elevated operator logon for Restore. Winmgmt and EventLog must already be running; host observations use the existing reviewed-build gate. The original version-1 journal records only historical ComputerName: current host bindings and hashes do not authenticate that history.
|
||||
|
||||
The plan is rebuilt from original evidence on Restore. Minimum-size writes are checked against the immediate-prewrite size so an independent increase during prompting is preserved. An unexplained larger final size is refused. Current size/mode/enable state must match the confirmed post-configuration state. Current channel path, ACL, isolation, type, owning provider and classic-log flag are captured when planning and must remain unchanged. Live event count and EVTX file allocation are intentionally not treated as configuration guards.
|
||||
|
||||
Restore flushes a Pending receipt before one fixed local `wevtutil sl` operation, rechecks the inputs and current channel, changes only the required size/mode arguments, and records final native readback. There is no atomic compare-and-set in this interface. A concurrent policy refresh or writer can still intervene, and verified values do not prove persistence.
|
||||
|
||||
`RestoredAndVerified` means the requested size/mode and preserved configuration matched during readback. `Refused` means no native write was attempted. `RestoreAttemptedUnverified` means a write may have partly succeeded; inspect the Pending receipt and any after-state evidence before further action. Automatic rollback and replay against the already-restored state are refused. A fatal evidence-write error may leave only a Pending receipt; keep it for investigation.
|
||||
|
||||
The Windows fixture uses genuine public configuration of the disposable runner's ForwardedEvents channel, then public planning, consent refusal, actual drift refusal, restoration and replay refusal. It restores the original channel configuration and compares every original audit mask. Matrices cover Server 2022/2025 and PowerShell 5.1/7; the test proves configuration behavior, not historical record preservation, achieved retention, forwarding or Sigma readiness. Sysmon is excluded.
|
||||
|
||||
Reference: [Microsoft wevtutil size, retention and auto-backup options](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/wevtutil).
|
||||
@@ -0,0 +1,26 @@
|
||||
# Native local failed-logon probe
|
||||
|
||||
`failed-logon-probe` checks whether one fixed native local authentication failure can be correlated with its Security4625 event. It does not enable auditing. Sysmon is outside this workflow.
|
||||
|
||||
```powershell
|
||||
.\WELA.ps1 failed-logon-probe
|
||||
.\WELA.ps1 failed-logon-probe -FailedLogonAction Run -FailedLogonOutputPath C:\Evidence\failed-logon-01
|
||||
```
|
||||
|
||||
The default Plan reads prerequisites and the actual latest Security record. It creates no files and makes no authentication attempt. Run requires a new private output directory and an elevated, unimpersonated 64-bit primary token. Existing Logon failure auditing, DWORD1 `SCENoApplyLegacyAuditPolicy`, an enabled/readable Security channel and running native observation/authentication services are prerequisites. Reviewed Windows11 and Server2022/2025 clients, standalone and member servers are accepted. Domain controllers are excluded because their account database is the domain database; native CI covers disposable workgroup Server2022/2025 under PowerShell5.1 and7, not client/domain policy variants.
|
||||
|
||||
Run generates a20-character account name from a fresh GUID and calls `NetUserGetInfo` against the local database. Only exact `NERR_UserNotFound` permits the next step. A fixed native `LogonUserW` call uses domain `.` (local account database only), network logon type3 and the NTLM provider2. The actual local native event identifies its package as `MICROSOFT_AUTHENTICATION_PACKAGE_V1_0`, which the matcher requires exactly; the provider choice does not imply that the XML field is the literal `NTLM`. A fixed public dummy string is not a real credential. There is exactly one attempt, with no retry, account creation, remote target or user-selected credential. The expected native result is failure1326. Any unexpected success closes the returned token without using it and remains unverified. The worker never impersonates.
|
||||
|
||||
The worker uses the current PowerShell executable and process-only execution-policy Bypass to load its fixed script. It has a20-second process bound. The optional `-FailedLogonTimeoutSeconds 1..30` controls event-delivery polling only; it never repeats authentication. Precise native UTC timestamps bound the actual authentication call without padding. A fresh Security record boundary, worker process/path, caller SID/logon session, exact generated account/domain, logon type/provider, and failure status/substatus must match exactly one provider/version0 Security4625. Provider schema differences, missing events, duplicate matches, denied reads, caps, token changes, policy/source/host/channel drift and a backwards record boundary remain unverified.
|
||||
|
||||
Evidence includes a durable `intent.json` before launching, the native receipt, before/after observations, exact raw XML, and a manifest with SHA256 artifact hashes. A timeout or failed receipt leaves the intent so an operator can see that an attempt may have occurred; absence of a successful report does not establish that no attempt happened. Record boundaries and hashes detect selected inconsistencies; they are not a tamper-proof log-continuity or machine-attestation mechanism. Keep the entire directory together. No policy, channel, service, account or trust configuration is written by the product.
|
||||
|
||||
The observed result proves this one local nonexistent-account failure only. It does not prove remote or domain authentication, real-account password failures, lockout handling, forwarding, SIEM parsing, every failed-logon variant or Sigma rule readiness. `ReadyRuleCredit` remains0. The attempt creates expected authentication telemetry and may be visible to local monitoring.
|
||||
|
||||
## Validation and references
|
||||
|
||||
Portable fixtures test exact100ns time boundaries, account/status/process/token mismatches, malformed XML, duplicate events, source drift, caps, failures and public CLI guards. The native workflow prepares only the disposable fixture's Logon failure mask and audit precedence, executes two independent public runs, validates actual4625 XML/receipt hashes, verifies unchanged local accounts and restores all59 audit masks and the original typed precedence.
|
||||
|
||||
Microsoft documents the local-domain behavior and native return contract in [LogonUserW](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonuserw), account lookup in [NetUserGetInfo](https://learn.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netusergetinfo), and the event fields/statuses in [4625: An account failed to log on](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4625).
|
||||
|
||||
The local package behavior is described in Microsoft's [MSV1_0 authentication package documentation](https://learn.microsoft.com/en-us/windows/win32/secauthn/msv1-0-authentication-package). Exact event-package spelling is additionally verified from retained native XML.
|
||||
@@ -60,6 +60,8 @@ One configuration control journals the original typed machine/current-user value
|
||||
|
||||
`Applied`/`AlreadyCompliant` mean the machine policy and directory observations passed these checks. They do not prove transcript generation or access for another identity. `Failed` covers read/write problems, unsafe/unknown destination state and verification errors; `Overridden` covers later detected policy drift. `Skipped` includes dry runs and operator-declined changes. Exit 0 means no failed or overridden controls, including runs with skips; it is not a transcript-generation or CIS-wide compliance result.
|
||||
|
||||
For a completed `Applied` local-directory configuration, [transcription-recovery](transcription-recovery.md) provides reviewed typed restoration with durable receipts and explicit temporary-suspension consent. Failed/partial configuration runs and unsupported original values still require manual review.
|
||||
|
||||
If a later write fails, an earlier `OutputDirectory` write can remain. Review `before.jsonl`, the current policy and the authoritative GPO/MDM source. To recover, restore **only** `OutputDirectory` and `EnableTranscripting` from `Before.Policy[0].Machine`, preserving each original value's registry type; remove a value when its original `ValueExists` was false. If necessary, temporarily set `EnableTranscripting` to DWORD `0` while restoring the previous location, then restore its original value/type or absence last. Leave invocation-header and unrelated values untouched. Remove a newly created `Transcription` key only if it was originally absent and is still empty; do not delete a whole policy subtree or restore old ACLs over later changes. The journal contains policy paths/security information and should be protected as administrator recovery data.
|
||||
|
||||
## Evidence and limits
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Recover Windows PowerShell transcription policy
|
||||
|
||||
`transcription-recovery` reviews and restores the two machine values changed by one completed `powershell-transcription -TranscriptionAction Configure` run. It requires that run's original `before.jsonl` and final JSON result, exactly one `Applied` control named `PowerShellTranscription/CisV4L2`, and current policy/directory observations that still match its final `After` evidence. Status, control, target and registry-type discriminators require actual strings; schema and outcome counters require integers. Boolean values cannot stand in for those fields. Failed, partial, skipped and already-compliant configuration records require manual review.
|
||||
|
||||
```powershell
|
||||
./WELA.ps1 transcription-recovery -TranscriptRecoveryAction Plan `
|
||||
-TranscriptRecoveryJournalPath C:\Recovery\original\before.jsonl `
|
||||
-TranscriptRecoveryOriginalResultsPath C:\Recovery\original-result.json `
|
||||
-TranscriptRecoveryOutputPath C:\Recovery\new-plan
|
||||
|
||||
# Review every step in plan.json, including RequiresTemporarySuspension.
|
||||
$reviewedHash = (Get-FileHash C:\Recovery\new-plan\plan.json -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
./WELA.ps1 transcription-recovery -TranscriptRecoveryAction Restore `
|
||||
-TranscriptRecoveryPlanPath C:\Recovery\new-plan\plan.json `
|
||||
-TranscriptRecoveryPlanHash $reviewedHash -DryRun `
|
||||
-TranscriptRecoveryAllowTemporarySuspension
|
||||
|
||||
./WELA.ps1 transcription-recovery -TranscriptRecoveryAction Restore `
|
||||
-TranscriptRecoveryPlanPath C:\Recovery\new-plan\plan.json `
|
||||
-TranscriptRecoveryPlanHash $reviewedHash `
|
||||
-TranscriptRecoveryOutputPath C:\Recovery\new-attempt `
|
||||
-TranscriptRecoveryAllowTemporarySuspension -Auto
|
||||
```
|
||||
|
||||
Omit `-TranscriptRecoveryAllowTemporarySuspension` when the reviewed plan does not require it. `-Auto` accepts the ordinary confirmation; it never supplies suspension consent. `DryRun` validates all bindings and consent, returns the proposed steps and creates no directory. Plan and real Restore require new private output directories. All evidence and transcript directory paths must be literal absolute paths on local fixed drives. UNC paths, mapped drives, alternate streams and observed reparse components are rejected.
|
||||
|
||||
## Supported restoration and ordering
|
||||
|
||||
The target is the existing shared machine registry key `HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription`. The command supports original `OutputDirectory` REG_SZ values or absence, and original `EnableTranscripting` DWORD `0`, DWORD `1`, or absence. Other original types and values require manual recovery. Both Registry64 and Registry32 must agree. Recovery retains the existing key, removes only values that were originally absent, and never deletes policy subtrees.
|
||||
|
||||
When the original enablement was DWORD `0`, recovery restores that disabled state before changing the destination. If a destination change is followed by restoring DWORD `1` or removing the enablement value, the plan requires explicit temporary suspension: write DWORD `0`, restore the destination, then restore the original enablement or absence. An originally absent destination is supported only with original DWORD `0`; enabled/default-user destinations require manual recovery.
|
||||
|
||||
**Temporary suspension can leave machine transcription disabled.** By supplying `-TranscriptRecoveryAllowTemporarySuspension`, you accept that a write error, drift refusal or terminated process after the disable step and before final restoration can leave `EnableTranscripting=0`, even when the recovery target enables transcription. There is no automatic rollback or re-enable. A handled failure reports an incomplete attempt and stops subsequent writes; a terminated process may leave only pending/confirmed receipts without a final result. Inspect those receipts and the current native policy, verify the destination, and manually recover the intended enablement before relying on automatic transcription again. Do not re-enable blindly with an unverified destination.
|
||||
|
||||
Computer policy takes precedence over user policy, and policy-enabled transcription applies to PowerShell sessions. Removing a machine value can expose user/default policy; the command restores the recorded registry state without asserting session adoption. Manual `Start-Transcript` remains possible when automatic policy transcription is disabled. [Microsoft Windows PowerShell policy documentation](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_group_policy_settings?view=powershell-5.1). `HKLM\SOFTWARE\Policies` is shared across the registry views; recovery writes through Registry64 once and verifies both observations. [Microsoft WOW64 registry documentation](https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys).
|
||||
|
||||
Existing sessions are not stopped or restarted. Transcript files, their ACLs, shares, retention, collection, module logging, script-block logging, invocation-header preferences, current-user policy and all unrelated PowerShell policy values are preserved. The command inventories the other machine/current-user PowerShell policy tree with explicit bounds and stops when it changes.
|
||||
|
||||
## Evidence and failure handling
|
||||
|
||||
The reviewed plan binds original file hashes, the exact current host/MachineGuid and OS context, the elevated primary-token user/group/logon observations, current implementation hashes, both registry views, and the old/new directory observations. Restore verifies the separately supplied plan hash and independently rebuilds the plan from its original evidence and current observations. It checks those bindings after the prompt, before each write, during readback and at completion. A changed directory, policy, reader, source, plan or implementation stops the run.
|
||||
|
||||
Version-1 Configure journals record only the historical `ComputerName`. Current MachineGuid/logon/code bindings do **not** establish historical identity or authenticate supplied records. Hashes establish consistency. Keep original evidence and the reviewed hash under administrator control, review the authoritative GPO/MDM policy separately, and do not treat local registry restoration as proof of policy ownership or persistence.
|
||||
|
||||
Each mutation has a flushed, new `NNN-pending.json` receipt written before it and a separate `NNN-confirmed.json` only after verified readback. `result.json` contains actual observed final policy and confirmed steps. A pending receipt without confirmation is an uncertain step; inspect current native policy and preserve all receipts before manual recovery. A write may have succeeded even when its readback/receipt failed. Failed attempts and replay after a completed restore are refused by the original final-state guard; this command does not resume partial attempts or accept a new baseline silently. Failure to persist the result fails outward while existing evidence remains.
|
||||
|
||||
These are bounded point-in-time checks, not an atomic registry/filesystem lock. Another administrator or policy refresh may change state after a check. Private output guards observe ACL and directory identity metadata; they do not provide adversarial filesystem locking or central storage authorization proof.
|
||||
|
||||
## Validation scope
|
||||
|
||||
Portable tests exercise typed restoration, absent values, ordering, consent, preview, unsupported history, duplicate JSON, plan/source/host/directory/policy drift, prompt-time races and partial failures. The explicitly gated disposable native matrix targets Server 2022/2025 with Windows PowerShell 5.1 and PowerShell 7 as WELA hosts. It performs actual public Configure/Plan/Restore, checks native typed values and preserved policy, captures receipts, tests real drift refusal, and restores the fixture's exact original policy in `finally`. A fresh Windows PowerShell 5.1 session checks a benign transcript marker at the restored private local destination. Artifacts retain that fixture evidence and `cleanup.json`; PowerShell 7 remains only a WELA host.
|
||||
|
||||
`Restored` means the selected typed registry values passed final verification. Production transcript generation, existing/future session behavior, other identities, client/DC roles, central read/modify authorization, collection and retention remain separate validation. The report grants `SigmaEvtxCredit=0`; transcript text is separate from 4103/4104 EVTX. This advances recovery for [issue #376](https://github.com/Yamato-Security/WELA/issues/376) without completing its central authorization/ingestion acceptance.
|
||||
Reference in new issue
Block a user