Series · 1 partsAnatomy of a Red Team OperationPart 1 · You are here
- 1The Red Team Reached Domain Admin. The Exercise Still Failed.You are here
Red team success in 60 seconds
The red team reached Domain Admin. The screenshot was clean, the path was reproducible, and the executive readout contained the sentence everybody expected: the domain could be compromised.
The exercise still failed.
Nobody had written down which business operation the directory access was supposed to threaten. The SOC saw one alert but could not connect it to the identity path. The operators could not prove which actions were performed before or after privilege was obtained. The temporary account remained enabled after the closeout call. The final report proved access, but it did not prove whether the organization could detect, decide, contain, or recover.
That is the distinction this series starts with:
Domain Admin is a capability. A red team objective describes the controlled business effect that capability could enable, the signals it should create, and the decision the defender must make.
A useful operation does not end at NT AUTHORITY\\SYSTEM, uid=0, Global Administrator, or Domain
Admin. It follows the chain one step further without causing the harmful effect: which protected
system becomes reachable, which control was expected to interrupt the path, which telemetry records
the action, who makes the containment decision, and how the environment returns to a known state.
This article presents a synthetic Windows and Active Directory casefile. It is a methodology and a controlled lab workflow, not a report of a real customer compromise. The commands are either local evidence handling or read-only directory and event-log queries. They do not create accounts, change group membership, dump credentials, establish persistence, disable controls, or modify the domain.
The synthetic casefile
The fictional organization in this article operates an order-release platform. A privileged directory group can administer the management tier used by that platform. The red team begins with an approved standard test identity and an approved workstation in an isolated lab domain. The team is allowed to enumerate the directory and demonstrate that a pre-provisioned privileged test account can reach the management boundary. It is not allowed to modify orders, production data, identity policy, monitoring, backups, or domain configuration.
The difference between the technical milestone and the operational objective is deliberate:
- Technical milestone: the approved privileged identity is demonstrably a member of the built-in Domain Admins group and can authenticate to the lab management boundary.
- Business hypothesis: compromise of the identity control plane could let an adversary reach the order-release administration path.
- Safe proof: resolve the effective identity and approved management target, then stop before any business transaction or configuration change.
- Defender obligation: correlate the privileged logon and discovery activity, identify the protected path, and make a documented containment decision within the exercise window.
- Recovery obligation: revoke or disable test access, preserve evidence, and independently prove that no test authority or artifact survives the operation.
Reaching Domain Admin proves only the first item. It does not, on its own, prove that the order platform is reachable, that a transaction could be authorized, that the SOC saw the path, or that the environment was restored.
Start with the decision that must remain protected
MITRE ATT&CK tactics describe the adversary’s tactical goals: initial access, execution, privilege escalation, discovery, lateral movement, and the other reasons an adversary performs an action. That vocabulary is useful for naming behavior. It is not the exercise objective.
“Test privilege escalation” tells the operator where to work. It does not tell the organization what must remain true. A stronger objective names a protected decision and a safe stopping point:
Determine whether a compromise of an approved standard identity can reach the order-release administration boundary; stop before changing an order; require the SOC to identify the identity path and decide whether to disable the test account; then prove the account and all test artifacts are removed or revoked.
I write that contract before selecting a technique. A compact machine-readable version makes scope drift and retrospective success criteria harder:
operation_id: RT-LAB-2026-001
objective:
protected_decision: "release an order to fulfillment"
hypothesis: "directory control can reach the order administration boundary"
safe_proof: "resolve effective access and authenticate to the approved lab management host"
forbidden_effects:
- "change an order or application configuration"
- "modify directory groups, policy, monitoring, or backups"
- "access production or third-party systems"
success_conditions:
red:
- "prove the approved identity path without crossing the business transaction boundary"
blue:
- "correlate the privileged logon and discovery action to the operation window"
- "record an allow, investigate, or contain decision with an owner"
recovery:
- "revoke test authority and verify no account, token, task, service, or artifact survives"
stop_conditions:
- "resolved target differs from the approved lab asset"
- "telemetry collection is unavailable"
- "account lockout, service degradation, or unexpected data access occurs"
This file is not authorization by itself. It is a signed-off expression of the authorized plan. NIST SP 800-115 treats assessment planning, coordination, execution, data handling, reporting, and remediation as connected activities; its Rules of Engagement template gives the plan an explicit place to define constraints. The important operational addition is to resolve those constraints again at runtime.
Signed rules do not resolve runtime ambiguity
The Rules of Engagement may authorize directory discovery. At 02:10, the operator still needs to
know whether dc01.lab.example is the expected lab controller, whether the identity in the shell is
the assigned one, whether the event collector is healthy, and whether the next action remains
read-only.
Use an action matrix that can be answered before execution:
| Proposed action | Target resolved at runtime | Expected effect | Authority | Approval | Stop condition |
|---|---|---|---|---|---|
| Enumerate built-in privileged-group membership | Approved lab domain SID | Directory reads only | Standard audit identity | Pre-approved | Domain SID or controller differs from casefile |
| Authenticate with pre-provisioned privileged test identity | Approved management host | One interactive or network logon | Named test identity | Time-bound operator approval | Host, account, route, or window differs |
| Query Security events | Approved collector or lab host | Log reads only | Defender event-reader role | Pre-approved | Collector gap or clock drift exceeds tolerance |
| Exercise the business transaction | Order-release application | Would change business state | None | Explicitly prohibited | Stop at access decision; do not submit |
| Change group membership or create persistence | Domain control plane | Durable identity change | None | Explicitly prohibited | Do not execute |
This matrix prevents a familiar failure: proving an authorization path by performing the business effect the control was supposed to protect. If reaching the transaction approval page establishes the access decision, submitting the transaction is impact, not evidence.
Build the evidence envelope before the action
The operator and defender should agree on a clock, case identifier, approved systems, and collection window before the controlled action. The following PowerShell creates a local evidence directory on the approved operator workstation, records the runtime identity and domain, captures the audit-policy baseline, and starts a transcript. It does not alter Active Directory.
Run it only on the authorized lab workstation. Use a case identifier that contains no customer name, credential, or secret.
Import-Module ActiveDirectory
$caseId = "RT-LAB-2026-001"
$evidenceRoot = Join-Path $env:ProgramData "JankeSec-RedTeam\\$caseId"
New-Item -ItemType Directory -Path $evidenceRoot -Force | Out-Null
$transcriptPath = Join-Path $evidenceRoot "$caseId-transcript.txt"
Start-Transcript -Path $transcriptPath -Force
$windowStartUtc = (Get-Date).ToUniversalTime()
$domain = Get-ADDomain
$controller = Get-ADDomainController -Discover -Service PrimaryDC
[pscustomobject]@{
CaseId = $caseId
WindowStartUtc = $windowStartUtc.ToString("o")
OperatorHost = $env:COMPUTERNAME
OperatorIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
DomainDnsRoot = $domain.DNSRoot
DomainSid = $domain.DomainSID.Value
ResolvedDc = $controller.HostName
ResolvedDcAddress = ($controller.IPv4Address -join ",")
} | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $evidenceRoot "runtime-context.json")
whoami.exe /all | Out-File (Join-Path $evidenceRoot "whoami-all.txt")
auditpol.exe /get /category:* | Out-File (Join-Path $evidenceRoot "audit-policy.txt")
Pause here. Compare DomainDnsRoot, DomainSid, ResolvedDc, operator identity, and IP address to
the signed casefile. A familiar hostname is not enough. If any resolved value differs, the stop
condition has fired before the test has touched the directory.
auditpol /get is evidence of configured local audit policy, not proof that events reach the SIEM.
The output should show whether logon, special logon, process creation, and directory-service change
auditing are expected, but pipeline health requires a separate end-to-end signal.
Measure capability without turning it into impact
The built-in Domain Admins group has a domain-relative identifier of 512. Resolve it from the
current domain SID instead of assuming an English group name. The following query is read-only and
exports only object class, account name, SID, and distinguished name. It does not retrieve secrets
or modify membership.
$domainAdminSid = [System.Security.Principal.SecurityIdentifier]::new(
"$($domain.DomainSID.Value)-512"
)
$domainAdmins = Get-ADGroup -Identity $domainAdminSid
$actionStartUtc = (Get-Date).ToUniversalTime()
$members = Get-ADGroupMember -Identity $domainAdmins -Recursive |
Select-Object objectClass, SamAccountName, SID, DistinguishedName |
Sort-Object objectClass, SamAccountName
$memberExport = Join-Path $evidenceRoot "domain-admin-members.csv"
$members | Export-Csv -LiteralPath $memberExport -NoTypeInformation
$actionEndUtc = (Get-Date).ToUniversalTime()
[pscustomobject]@{
CaseId = $caseId
Action = "Read built-in Domain Admins recursive membership"
AttackMapping = "T1069.002 Permission Groups Discovery: Domain Groups"
StartUtc = $actionStartUtc.ToString("o")
EndUtc = $actionEndUtc.ToString("o")
ResolvedGroup = $domainAdmins.DistinguishedName
ResultCount = $members.Count
SideEffect = "Directory reads only"
} | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $evidenceRoot "action-01.json")
This query establishes current group membership as observed by the querying identity. It does not prove that every member can log on to the management host, that nested or delegated paths are the only routes to control, that a specific credential is available, or that the protected application accepts directory authority. Those are separate claims with separate negative controls.
If the lab uses Atomic Red Team, inspect the selected test before running it:
Invoke-AtomicTest T1069.002 -ShowDetailsBrief
Invoke-AtomicTest T1069.002 -CheckPrereqs
Do not treat a technique identifier as approval. The Invoke-AtomicRedTeam project warns that tests can leave systems in an undesirable state and recommends an authorized test machine with working collection and EDR. Review the exact atomic definition, inputs, prerequisites, cleanup, and resulting commands; then bind the chosen test number and version to the action matrix. For this first article, the explicit SID-based read above is enough.
The defender test begins where the red command ends
The red team’s transcript proves what the operator intended and observed. It does not prove what the endpoint, domain controller, collector, SIEM, or analyst saw. Those are independent evidence planes.
Windows event IDs provide correlation points, not conclusions:
4624records a successful logon on the system that was accessed.4672records sensitive privileges assigned to a new logon, but common system activity can also generate it; it is not synonymous with Domain Admin.4688records process creation when the audit subcategory is enabled. Command-line fields require the additional command-line process auditing setting and can expose sensitive arguments.4728,4732, and4756concern additions to security-enabled groups. Their absence is expected in this read-only exercise.5136concerns directory object modification. Its absence supports, but cannot alone prove, that the directory was unchanged during the window.
Microsoft’s process-auditing guidance explicitly notes both the need for Process Creation auditing and the privacy risk of recording plain text command-line arguments. Never place passwords, tokens, customer data, or secrets in a command line merely to make the test searchable.
Export the bounded Windows event window
The defender—not the red operator—should run the event export with an approved event-reader identity
on the relevant lab host or collector. Use the exact UTC window from action-01.json, then preserve
the original event record IDs.
$eventIds = 4624, 4625, 4672, 4688, 4728, 4729, 4732, 4733, 4756, 4757, 5136
$windowEndUtc = (Get-Date).ToUniversalTime()
$events = Get-WinEvent -FilterHashtable @{
LogName = "Security"
Id = $eventIds
StartTime = $windowStartUtc.ToLocalTime()
EndTime = $windowEndUtc.ToLocalTime()
}
$normalized = foreach ($event in $events) {
$xml = [xml]$event.ToXml()
$fields = @{}
foreach ($field in $xml.Event.EventData.Data) {
if ($field.Name) { $fields[$field.Name] = $field.'#text' }
}
[pscustomobject]@{
TimeCreatedUtc = $event.TimeCreated.ToUniversalTime().ToString("o")
EventId = $event.Id
RecordId = $event.RecordId
Computer = $event.MachineName
Account = $fields["SubjectUserName"]
TargetAccount = $fields["TargetUserName"]
LogonId = $fields["SubjectLogonId"]
ProcessName = $fields["NewProcessName"]
CommandLine = $fields["CommandLine"]
ObjectDn = $fields["ObjectDN"]
}
}
$normalized |
Sort-Object TimeCreatedUtc, RecordId |
Export-Csv -LiteralPath (Join-Path $evidenceRoot "windows-events.csv") -NoTypeInformation
The local-time conversion is intentional because the Windows filtering API consumes local
DateTime values here; the exported timeline is normalized back to UTC. Confirm this behavior in
your collector. A time-zone assumption can create a clean-looking but false gap.
Query the SIEM independently
For Microsoft Sentinel deployments using the SecurityEvent table, the defender can run a bounded
query like this. Replace the synthetic values with the signed case window and approved hosts; do not
search for a password or secret.
let operation_start = datetime(2026-08-30T17:00:00Z);
let operation_end = datetime(2026-08-30T17:20:00Z);
let approved_hosts = dynamic(["RT-WKS01", "LAB-DC01", "LAB-MGMT01"]);
SecurityEvent
| where TimeGenerated between (operation_start .. operation_end)
| where Computer has_any (approved_hosts)
| where EventID in (4624, 4625, 4672, 4688, 4728, 4729, 4732, 4733, 4756, 4757, 5136)
| project TimeGenerated, Computer, EventID, Account, TargetAccount,
SubjectLogonId, Activity, Process, CommandLine, EventData
| order by TimeGenerated asc
An empty query result does not mean the action was invisible. It could mean the host was outside the data connector, the audit subcategory was disabled, the event had not arrived, the field mapping differed, the time window was wrong, or retention had already removed it. Record which layer failed.
Detection is not the same as a defender decision
An alert firing is a product event. A defender decision has an owner, evidence, a timestamp, and a declared next action. For this casefile, the useful sequence is:
- The endpoint or identity sensor records the controlled discovery or privileged logon.
- The collector receives the event with stable host, identity, and time fields.
- Detection logic creates an alert or attaches the action to the operation case.
- An analyst distinguishes the authorized exercise from ordinary administration and malicious activity without relying on an unrecorded phone call.
- The analyst chooses
allow,investigate, orcontainand records why. - If containment is selected, the assigned team disables or revokes the approved test access using the pre-arranged recovery procedure.
- A separate check proves that the recovery action took effect.
Pre-whitelisting every red team indicator may keep the SOC calm, but it removes the system under test. Full secrecy can create the opposite problem: an authorized exercise becomes an avoidable incident. Deconfliction should reveal the minimum information necessary to keep people and systems safe while preserving the decision path being evaluated.
Evidence matrix
| Claim | Red evidence | Independent defender evidence | Negative control | What it does not prove |
|---|---|---|---|---|
| The operator acted in the approved domain | Runtime context contains expected domain SID, controller, host, identity, and UTC time | Asset inventory resolves the same systems to the lab case | Repeat resolution against an intentionally different lab domain and require the scope gate to stop | That every later command stayed in scope |
| The built-in privileged group was enumerated read-only | Transcript, action-01.json, and exported member list | Endpoint process telemetry and directory/EDR network telemetry in the same window | Run the same wrapper with a benign local identity query and compare fields | That membership was changed or a credential was obtained |
| The approved privileged identity created a special logon | Named test identity and management-host access record | Correlated 4624 and, where applicable, 4672 on the accessed host | Authenticate an approved non-privileged test identity and compare assigned privileges | That 4672 alone means Domain Admin or malicious activity |
| No directory write was part of the controlled step | Action definition is read-only and transcript contains no write command | No correlated group-change or 5136 event in the complete collection window | In an isolated disposable lab, defenders may generate a separately approved canary change and verify the pipeline sees it | That absence of an event proves no change occurred when auditing or collection is incomplete |
| The SOC detected and processed the action | Red team supplies only case window and approved deconfliction data | Alert ID, analyst case, timestamps, evidence, owner, and decision | Repeat with a baseline administrative action and measure whether logic distinguishes it | That an alert alone produced containment or recovery |
| Test authority was revoked | Operator can no longer use the test access after closeout | Identity owner confirms account/token state and relevant sessions are invalid | Approved control identity still reaches its normal management path | That all unrelated compromise paths are closed |
The negative control is not a decorative column. It is what prevents a red team narrative from becoming circular: the team acted, the team took a screenshot, and the team declared the screenshot proof of everything surrounding the action.
Seal the evidence and close the operation
End the collection window explicitly, stop the transcript, hash every artifact, and generate a manifest. Hashes help detect later changes to the bundle; they do not prove that an artifact came from the claimed system or that the recorder was trustworthy.
$windowEndUtc = (Get-Date).ToUniversalTime()
[pscustomobject]@{
CaseId = $caseId
WindowEndUtc = $windowEndUtc.ToString("o")
Outcome = "Controlled directory read completed; no directory write authorized"
} | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $evidenceRoot "operation-close.json")
Stop-Transcript
$manifestPath = Join-Path $evidenceRoot "sha256-manifest.csv"
Get-ChildItem -LiteralPath $evidenceRoot -File |
Where-Object Name -ne "sha256-manifest.csv" |
Sort-Object Name |
ForEach-Object {
$hash = Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256
[pscustomobject]@{
File = $_.Name
Bytes = $_.Length
SHA256 = $hash.Hash
CollectedUtc = (Get-Date).ToUniversalTime().ToString("o")
}
} | Export-Csv -LiteralPath $manifestPath -NoTypeInformation
Do not finish with the manifest. The closeout checklist needs named owners and independent answers:
- Was the pre-provisioned test identity disabled, expired, or returned to its documented baseline?
- Were active sessions and time-bound tokens revoked rather than merely waiting to expire?
- Did the test create any account, group membership, scheduled task, service, certificate, firewall rule, startup item, cloud resource, or data object? This first procedure should answer “no”; verify rather than assume.
- Were operator-side transcripts, exports, and credentials moved to the approved evidence store and removed according to the engagement’s retention policy?
- Can the control owner reproduce the denial or revoked access without the red team present?
- Are every alert, incident, exception, and false-negative assigned to an owner and retest date?
Cleanup is not “the command returned success.” Recovery is the independently observed state after cleanup.
What a successful first operation looks like
The red team does not need to submit an order, create persistence, disable security tooling, or dump a directory database to make this case valuable. It needs to produce a connected record:
- the protected decision and safe stopping point were defined before the action;
- runtime scope resolved to the authorized lab domain, host, identity, and time window;
- the directory query and privileged test access stayed inside the approved authority;
- operator, endpoint, identity, collector, alert, and analyst evidence can be correlated without pretending one source proves the others;
- the SOC made a visible decision rather than merely accumulating an alert;
- test authority was revoked and the clean state was independently verified;
- every gap became a control owner, evidence requirement, and retest condition.
If the team reaches Domain Admin but cannot answer those questions, the exercise has demonstrated a privilege path and little else. That may still be a serious finding. It is not yet a successful red team operation.
The next part of this series moves to the boundary that makes or breaks everything after planning: the Rules of Engagement may be signed, but runtime authority still has to decide which exact action, target, identity, effect, and approval are allowed to exist.
Next: “The Rules of Engagement Were Signed. Runtime Authority Was Still Undefined.”
How current is this note?
The latest source-review, content-update, or publication date is shown.
Primary public records were checked. Environment-specific behavior remains outside the claim unless separately reproduced.
