mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-10-08 07:15:25 +02:00
Integrate reviewed WMI recovery with combined dev auditing batch
This commit is contained in:
commit
bfa286bf7d
46 files changed
+2449
-47
No files matched your search
+19
-1
@@ -1,6 +1,6 @@
|
||||
# Guarded audit recovery
|
||||
|
||||
Related to #365. `audit-recovery` restores **explicitly selected** advanced audit subcategories and the typed `SCENoApplyLegacyAuditPolicy` value from a completed WELA configuration journal and its matching JSON results. Sysmon is out of scope. Other journal kinds remain manual recovery tasks.
|
||||
Related to #365. `audit-recovery` restores **explicitly selected** advanced audit subcategories and the typed `SCENoApplyLegacyAuditPolicy` value from a completed WELA configuration journal and its matching JSON results. Sysmon is out of scope. The three named logging switches below are also supported. Other journal controls remain manual recovery tasks.
|
||||
|
||||
```powershell
|
||||
# Save results during the original configuration.
|
||||
@@ -22,3 +22,21 @@ Subcategory recovery requires enabled DWORD precedence. To restore precedence it
|
||||
Version-1 journals identify the historical host only by ComputerName. The review plan additionally binds the current MachineGuid and observed build/patch/join/role context. This does **not** prove historical image identity; use only your trusted original evidence. Hashes establish byte consistency, not signatures or authenticity. Reports describe point-in-time local restoration, not GPO persistence, generated events or Sigma readiness.
|
||||
|
||||
Tests cover minimum-mask truth tables, evidence/host/plan tampering, drift, ordering, partial failure, readback and idempotence. Explicitly gated disposable Server 2022/2025 CI exercises actual completed journals and exact audit-policy restoration under PowerShell 5.1/7, with independent safety restoration. Domain policy refresh and Windows 11/DC/ADCS deployment checks remain separate.
|
||||
|
||||
## Named logging DWORD recovery
|
||||
|
||||
The same Plan/Restore flow accepts exactly these additional `RecoveryControlId` values:
|
||||
|
||||
- `Registry/HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit/ProcessCreationIncludeCmdLine_Enabled`
|
||||
- `Registry/HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging/EnableScriptBlockLogging`
|
||||
- `Registry/HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging/EnableModuleLogging`
|
||||
|
||||
Each must have a matching completed `Applied` DWORD-1 write. Supported original states are DWORD 0/1 or value absence; strings, other integer values/types, incomplete writes, module-name lists, transcription settings, NTLM and arbitrary keys are refused. Recovery changes or removes only the selected value. **Existing keys are retained**, including keys created by the original configuration: `OriginalKeyExisted` reports that distinction. Missing current keys require manual review. This does not restore an entire PowerShell logging configuration or provide event/Sigma credit.
|
||||
|
||||
Planning records the native path plus bounded hashes of all other values, direct child names and owner/group/DACL. Restoration reopens existing native 64-bit HKLM SOFTWARE keys component by component without following registry links, checks those guards, then changes the selected value through the same held handle. Immediate readback and a fresh path reopen must agree. Inventories are bounded to 256 values/children, 64 KiB per value/security descriptor and 1 MiB total value data; unsupported inventories fail closed. No key, child, owner/group/DACL or SACL is intentionally modified by recovery. The guard observes owner/group/DACL, **not the SACL or descendant contents**.
|
||||
|
||||
The reviewed plan also binds current recovery implementation hashes; changed or previously loaded mismatched native code requires a new plan/process. Guards pin observations at recovery planning time; the original version-1 journal does not contain historical registry object identities or neighboring data. Native names are not durable identities. Repeated recovery reports `AlreadyRecovered` when the selected value is already at the reviewed target and guards still match, without claiming who restored it. Concurrent replacement with identical observations, change-and-change-back, and policy/admin writes cannot be excluded atomically. Use a quiet maintenance window; there is no automatic rollback after a failed post-write check.
|
||||
|
||||
Portable regressions exercise the three-value allowlist, typed/absent states, source/evidence tampering, neighboring-data drift, dry-run, receipts and idempotence. Gated native Server 2022/2025 runs under Windows PowerShell 5.1 and PowerShell 7 create real configuration journals for each switch from DWORD 0 and absence, verify value-only restoration and neighboring-data preservation, and restore the runner's original typed states. These are disposable local tests, not domain-policy persistence or Windows 11 deployment evidence.
|
||||
|
||||
Native API contracts: [RegOpenKeyEx](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexw) opens existing keys, and [RegGetKeySecurity](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-reggetkeysecurity) distinguishes owner/group/DACL access from SACL access.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Native DNS Client completion probe
|
||||
|
||||
`dns-client-probe` advances #386 with a fixed benign native DNS lookup and correlation to Windows event 3008. It does **not** implement a Sigma rule test. Sysmon is excluded. Windows DNS Client Operational logging and `Dnscache` must already be enabled/running; the command never changes DNS configuration, channel settings, audit policy or service state.
|
||||
|
||||
```powershell
|
||||
# Observe prerequisites only. Choose a resolver you are authorized to query.
|
||||
./WELA.ps1 dns-client-probe -DnsClientProbeResolver 192.0.2.53
|
||||
|
||||
# Explicit network operation; use a NEW local output directory.
|
||||
./WELA.ps1 dns-client-probe -DnsClientProbeAction Run `
|
||||
-DnsClientProbeResolver 192.0.2.53 `
|
||||
-DnsClientProbeOutputPath C:\WelaEvidence\dns-client-01
|
||||
```
|
||||
|
||||
The example address is documentation-only: replace it with an approved resolver. Plan creates no files and sends no probe lookup. Run generates exactly one application request for `wela-<random-guid>.wela.test.` type A; `.test` is reserved for DNS testing by [RFC 2606](https://www.rfc-editor.org/rfc/rfc2606.html). There is no caller-selected domain, record type or application connection to a returned address. A same-engine 64-bit worker uses synchronous `DnsQueryEx` with one explicit IPv4 DNS server, TCP port 53, recursion disabled, cache bypass, no hosts/local-name/NetBT/multicast fallback, fully qualified naming and IDN disabled. DNS retry/internal processing and normal response caching are OS behavior; this is not a promise of one wire packet, cache immutability or resolver-side enforcement. The query name, selected resolver and exact flags are retained. Only canonical unicast IPv4 literals are accepted; there is no hostname or configurable port.
|
||||
|
||||
The bounded worker has twenty seconds to finish. Parent/worker timestamps use [GetSystemTimePreciseAsFileTime](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime), with no coarse-clock fallback or positive-match time padding. Terminating the worker does not prove cancellation of DNS service or network work; timed-out completion remains unverified. The separate event wait defaults to fifteen seconds (`-DnsClientProbeTimeoutSeconds 1..30`). Native status 0 (A answers), 9003 (NXDOMAIN) and 9501 (no records) are reviewed completion outcomes. A negative response is not reported as successful name resolution. Missing events, unknown outcomes/versions/types, caps, token or configuration/source drift and incomplete reads remain `Unverified` with a nonzero exit. No setup is automatically performed to make the test pass.
|
||||
|
||||
Evidence includes observed build/patch/role, token and same-engine context, exact provider GUID, live event/version/field types and template hashes, original pinned rule hashes, channel metadata, a pre-query record boundary, bounded original worker JSON (also retained if its validation fails), worker timestamps/status/answers and hashed original matched XML. Matching requires event 3008 version 0 on **Microsoft-Windows-DNS-Client/Operational**, source computer, unique query name/type, native completion status, requested option bits, record boundary and operation time. The emitter PID is retained in original XML; it may belong to the DNS service broker, so it is not assumed to equal the requesting worker PID. This correlation does not prove exclusive request attribution, the wire destination, DNSSEC validation or absence of simultaneous unrelated events. Full caller token snapshots bracket actual query/event I/O and are compared before final metadata inventory; the worker has its own exact before/after token checks. Metadata inventories are outside this interval because DISM and channel inspection may temporarily adjust privileges. The native read is limited to this random query name, record boundary and last sixty seconds; any retained candidate outside the exact operation interval is diagnostic only. Native event-query status is retained separately from its records. Artifact hashes detect byte changes; they are not signatures or historical host authentication.
|
||||
|
||||
`PrerequisitesObserved` means only that Plan observed supported metadata. `NativeDnsLookupObserved` means that a native completion and matching local event were observed. Neither proves forwarding, downstream parsing, detection execution or retention capacity. In particular, all six pinned DNS Client rules refer to **Microsoft-Windows-DNS Client Events/Operational**, a different channel string. WELA retains that mismatch and does not rewrite it. `ReadyRuleCredit` remains **0**; there is no six-rule Sigma uplift.
|
||||
|
||||
Native acceptance uses a separately opt-in fixture on disposable GitHub-hosted workgroup Server 2022/2025 under Windows PowerShell 5.1 and PowerShell 7. The fixture refuses an existing DNS role, installs its own standalone role, creates authoritative `wela.test` with a wildcard A record to `192.0.2.1`, and queries only loopback. It temporarily enables the Client channel if needed, restores its exact original settings, checks audit policies, removes its owned zone/records and removes only newly installed DNS features. Feature removal may require VM disposal rather than a live reboot; the cleanup receipt records that boundary. Fixture setup is not part of the product. Windows 11, domain-joined/DC/ADCS hosts and external resolver/network behavior still require their own acceptance evidence.
|
||||
|
||||
The P/Invoke entry point is exactly `DnsQueryEx`, preventing [Unicode suffix probing](https://learn.microsoft.com/en-us/dotnet/standard/native-interop/specifying-a-character-set). The server-address buffer follows [Microsoft’s DNSAsyncQuery sample](https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/DNSAsyncQuery/cpp/DnsQueryEx.cpp): one element, zero aggregate family, and the sockaddr default DNS port.
|
||||
|
||||
Native API references: [DnsQueryEx](https://learn.microsoft.com/en-us/windows/win32/api/windns/nf-windns-dnsqueryex), [DNS_QUERY_REQUEST](https://learn.microsoft.com/en-us/windows/win32/api/windns/ns-windns-dns_query_request), [DNS_ADDR_ARRAY](https://learn.microsoft.com/en-us/windows/win32/api/windnsdef/ns-windnsdef-dns_addr_array), [DNS query flags](https://learn.microsoft.com/en-us/windows/win32/dns/dns-constants), and the [Microsoft Windows SDK declarations](https://github.com/microsoft/win32metadata/blob/main/generation/WinSDK/RecompiledIdlHeaders/um/WinDNS.h).
|
||||
@@ -15,10 +15,16 @@ Related to #382. `evtx-recovery` exports one validated native Security 4688 prob
|
||||
|
||||
The importer requires the exact five native-probe files, four matching hashes, strict JSON, consistent embedded metadata, all 59 typed audit masks, valid native process/event identities and unchanged source prerequisites. Imported evidence is operator supplied; hashes prove consistency, not authenticity. Export compares the live source host and policy context to the original probe and verifies the actual Security record before copying it. The exported file is reopened even when Windows reports a successful export: an empty EVTX is not success.
|
||||
|
||||
The archive stays open without write/delete sharing during hashing and native readback. Exactly one event must match the original System and EventData semantics. XML namespace/attribute order and optional RenderingInfo are handled without ignoring original fields. Empty, corrupt, denied, duplicate or changed records remain `Unverified`, as do reader/host/source changes. The report records actual archive bytes/hash, reader SID/groups/session identity, host context, source identity, query, timestamps and recovered raw XML. Readback observes the current token, not hypothetical access by a supplied SID.
|
||||
The archive stays open without write/delete sharing during hashing and native readback. Exactly one event must match the original System and EventData semantics, and the native query status must identify that exact file with a zero status code. XML namespace/attribute order and optional RenderingInfo are handled without ignoring original fields. Empty, corrupt, denied, duplicate or changed records remain `Unverified`, as do reader/host/source changes. Each recovered XML record is capped at four MiB; the archive is capped at sixteen MiB. The report records actual archive bytes/hash, reader SID/groups/logon identity, host context, source identity, query, timestamps and recovered raw XML.
|
||||
|
||||
Version 2 recovery reports contain `ReaderBefore` and `ReaderAfter` primary-token snapshots with the actual user, process, token ID, authentication/logon ID and modification ID. The native helper rejects impersonation and requires its loaded C# source to match the current file. The recorded interval starts after private output/ACL and source-policy preparation, before event access; it ends after archive hashing, native query and input-file verification. Token changes, including privilege adjustments that change the modification ID, invalidate the interval. Final host and source-policy inventory runs outside that interval because those APIs may adjust available privileges. Implementation fingerprints and private evidence hashes are checked before the final manifest. These observations are not signatures or an atomic transaction against another administrator.
|
||||
|
||||
`Verify` reads ordinary host metadata and does not require administrator-only installed-feature inventory. Run it in the intended account's own Windows session with existing read access to the unchanged probe bundle and EVTX plus permission to create its private output. `FileReadAccess=Denied` records an actual denied file-open attempt; `NativeQuery=NotAttempted` makes clear that no event query followed. An opened file records `FileReadAccess=Allowed`, while `NativeQuery=ExactEventRecovered` requires the actual Windows event API and matching event content. Native query denial is recorded separately. A later token/context/evidence failure keeps the overall report `Unverified`, even if earlier I/O observations succeeded. The event's original producer is independent of the archive reader: opening a Security EVTX does not establish access to the live Security channel, an Event Log Readers membership requirement, or access by a WEC service token. The product offers no credential, impersonation, group-membership or privilege-granting options and does not grant archive permissions.
|
||||
|
||||
`NativeEventRecovered` proves only that this recorded reader recovered this one event at the observation time. It does not establish completeness, eighteen-month retention, rollover behavior, storage capacity, other-principal access, disaster recovery or Sigma readiness. It does not archive localized message resources or clear the source log. The new archive is a probe artifact, not a full-log backup.
|
||||
|
||||
Focused fixtures exercise source/event tampering, empty/duplicate/corrupt/denied readback, drift, paths and CLI guards. Explicitly gated disposable Server 2022/2025 tests under PowerShell 5.1/7 collect a genuine 4688 event, export/reopen it, independently verify it, reject an actual empty EVTX and restore all temporary audit settings. Windows 11/DC/ADCS, alternate-reader and long-term recovery exercises remain deployment checks.
|
||||
Focused fixtures exercise source/event tampering, empty/duplicate/corrupt/denied readback, native query status, primary-token/logon/modification/host/source drift, paths and CLI guards. Explicitly gated disposable Server 2022/2025 tests under PowerShell 5.1/7 collect a genuine 4688 event, export/reopen it, independently verify it and reject an actual empty EVTX. A separate owned standard account uses two fresh primary-token logons to prove file-read denial and exact native recovery after removing a deny ACE from an owned archive copy. The account is neither an administrator nor an Event Log Readers member. Its credentials pass only through the process API, and its temporary files, outputs and ACL changes stay in the owned fixture. The test restores that file ACL, removes only the owned account, and checks all 59 original audit masks and typed registry values/absence. It requires both `-AllowDisposablePolicyWrite` and `-AllowDisposableAccount` on an ephemeral GitHub-hosted runner. Windows 11/DC/ADCS, service/network-reader and long-term recovery exercises remain deployment checks.
|
||||
|
||||
Implementation references: [Microsoft EventLogSession.ExportLog](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventing.reader.eventlogsession.exportlog) selects events without message resources; [EvtExportLog](https://learn.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtexportlog) requires a new target and can create a header-only file for an empty query.
|
||||
|
||||
[TOKEN_STATISTICS](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-token_statistics) defines token, logon and modification identities; [WindowsIdentity.GetCurrent](https://learn.microsoft.com/en-us/dotnet/api/system.security.principal.windowsidentity.getcurrent) distinguishes a process primary token from thread impersonation; [EvtQuery](https://learn.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtquery) supports local file queries independently of live channel queries.
|
||||
@@ -0,0 +1,65 @@
|
||||
# Recover one selected leaf-file audit ACE
|
||||
|
||||
`file-sacl-recovery` removes one explicit ordinary audit ACE proven to have been added by a completed `targeted-sacl` operation. It supports one existing local leaf file, selected from the installed catalog with `Inheritance=None` and without child consent. Use elevated native 64-bit PowerShell on the original host. Registry keys, directories, descendants, inherited/object/callback ACE additions, arbitrary supplied ACEs and older or source-mismatched receipts require manual review.
|
||||
|
||||
This command changes only that file's SACL. It does not restore audit policy, rewrite its DACL, stop services, alter inheritance settings, or make an event-generation/Sigma readiness claim. Sysmon is out of scope. Preserve trusted original evidence; hashes detect changes and bind the reviewed selection but do not authenticate an untrusted receipt author.
|
||||
|
||||
## Required evidence and review
|
||||
|
||||
Retain all four files from the original public selected-target operation:
|
||||
|
||||
- Its original one-target `Plan` JSON, recorded while the row was `ChangeRequired`.
|
||||
- The matching `<id>.pending.json` and `<id>.confirmed.json` under the original backup directory.
|
||||
- The successful `Configure` results JSON, with its one row marked `Applied`.
|
||||
|
||||
The original before/after snapshots must prove exactly one new explicit ordinary audit ACE for the selected principal, rights and outcomes. Every previous ACE's bytes and count must remain; owner/group, DACL bytes, control flags, resource-manager control byte and SACL revision must agree, except that the original addition may have introduced the SACL-present flag. Neither an already-covered ACE nor any additional unexplained delta grants removal authority. Original snapshots are reconstructed from their binary descriptors and checked against their reported metadata.
|
||||
|
||||
The host/context, installed catalog and original selected-operation source hashes must still match. Recovery additionally records current helper/source hashes, actual elevated operator SID/groups and machine GUID, original input hashes, full current descriptor bytes and volume/file-index/creation identity. Current state must exactly match the confirmed addition. Input JSON is strict UTF-8, rejects duplicate properties and is limited to four MiB per file.
|
||||
|
||||
```powershell
|
||||
.\WELA.ps1 file-sacl-recovery `
|
||||
-FileSaclRecoveryOriginalPlanPath C:\Evidence\selected-plan.json `
|
||||
-FileSaclRecoveryPendingPath C:\Evidence\receipts\sacl-<id>.pending.json `
|
||||
-FileSaclRecoveryConfirmedPath C:\Evidence\receipts\sacl-<id>.confirmed.json `
|
||||
-FileSaclRecoveryResultsPath C:\Evidence\selected-results.json `
|
||||
-FileSaclRecoveryOutputPath C:\Evidence\recovery-review
|
||||
```
|
||||
|
||||
Review the new `plan.json`, especially `OriginalFiles`, `Operator`, `Expected`, `AddedAce` and `BeforeAddition`. Record its SHA-256 from the command result or `Get-FileHash`. The review directory must be new, outside the WELA installation, with an existing parent.
|
||||
|
||||
```powershell
|
||||
$plan = 'C:\Evidence\recovery-review\plan.json'
|
||||
$hash = (Get-FileHash $plan -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
.\WELA.ps1 file-sacl-recovery -FileSaclRecoveryAction Restore `
|
||||
-FileSaclRecoveryPlanPath $plan -FileSaclRecoveryPlanHash $hash -DryRun
|
||||
|
||||
.\WELA.ps1 file-sacl-recovery -FileSaclRecoveryAction Restore `
|
||||
-FileSaclRecoveryPlanPath $plan -FileSaclRecoveryPlanHash $hash `
|
||||
-Auto -FileSaclRecoveryOutputPath C:\Evidence\recovery-result
|
||||
```
|
||||
|
||||
`DryRun` rebuilds and compares the review from the original evidence and current host/file, then reports `WouldRemoveAddedAce`; it writes nothing. Actual restore requires `Auto` and a new private output directory outside the review directory. Both actions reject a modified or stale plan; rerunning an already completed plan is refused.
|
||||
|
||||
## Mutation and outcomes
|
||||
|
||||
Before mutation, `reviewed-plan.json` and `pending.json` are created exclusively, flushed to disk, reopened and hashed. Implementation, operator, host, original input files and reviewed plan are rechecked. The native helper holds a file handle without delete sharing, rejects directories and reparse files, verifies its final path and actual identity, and rereads the exact descriptor. It submits only `SACL_SECURITY_INFORMATION` to remove the unique proven ACE. Temporary `SeSecurityPrivilege` state is restored.
|
||||
|
||||
Afterwards WELA reads the held file and reopens the path, checks identity, unrelated ACE bytes/counts, SACL presence, revision when an ACL remains, owner/group, DACL, control flags and resource-manager control, then rechecks sources/evidence and reopens once more. Descriptor observations cover WinSDK-defined sections `0x1ff`; future sections are unobserved. Windows security-descriptor operations are not an atomic compare-and-swap against another administrator. Quiesce concurrent ACL writers; the guards detect observed drift, not an arbitrarily timed competing write.
|
||||
|
||||
`result.json` reports:
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
| `AddedAceRemoved` | One proven addition was removed and the bounded readback/preservation checks passed. |
|
||||
| `Refused` | The operation failed before any native write attempt. |
|
||||
| `WriteAttemptedUnverified` | A native write was attempted but complete final verification failed. Retain evidence and inspect manually. |
|
||||
|
||||
Removing the final audit ACE may leave an **empty or null present SACL** even if the historical descriptor had no SACL. Windows may retain `SACL_PRESENT` while returning no ACL pointer (`PresentNull`); this is accepted only when the removed ACE was the sole original ACE and all outside control/header fields still match. `SaclBefore` and `SaclAfter` record the observed representation and available ACL revision. This is an ACE-removal result, not a byte-for-byte restoration of the historical descriptor. `OriginalDescriptorBytesMatch` is only an observation; exact historical descriptor equality and original ACE ordering are not promised. Unrelated ACE bytes and counts are preserved. WELA does not automatically re-add the ACE after partial failure. No outcome grants rule-readiness credit.
|
||||
|
||||
## Validation and limits
|
||||
|
||||
`tests/FileSaclRecovery.Tests.ps1` covers strict input, source binding, durable exclusive output and action guards; separate CLI tests run real public process dispatch. Native descriptor tests exercise exact deltas and unsafe ACE/header/control changes. The explicitly gated Windows fixture runs on disposable Server 2022/2025 with Windows PowerShell 5.1 and PowerShell 7: it installs an owned one-file catalog only in a disposable checkout copy, obtains genuine public `Plan`/`Configure` receipts, then exercises public review/dry-run/removal/replay refusal, altered evidence/source and replacement file identity. Empty and unrelated-ACE cases retain their observed outside descriptor components. The fixture restores all 59 audit-policy masks and the exact typed precedence value/absence and deletes only its owned files.
|
||||
|
||||
This is not Windows 11, DC, ADCS, inherited directory recovery, distributed policy refresh or event/backend acceptance evidence. The original selected-target implementation files remain unchanged so the recovery feature itself does not invalidate their existing source hashes.
|
||||
|
||||
API contracts: [GetSecurityInfo](https://learn.microsoft.com/en-us/windows/win32/api/aclapi/nf-aclapi-getsecurityinfo), [SetSecurityInfo](https://learn.microsoft.com/en-us/windows/win32/api/aclapi/nf-aclapi-setsecurityinfo), [RawSecurityDescriptor](https://learn.microsoft.com/en-us/dotnet/api/system.security.accesscontrol.rawsecuritydescriptor).
|
||||
@@ -52,3 +52,5 @@ The mocked regression suite exercises missing fields/providers, unsupported type
|
||||
Primary references: [Microsoft WEF Appendix C/F](https://learn.microsoft.com/en-us/windows/security/operating-system-security/device-management/use-windows-event-forwarding-to-assist-in-intrusion-detection), [DNS logging and diagnostics](https://learn.microsoft.com/en-us/windows-server/networking/dns/dns-logging-and-diagnostics), [EventMetadata](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventing.reader.eventmetadata?view=windowsdesktop-10.0), [EventLogLink](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventing.reader.eventloglink?view=windowsdesktop-10.0), [Windows 11 release families](https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information), and [Windows Server release families](https://learn.microsoft.com/en-us/windows/release-health/windows-server-release-info). The WEF sample identifies event/channel candidates; it does not validate these rule definitions or this implementation on every build.
|
||||
|
||||
For an explicitly reviewed DNS Server analytical transition with stopped-trace archival, use the separate [DNS analytical lifecycle](dns-analytical.md). The ordinary provider-pack setter continues to refuse Analytical/Debug configuration.
|
||||
|
||||
The separate [`dns-client-probe`](dns-client-probe.md) can collect a fixed native DNS Client lookup completion and exact Operational3008 XML. The six pinned rule channel strings remain mismatched; the probe grants no Sigma readiness credit.
|
||||
@@ -73,3 +73,5 @@ The mock suite covers typed values, shared views, idempotence, ordering, destina
|
||||
Native CI passed on both Server 2022 and Server 2025 under Windows PowerShell 5.1 and PowerShell 7 in [run 35439090461](https://github.com/Yamato-Security/WELA/actions/runs/35439090461): 14 native assertions per OS/host combination, including fresh x64/x86 Windows PowerShell 5.1 transcript markers and verified restoration (56 native assertions total). Production/central validation still requires actual client, server, DC and service identities: test a benign new session, record the transcript and effective policy, verify unauthorized read/modify attempts fail, check collection and quotas/retention, and verify recovery. CI's local private folder does not satisfy the central authorization/ingestion acceptance criterion. Sysmon and external telemetry are out of scope.
|
||||
|
||||
Reviewed CIS references: [Windows 11 Enterprise v4.0.0, PDF pages 1286–1287](https://rayasec.com/wp-content/uploads/CIS-Benchmark/Microsoft-Windows-Desktop/CIS_Microsoft_Windows_11_Enterprise_Benchmark_v4.0.0.pdf#page=1286), [Windows Server 2022 v4.0.0, PDF pages 1029–1030](https://rayasec.com/wp-content/uploads/CIS-Benchmark/Microsoft-Windows-Server/CIS_Microsoft_Windows_Server_2022_Benchmark_v4.0.0.pdf#page=1029).
|
||||
|
||||
For a fixed child under the actual current account, see the optional [automatic transcription probe](transcript-probe.md). It verifies completed local automatic output without changing policy or granting EVTX/Sigma credit.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Automatic Windows PowerShell transcription probe
|
||||
|
||||
`transcript-probe` checks whether one fixed Windows PowerShell 5.1 child, launched as the current WELA account, produces its own completed **automatic** transcript in an already configured local destination. It does not change policy, ACLs, services or shares. It never calls `Start-Transcript` as a fallback. Transcripts are not EVTX, and the report always grants zero Sigma/EVTX credit.
|
||||
|
||||
This is the current-account local-writer acceptance portion of #376. The existing [transcription audit/configure command](powershell-transcription.md) remains separate. UNC/share acceptance, remote collection, retention and testing other writer accounts remain separate work.
|
||||
|
||||
## Commands
|
||||
|
||||
Run from native 64-bit Windows PowerShell 5.1 or PowerShell 7 on a supported Windows 11, Server 2022 or Server 2025 host. The child being tested is always native Windows PowerShell 5.1; running WELA in PowerShell 7 does not test PowerShell 7 transcription.
|
||||
|
||||
```powershell
|
||||
# Default Plan: inspect the selected destination and prerequisites.
|
||||
.\WELA.ps1 transcript-probe -TranscriptProbeDirectory C:\Transcripts
|
||||
|
||||
# Explicit Run: one fixed child, then verify its completed automatic transcript.
|
||||
.\WELA.ps1 transcript-probe -TranscriptProbeAction Run `
|
||||
-TranscriptProbeDirectory C:\Transcripts `
|
||||
-TranscriptProbeOutputPath C:\Evidence\transcript-unique-run
|
||||
```
|
||||
|
||||
The output directory must be new, have an existing parent, and be outside the transcript destination. WELA creates it with access for the current account, SYSTEM and Administrators. `Plan` launches no probe child and creates no explicit evidence directory. An already enabled transcription policy can naturally transcribe the WELA invocation itself, including a Plan invocation.
|
||||
|
||||
An enabled machine `EnableTranscripting` DWORD policy and an explicit literal `OutputDirectory` string must already exist and match the selected local fixed-drive directory. Both shared registry views must agree. Current-user policy and invocation-header settings are also recorded and checked for known types. This initial command does not infer default destinations or accept a current-user-only policy. Winmgmt must already be running for read-only host observations.
|
||||
|
||||
The account needs directory/date-folder metadata and listing access, plus read access to the new transcript. A write-only drop-box destination may accept automatic transcripts but cannot be proven by this verifier. Permission failures remain unverified; WELA does not broaden access to obtain proof.
|
||||
|
||||
## What a successful result means
|
||||
|
||||
`Status: CompletedAutomaticTranscript` and `WriterAuthorization: ObservedForThisChild` mean the local verifier observed exactly one fresh matching completed transcript from the fixed child during this run. The evidence binds the following:
|
||||
|
||||
- The actual child PID, executable, PowerShell 5.1 Desktop version, command arguments and loaded engine assembly hash.
|
||||
- Actual current-account SID, logon authentication ID and group attributes in the parent and child. Full before/after token observations are retained within each process; cross-process matching uses the authorization identity and groups because process startup can change privilege flags.
|
||||
- Unique standalone begin/end output lines, the native engine's localized header/footer resource templates, header identity/PID/host command, and header/footer timestamps within the observed launch/exit window.
|
||||
- Existing policy, executable/source hashes, host and time-zone observations, plus handle-based destination/date-folder identity and owner/group/DACL observations before and after the operation.
|
||||
- Bounded raw matching transcript bytes, SHA-256 hashes and a matching file handle retained through final checks. Reparse points, multi-link files and identity/descriptor drift are refused.
|
||||
|
||||
The fixed child uses `-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File` with WELA's bundled worker and a generated nonce. The execution-policy option is local to that process; it does not change stored policy or override enforced Group Policy. No arbitrary command, credential or alternative executable can be supplied to this command.
|
||||
|
||||
Output preparation and initial observations precede the parent token interval. The measured interval covers the worker and transcript verification; the report retains both parent token snapshots. The child records its own before/after interval. Token differences, ambiguous transcripts, incomplete output, unexpected formats or context drift fail verification. A completed transcript proves this observed operation, not continuing authorization, other users' access, remote share acceptance, reliable collection or application of every baseline recommendation.
|
||||
|
||||
This is local consistency evidence, not tamper-proof attestation against another process controlled by the same account or an administrator. The fixed nonce, PID, command and time checks provide correlation; they do not establish exclusive writer attribution against a malicious local actor.
|
||||
|
||||
## Bounds and artifacts
|
||||
|
||||
The inventory covers only the previous, current and next local calendar-date folders, with at most 256 entries in total. Existing transcript contents are never read. The verifier considers at most 32 new file identities, reads at most 1 MiB per candidate and 4 MiB in total, and accepts exactly one matching transcript. Unexpected directories, names, encodings or candidate times remain unverified. Busy destinations can exceed these conservative bounds.
|
||||
|
||||
The worker has a 30-second deadline. Each redirected output stream retains at most 65,536 characters, with bounded pipe-drain and termination waits. The report includes explicit diagnostics for refusal and incomplete evidence; no fallback obtains a positive result.
|
||||
|
||||
A completed Run writes `result.json`, `worker.json` and the exact matching `transcript.txt` bytes into the protected output. The result also retains bounded fixed-worker stdout/stderr and launch/exit observations, including when worker validation fails. Failed runs that reached output preparation keep diagnostic artifacts and exit nonzero. Prerequisite failures can occur before an output directory exists. Source transcripts are retained in their configured destination; WELA never removes them.
|
||||
|
||||
## Validation
|
||||
|
||||
Portable fixtures exercise the actual native-format matcher, identity/time/nonce/version refusals, localization templates, encoding limits, policy types, drift and dedicated CLI guards. The opt-in hosted Windows fixture provisions only its own standard account and destination, enables a temporary machine transcription policy, and invokes the public command in fresh processes. It tests an allowed writer and then a denied writer, preserves both original typed policy views, restores the original destination ACL and removes only the owned account. This fixture is gated to disposable standalone GitHub-hosted Server 2022/2025 machines and both WELA host engines. It never substitutes an explicit transcript for automatic policy output.
|
||||
|
||||
Microsoft documents automatic policy transcription and machine-policy precedence in [Turn on PowerShell Transcription](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_group_policy_settings?view=powershell-5.1#turn-on-powershell-transcription). The verifier reads the actual installed engine's transcript resource templates rather than assuming an English header.
|
||||
Reference in new issue
Block a user