09/14/2021 04:30:48 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=3 Type=Warning ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532346 Keywords=None Message=Creating Scriptblock text (1 of 1): # Copyright 2016 Cloudbase Solutions Srl # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # $here = Split-Path -Parent $MyInvocation.MyCommand.Path $assemblies = Join-Path $here "Load-Assemblies.ps1" if (Test-Path $assemblies) { . $here\Load-Assemblies.ps1 } function Get-YamlDocuments { [CmdletBinding()] Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [string]$Yaml, [switch]$UseMergingParser=$false ) PROCESS { $stringReader = new-object System.IO.StringReader($Yaml) $parser = New-Object "YamlDotNet.Core.Parser" $stringReader if($UseMergingParser) { $parser = New-Object "YamlDotNet.Core.MergingParser" $parser } $yamlStream = New-Object "YamlDotNet.RepresentationModel.YamlStream" $yamlStream.Load([YamlDotNet.Core.IParser] $parser) $stringReader.Close() return $yamlStream } } function Convert-ValueToProperType { [CmdletBinding()] Param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [System.Object]$Node ) PROCESS { if (!($Node.Value -is [string])) { return $Node } if ($Node.Style -eq 'Plain') { $types = @([int], [long], [double], [boolean], [decimal]) foreach($i in $types){ $parsedValue = New-Object -TypeName $i.FullName if ($i.IsAssignableFrom([boolean])){ $result = $i::TryParse($Node,[ref]$parsedValue) } else { $result = $i::TryParse($Node, [Globalization.NumberStyles]::Any, [Globalization.CultureInfo]::InvariantCulture, [ref]$parsedValue) } if( $result ) { return $parsedValue } } } # From the YAML spec: http://yaml.org/type/timestamp.html $regex = @' [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] # (ymd) |[0-9][0-9][0-9][0-9] # (year) -[0-9][0-9]? # (month) -[0-9][0-9]? # (day) ([Tt]|[ \t]+)[0-9][0-9]? # (hour) :[0-9][0-9] # (minute) :[0-9][0-9] # (second) (\.[0-9]*)? # (fraction) (([ \t]*)Z|[-+][0-9][0-9]?(:[0-9][0-9])?)? # (time zone) '@ if([Text.RegularExpressions.Regex]::IsMatch($Node.Value, $regex, [Text.RegularExpressions.RegexOptions]::IgnorePatternWhitespace) ) { [DateTime]$datetime = [DateTime]::MinValue if( ([DateTime]::TryParse($Node.Value,[ref]$datetime)) ) { return $datetime } } if ($Node.Style -eq 'Plain' -and $Node.Value -in '','~','null','Null','NULL') { return $null } return $Node.Value } } function Convert-YamlMappingToHashtable { [CmdletBinding()] Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [YamlDotNet.RepresentationModel.YamlMappingNode]$Node, [switch] $Ordered ) PROCESS { if ($Ordered) { $ret = [ordered]@{} } else { $ret = @{} } foreach($i in $Node.Children.Keys) { $ret[$i.Value] = Convert-YamlDocumentToPSObject $Node.Children[$i] -Ordered:$Ordered } return $ret } } function Convert-YamlSequenceToArray { [CmdletBinding()] Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [YamlDotNet.RepresentationModel.YamlSequenceNode]$Node, [switch]$Ordered ) PROCESS { $ret = [System.Collections.Generic.List[object]](New-Object "System.Collections.Generic.List[object]") foreach($i in $Node.Children){ $ret.Add((Convert-YamlDocumentToPSObject $i -Ordered:$Ordered)) } return ,$ret } } function Convert-YamlDocumentToPSObject { [CmdletBinding()] Param( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [System.Object]$Node, [switch]$Ordered ) PROCESS { switch($Node.GetType().FullName){ "YamlDotNet.RepresentationModel.YamlMappingNode"{ return Convert-YamlMappingToHashtable $Node -Ordered:$Ordered } "YamlDotNet.RepresentationModel.YamlSequenceNode" { return Convert-YamlSequenceToArray $Node -Ordered:$Ordered } "YamlDotNet.RepresentationModel.YamlScalarNode" { return (Convert-ValueToProperType $Node) } } } } function Convert-HashtableToDictionary { Param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [hashtable]$Data ) foreach($i in $($data.Keys)) { $Data[$i] = Convert-PSObjectToGenericObject $Data[$i] } return $Data } function Convert-OrderedHashtableToDictionary { Param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [System.Collections.Specialized.OrderedDictionary] $Data ) foreach ($i in $($data.Keys)) { $Data[$i] = Convert-PSObjectToGenericObject $Data[$i] } return $Data } function Convert-ListToGenericList { Param( [Parameter(Mandatory=$false,ValueFromPipeline=$true)] [array]$Data=@() ) $ret = [System.Collections.Generic.List[object]](New-Object "System.Collections.Generic.List[object]") for($i=0; $i -lt $Data.Count; $i++) { $ret.Add((Convert-PSObjectToGenericObject $Data[$i])) } return ,$ret } function Convert-PSCustomObjectToDictionary { Param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [PSCustomObject]$Data ) $ret = [System.Collections.Generic.Dictionary[string,object]](New-Object 'System.Collections.Generic.Dictionary[string,object]') foreach ($i in $Data.psobject.properties) { $ret[$i.Name] = Convert-PSObjectToGenericObject $i.Value } return $ret } function Convert-PSObjectToGenericObject { Param( [Parameter(Mandatory=$false,ValueFromPipeline=$true)] [System.Object]$Data ) if ($null -eq $data) { return $data } $dataType = $data.GetType() if ($data -isnot [System.Object]) { return $data -as $dataType } if ($dataType.FullName -eq "System.Management.Automation.PSCustomObject") { return Convert-PSCustomObjectToDictionary $data } elseif (([System.Collections.Specialized.OrderedDictionary].IsAssignableFrom($dataType))){ return Convert-OrderedHashtableToDictionary $data } elseif (([System.Collections.IDictionary].IsAssignableFrom($dataType))){ return Convert-HashtableToDictionary $data } elseif (([System.Collections.IList].IsAssignableFrom($dataType))) { return Convert-ListToGenericList $data } return $data -as $dataType } function ConvertFrom-Yaml { [CmdletBinding()] Param( [Parameter(Mandatory=$false, ValueFromPipeline=$true, Position=0)] [string]$Yaml, [switch]$AllDocuments=$false, [switch]$Ordered, [switch]$UseMergingParser=$false ) BEGIN { $d = "" } PROCESS { if($Yaml -is [string]) { $d += $Yaml + "`n" } } END { if($d -eq ""){ return } $documents = Get-YamlDocuments -Yaml $d -UseMergingParser:$UseMergingParser if (!$documents.Count) { return } if($documents.Count -eq 1){ return Convert-YamlDocumentToPSObject $documents[0].RootNode -Ordered:$Ordered } if(!$AllDocuments) { return Convert-YamlDocumentToPSObject $documents[0].RootNode -Ordered:$Ordered } $ret = @() foreach($i in $documents) { $ret += Convert-YamlDocumentToPSObject $i.RootNode -Ordered:$Ordered } return $ret } } $stringQuotingEmitterSource = @" using System; using System.Text.RegularExpressions; using YamlDotNet; using YamlDotNet.Core; using YamlDotNet.Serialization; using YamlDotNet.Serialization.EventEmitters; public class StringQuotingEmitter: ChainedEventEmitter { // Patterns from https://yaml.org/spec/1.2/spec.html#id2804356 private static Regex quotedRegex = new Regex(@`"^(\~|null|true|false|-?(0|[0-9][0-9]*)(\.[0-9]*)?([eE][-+]?[0-9]+)?)?$`", RegexOptions.Compiled); public StringQuotingEmitter(IEventEmitter next): base(next) {} public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { var typeCode = eventInfo.Source.Value != null ? Type.GetTypeCode(eventInfo.Source.Type) : TypeCode.Empty; switch (typeCode) { case TypeCode.Char: if (Char.IsDigit((char)eventInfo.Source.Value)) { eventInfo.Style = ScalarStyle.DoubleQuoted; } break; case TypeCode.String: var val = eventInfo.Source.Value.ToString(); if (quotedRegex.IsMatch(val)) { eventInfo.Style = ScalarStyle.DoubleQuoted; } else if (val.IndexOf('\n') > -1) { eventInfo.Style = ScalarStyle.Literal; } break; } base.Emit(eventInfo, emitter); } public static SerializerBuilder Add(SerializerBuilder builder) { return builder.WithEventEmitter(next => new StringQuotingEmitter(next)); } } "@ $referenceList = @([YamlDotNet.Serialization.Serializer].Assembly.Location,[Text.RegularExpressions.Regex].Assembly.Location) if ($PSVersionTable.PSEdition -eq "Core") { Add-Type -TypeDefinition $stringQuotingEmitterSource -ReferencedAssemblies $referenceList -Language CSharp -CompilerOptions "-nowarn:1701" } else { Add-Type -TypeDefinition $stringQuotingEmitterSource -ReferencedAssemblies $referenceList -Language CSharp } function Get-Serializer { Param( [Parameter(Mandatory=$true)][YamlDotNet.Serialization.SerializationOptions]$Options ) $builder = New-Object "YamlDotNet.Serialization.SerializerBuilder" if ($Options.HasFlag([YamlDotNet.Serialization.SerializationOptions]::Roundtrip)) { $builder = $builder.EnsureRoundtrip() } if ($Options.HasFlag([YamlDotNet.Serialization.SerializationOptions]::DisableAliases)) { $builder = $builder.DisableAliases() } if ($Options.HasFlag([YamlDotNet.Serialization.SerializationOptions]::EmitDefaults)) { $builder = $builder.EmitDefaults() } if ($Options.HasFlag([YamlDotNet.Serialization.SerializationOptions]::JsonCompatible)) { $builder = $builder.JsonCompatible() } if ($Options.HasFlag([YamlDotNet.Serialization.SerializationOptions]::DefaultToStaticType)) { $builder = $builder.WithTypeResolver((New-Object "YamlDotNet.Serialization.TypeResolvers.StaticTypeResolver")) } $builder = [StringQuotingEmitter]::Add($builder) return $builder.Build() } function ConvertTo-Yaml { [CmdletBinding(DefaultParameterSetName = 'NoOptions')] Param( [Parameter(ValueFromPipeline = $true, Position=0)] [System.Object]$Data, [string]$OutFile, [Parameter(ParameterSetName = 'Options')] [YamlDotNet.Serialization.SerializationOptions]$Options = [YamlDotNet.Serialization.SerializationOptions]::Roundtrip, [Parameter(ParameterSetName = 'NoOptions')] [switch]$JsonCompatible, [switch]$Force ) BEGIN { $d = [System.Collections.Generic.List[object]](New-Object "System.Collections.Generic.List[object]") } PROCESS { if($data -is [System.Object]) { $d.Add($data) } } END { if ($d -eq $null -or $d.Count -eq 0) { return } if ($d.Count -eq 1) { $d = $d[0] } $norm = Convert-PSObjectToGenericObject $d if ($OutFile) { $parent = Split-Path $OutFile if (!(Test-Path $parent)) { Throw "Parent folder for specified path does not exist" } if ((Test-Path $OutFile) -and !$Force) { Throw "Target file already exists. Use -Force to overwrite." } $wrt = New-Object "System.IO.StreamWriter" $OutFile } else { $wrt = New-Object "System.IO.StringWriter" } if ($PSCmdlet.ParameterSetName -eq 'NoOptions') { $Options = 0 if ($JsonCompatible) { # No indent options :~( $Options = [YamlDotNet.Serialization.SerializationOptions]::JsonCompatible } } try { $serializer = Get-Serializer $Options $serializer.Serialize($wrt, $norm) } catch{ $_ } finally { $wrt.Close() } if ($OutFile) { return } else { return $wrt.ToString() } } } New-Alias -Name cfy -Value ConvertFrom-Yaml New-Alias -Name cty -Value ConvertTo-Yaml Export-ModuleMember -Function * -Alias * ScriptBlock ID: 08594993-f2ed-40f0-89a1-7e9307e358e0 Path: C:\Users\Administrator\Documents\WindowsPowerShell\Modules\powershell-yaml\0.4.2\powershell-yaml.psm1 09/14/2021 04:30:48 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532336 Keywords=None Message=Creating Scriptblock text (1 of 1): function Load-Assembly { $libDir = Join-Path $here "lib" $assemblies = @{ "core" = Join-Path $libDir "netstandard1.3\YamlDotNet.dll"; "net45" = Join-Path $libDir "net45\YamlDotNet.dll"; "net35" = Join-Path $libDir "net35\YamlDotNet.dll"; } if ($PSVersionTable.PSEdition -eq "Core") { return [Reflection.Assembly]::LoadFrom($assemblies["core"]) } elseif ($PSVersionTable.PSVersion.Major -ge 4) { return [Reflection.Assembly]::LoadFrom($assemblies["net45"]) } else { return [Reflection.Assembly]::LoadFrom($assemblies["net35"]) } } ScriptBlock ID: ac01e108-9c69-45f3-80bc-d56892894a65 Path: C:\Users\Administrator\Documents\WindowsPowerShell\Modules\powershell-yaml\0.4.2\Load-Assemblies.ps1 09/14/2021 04:30:48 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532333 Keywords=None Message=Creating Scriptblock text (1 of 1): function Initialize-Assemblies { $requiredTypes = @( "Parser", "MergingParser", "YamlStream", "YamlMappingNode", "YamlSequenceNode", "YamlScalarNode", "ChainedEventEmitter", "Serializer", "Deserializer", "SerializerBuilder", "StaticTypeResolver" ) $yaml = [System.AppDomain]::CurrentDomain.GetAssemblies() | ? Location -Match "YamlDotNet.dll" if (!$yaml) { return Load-Assembly } foreach ($i in $requiredTypes){ if ($i -notin $yaml.DefinedTypes.Name) { Throw "YamlDotNet is loaded but missing required types ($i). Older version installed on system?" } } } ScriptBlock ID: 50015d98-deae-44b5-8735-7206ba4c3911 Path: C:\Users\Administrator\Documents\WindowsPowerShell\Modules\powershell-yaml\0.4.2\Load-Assemblies.ps1 09/14/2021 04:30:48 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=3 Type=Warning ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532330 Keywords=None Message=Creating Scriptblock text (1 of 1): # Copyright 2016 Cloudbase Solutions Srl # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # $here = Split-Path -Parent $MyInvocation.MyCommand.Path function Load-Assembly { $libDir = Join-Path $here "lib" $assemblies = @{ "core" = Join-Path $libDir "netstandard1.3\YamlDotNet.dll"; "net45" = Join-Path $libDir "net45\YamlDotNet.dll"; "net35" = Join-Path $libDir "net35\YamlDotNet.dll"; } if ($PSVersionTable.PSEdition -eq "Core") { return [Reflection.Assembly]::LoadFrom($assemblies["core"]) } elseif ($PSVersionTable.PSVersion.Major -ge 4) { return [Reflection.Assembly]::LoadFrom($assemblies["net45"]) } else { return [Reflection.Assembly]::LoadFrom($assemblies["net35"]) } } function Initialize-Assemblies { $requiredTypes = @( "Parser", "MergingParser", "YamlStream", "YamlMappingNode", "YamlSequenceNode", "YamlScalarNode", "ChainedEventEmitter", "Serializer", "Deserializer", "SerializerBuilder", "StaticTypeResolver" ) $yaml = [System.AppDomain]::CurrentDomain.GetAssemblies() | ? Location -Match "YamlDotNet.dll" if (!$yaml) { return Load-Assembly } foreach ($i in $requiredTypes){ if ($i -notin $yaml.DefinedTypes.Name) { Throw "YamlDotNet is loaded but missing required types ($i). Older version installed on system?" } } } Initialize-Assemblies | Out-Null ScriptBlock ID: cd761316-c21c-42e3-bce4-5340a1f29f12 Path: C:\Users\Administrator\Documents\WindowsPowerShell\Modules\powershell-yaml\0.4.2\Load-Assemblies.ps1 09/14/2021 04:30:48 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532324 Keywords=None Message=Creating Scriptblock text (1 of 1): # Copyright 2016 Cloudbase Solutions Srl # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # # Module manifest for module 'powershell-yaml' # # Generated by: Gabriel Adrian Samfira # # Generated on: 10/01/2016 # @{ # Script module or binary module file associated with this manifest. RootModule = 'powershell-yaml.psm1' # Version number of this module. ModuleVersion = '0.4.2' # ID used to uniquely identify this module GUID = '6a75a662-7f53-425a-9777-ee61284407da' # Author of this module Author = 'Gabriel Adrian Samfira','Alessandro Pilotti' # Company or vendor of this module CompanyName = 'Cloudbase Solutions SRL' # Copyright statement for this module Copyright = '(c) 2016 Cloudbase Solutions SRL. All rights reserved.' # Description of the functionality provided by this module Description = 'Powershell module for serializing and deserializing YAML' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '3.0' # Script files (.ps1) that are run in the caller's environment prior to importing this module. ScriptsToProcess = @("Load-Assemblies.ps1") # Functions to export from this module FunctionsToExport = "ConvertTo-Yaml","ConvertFrom-Yaml" AliasesToExport = "cfy","cty" } ScriptBlock ID: f58f3e1e-5142-4825-a19a-249c037c890c Path: C:\Users\Administrator\Documents\WindowsPowerShell\Modules\powershell-yaml\0.4.2\powershell-yaml.psd1 09/14/2021 04:30:48 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532321 Keywords=None Message=Creating Scriptblock text (1 of 1): @{ # Script module or binary module file associated with this manifest. RootModule = 'Invoke-AtomicRedTeam.psm1' # Version number of this module. ModuleVersion = '1.0.1.0' # ID used to uniquely identify this module GUID = '8f492621-18f8-432e-9532-b1d54d3e90bd' # Author of this module Author = 'Casey Smith @subTee, Josh Rickard @MS_dministrator, Carrie Roberts @OrOneEqualsOne, Matt Graeber @mattifestation' # Company or vendor of this module CompanyName = 'Red Canary, Inc.' # Copyright statement for this module Copyright = '(c) 2021 Red Canary. All rights reserved.' # Description of the functionality provided by this module Description = 'A PowerShell module that runs Atomic Red Team tests from yaml definition files.' # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.0' # Modules that must be imported into the global environment prior to importing this module RequiredModules = @('powershell-yaml') # Script files (.ps1) that are run in the caller's environment prior to importing this module. # AtomicClassSchema.ps1 needs to be present in the caller's scope in order for the built-in classes to surface properly. ScriptsToProcess = @('Private\AtomicClassSchema.ps1') # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @( 'Invoke-AtomicTest', 'Get-AtomicTechnique', 'New-AtomicTechnique', 'New-AtomicTest', 'New-AtomicTestInputArgument', 'New-AtomicTestDependency', 'Start-AtomicGUI', 'Stop-AtomicGUI' ) # Variables to export from this module VariablesToExport = '*' # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. Tags = @('Security', 'Defense') # A URL to the license for this module. LicenseUri = 'https://github.com/redcanaryco/invoke-atomicredteam/blob/master/LICENSE.txt' # A URL to the main website for this project. ProjectUri = 'https://github.com/redcanaryco/invoke-atomicredteam' # A URL to an icon representing this module. # IconUri = '' # ReleaseNotes of this module ReleaseNotes = @' 1.0.1 ----- * Adding 'powershell-yaml' to RequiredModules in the module manifest 1.0.0 ----- * Initial release for submission to the PowerShell Gallery '@ } # End of PSData hashtable } # End of PrivateData hashtable } ScriptBlock ID: d43752cf-6870-4103-a6c0-692d66d8d734 Path: C:\AtomicRedTeam\invoke-atomicredteam\Invoke-AtomicRedTeam.psd1 09/14/2021 04:30:48 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532319 Keywords=None Message=Creating Scriptblock text (1 of 1): Import-Module "C:\AtomicRedTeam\invoke-atomicredteam\Invoke-AtomicRedTeam.psd1" -Force ScriptBlock ID: d46acec2-7653-49a0-b02c-efd910b343f5 Path: 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532428 Keywords=None Message=Creating Scriptblock text (1 of 1): prompt ScriptBlock ID: d1105297-15f3-46d5-a8c4-6ae5e51aaaeb Path: 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532419 Keywords=None Message=Creating Scriptblock text (1 of 1): function Write-PrereqResults ($FailureReasons, $testId) { if ($FailureReasons.Count -eq 0) { Write-KeyValue "Prerequisites met: " $testId } else { Write-Host -ForegroundColor Red "Prerequisites not met: $testId" foreach ($reason in $FailureReasons) { Write-Host -ForegroundColor Yellow -NoNewline "`t[*] $reason" } Write-Host -ForegroundColor Yellow -NoNewline "`nTry installing prereq's with the " Write-Host -ForegroundColor Cyan -NoNewline "-GetPrereqs" Write-Host -ForegroundColor Yellow " switch" } } ScriptBlock ID: 25c01c51-28b5-4ff3-81bd-1fc056f8f1af Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Write-PrereqResults.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532416 Keywords=None Message=Creating Scriptblock text (1 of 1): function Write-KeyValue ($key, $value) { Write-Host -ForegroundColor Cyan -NoNewline $key $split = $value -split "(#{[a-z-_A-Z]*})" foreach ($s in $split){ if($s -match "(#{[a-z-_A-Z]*})"){ Write-Host -ForegroundColor Red -NoNewline $s } else { Write-Host -ForegroundColor Green -NoNewline $s } } Write-Host "" } ScriptBlock ID: 5741017d-2ce4-4355-8e3c-866046bd5052 Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Write-KeyValue.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532413 Keywords=None Message=Creating Scriptblock text (1 of 1): function Write-ExecutionLog($startTime, $technique, $testNum, $testName, $logPath, $targetHostname, $targetUser, $guid) { if (!(Test-Path $logPath)) { New-Item $logPath -Force -ItemType File | Out-Null } $timeUTC = (Get-Date($startTime).toUniversalTime() -uformat "%Y-%m-%dT%H:%M:%SZ").ToString() $timeLocal = (Get-Date($startTime) -uformat "%Y-%m-%dT%H:%M:%S").ToString() [PSCustomObject][ordered]@{ "Execution Time (UTC)" = $timeUTC; "Execution Time (Local)" = $timeLocal; "Technique" = $technique; "Test Number" = $testNum; "Test Name" = $testName; "Hostname" = $targetHostname; "Username" = $targetUser "GUID" = $guid } | Export-Csv -Path $LogPath -NoTypeInformation -Append } ScriptBlock ID: edf0391a-82a0-439f-9b63-5bd4dd8f52a0 Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Write-ExecutionLog.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532410 Keywords=None Message=Creating Scriptblock text (1 of 1): function Invoke-CleanupDescription(){ $ret1 = $test.description.ToString().trim() -replace '(?" | Out-File (Join-Path $WorkingDirectory $stdoutFile) -Append -Encoding ASCII break # if we're here it means the file wasn't locked and Out-File worked, so we can leave the retry loop } catch {} # file is locked Start-Sleep -m 100 } } } if ($IsLinux -or $IsMacOS) { Start-Sleep -Seconds 5 # On nix, the last 4 lines of stdout get overwritten upon return so pause for a bit to ensure user can view results } # Get Process result return $process.ExitCode } finally { if ($null -ne $process) { $process.Dispose() } } } } ScriptBlock ID: 26ce78ff-13b3-481a-8598-03c8e5dece61 Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Invoke-Process.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532401 Keywords=None Message=Creating Scriptblock text (1 of 1): function Invoke-KillProcessTree { Param([int]$ppid) if ($IsLinux -or $IsMacOS) { sh -c "pkill -9 -P $ppid" } else { while ($null -ne ($gcim = Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $ppid })) { $gcim | ForEach-Object { Invoke-KillProcessTree $_.ProcessId; Start-Sleep -Seconds 0.5 } } Stop-Process -Id $ppid -ErrorAction Ignore } } ScriptBlock ID: f219203c-69c5-4d52-97e7-b066c2d1736c Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Invoke-KillProcessTree.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532398 Keywords=None Message=Creating Scriptblock text (1 of 1): function Invoke-ExecuteCommand ($finalCommand, $executor, $TimeoutSeconds, $session = $null, $interactive) { $null = @( if ($null -eq $finalCommand) { return 0 } $finalCommand = $finalCommand.trim() Write-Verbose -Message 'Invoking Atomic Tests using defined executor' if ($executor -eq "command_prompt" -or $executor -eq "sh" -or $executor -eq "bash") { $execPrefix = "-c" $execExe = $executor if ($executor -eq "command_prompt") { $execPrefix = "/c"; $execExe = "cmd.exe"; $execCommand = $finalCommand -replace "`n", " & " } else { $finalCommand = $finalCommand -replace "[\\`"]", "`\$&" $execCommand = $finalCommand -replace "(? [CmdletBinding()] [OutputType([AtomicTechnique])] param ( [Parameter(Mandatory)] [String[]] $AttackTechnique, [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $DisplayName, [Parameter(Mandatory)] [AtomicTest[]] [ValidateNotNull()] $AtomicTests ) $AtomicTechniqueInstance = [AtomicTechnique]::new() foreach ($Technique in $AttackTechnique) { # Attack techniques should match the MITRE ATT&CK [sub-]technique format. # This is not a requirement so just warn the user. if ($Technique -notmatch '^(?-i:T\d{4}(\.\d{3}){0,1})$') { Write-Warning "The following supplied attack technique does not start with 'T' followed by a four digit number: $Technique" } } $AtomicTechniqueInstance.attack_technique = $AttackTechnique $AtomicTechniqueInstance.display_name = $DisplayName $AtomicTechniqueInstance.atomic_tests = $AtomicTests return $AtomicTechniqueInstance } function New-AtomicTest { <# .SYNOPSIS Specifies an atomic test. .PARAMETER Name Specifies the name of the test that indicates how it tests the technique. .PARAMETER Description Specifies a long form description of the test. Markdown is supported. .PARAMETER SupportedPlatforms Specifies the OS/platform on which the test is designed to run. The following platforms are currently supported: Windows, macOS, Linux. A single test can support multiple platforms. .PARAMETER ExecutorType Specifies the the framework or application in which the test should be executed. The following executor types are currently supported: CommandPrompt, Sh, Bash, PowerShell. - CommandPrompt: The Windows Command Prompt, aka cmd.exe Requires the -ExecutorCommand argument to contain a multi-line script that will be preprocessed and then executed by cmd.exe. - PowerShell: PowerShell Requires the -ExecutorCommand argument to contain a multi-line PowerShell scriptblock that will be preprocessed and then executed by powershell.exe - Sh: Linux's bourne shell Requires the -ExecutorCommand argument to contain a multi-line script that will be preprocessed and then executed by sh. - Bash: Linux's bourne again shell Requires the -ExecutorCommand argument to contain a multi-line script that will be preprocessed and then executed by bash. .PARAMETER ExecutorElevationRequired Specifies that the test must run with elevated privileges. .PARAMETER ExecutorSteps Specifies a manual list of steps to execute. This should be specified when the atomic test cannot be executed in an automated fashion, for example when GUI steps are involved that cannot be automated. .PARAMETER ExecutorCommand Specifies the command to execute as part of the atomic test. This should be specified when the atomic test can be executed in an automated fashion. The -ExecutorType specified will dictate the command specified, e.g. PowerShell scriptblock code when the "PowerShell" ExecutorType is specified. .PARAMETER ExecutorCleanupCommand Specifies the command to execute if there are any artifacts that need to be cleaned up. .PARAMETER InputArguments Specifies one or more input arguments. Input arguments are defined using the New-AtomicTestInputArgument function. .PARAMETER DependencyExecutorType Specifies an override execution type for dependencies. By default, dependencies are executed using the framework specified in -ExecutorType. In most cases, 'PowerShell' is specified as a dependency executor type when 'CommandPrompt' is specified as an executor type. .PARAMETER Dependencies Specifies one or more dependencies. Dependencies are defined using the New-AtomicTestDependency function. .EXAMPLE $InputArg1 = New-AtomicTestInputArgument -Name filename -Description 'location of the payload' -Type Path -Default 'PathToAtomicsFolder\T1118\src\T1118.dll' $InputArg2 = New-AtomicTestInputArgument -Name source -Description 'location of the source code to compile' -Type Path -Default 'PathToAtomicsFolder\T1118\src\T1118.cs' $AtomicTest = New-AtomicTest -Name 'InstallUtil uninstall method call' -Description 'Executes the Uninstall Method' -SupportedPlatforms Windows -InputArguments $InputArg1, $InputArg2 -ExecutorType CommandPrompt -ExecutorCommand @' C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:library /out:#{filename} #{source} C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U #{filename} '@ .OUTPUTS AtomicTest Outputs an object representing an atomic test. This object is intended to be supplied to the New-AtomicTechnique -AtomicTests parameter. The output of New-AtomicTest can be piped to ConvertTo-Yaml. The resulting output can be added to an existing atomic technique YAML doc. #> [CmdletBinding(DefaultParameterSetName = 'AutomatedExecutor')] [OutputType([AtomicTest])] param ( [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $Name, [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $Description, [Parameter(Mandatory)] [String[]] [ValidateSet('Windows', 'macOS', 'Linux')] $SupportedPlatforms, [Parameter(Mandatory, ParameterSetName = 'AutomatedExecutor')] [String] [ValidateSet('CommandPrompt', 'Sh', 'Bash', 'PowerShell')] $ExecutorType, [Switch] $ExecutorElevationRequired, [Parameter(Mandatory, ParameterSetName = 'ManualExecutor')] [String] [ValidateNotNullOrEmpty()] $ExecutorSteps, [Parameter(Mandatory, ParameterSetName = 'AutomatedExecutor')] [String] [ValidateNotNullOrEmpty()] $ExecutorCommand, [String] [ValidateNotNullOrEmpty()] $ExecutorCleanupCommand, [AtomicInputArgument[]] $InputArguments, [String] [ValidateSet('CommandPrompt', 'Sh', 'Bash', 'PowerShell')] $DependencyExecutorType, [AtomicDependency[]] $Dependencies ) $AtomicTestInstance = [AtomicTest]::new() $AtomicTestInstance.name = $Name $AtomicTestInstance.description = $Description $AtomicTestInstance.supported_platforms = $SupportedPlatforms | ForEach-Object { $_.ToLower() } $StringsWithPotentialInputArgs = New-Object -TypeName 'System.Collections.Generic.List`1[String]' switch ($PSCmdlet.ParameterSetName) { 'AutomatedExecutor' { $ExecutorInstance = [AtomicExecutorDefault]::new() $ExecutorInstance.command = $ExecutorCommand $StringsWithPotentialInputArgs.Add($ExecutorCommand) } 'ManualExecutor' { $ExecutorInstance = [AtomicExecutorManual]::new() $ExecutorInstance.steps = $ExecutorSteps $StringsWithPotentialInputArgs.Add($ExecutorSteps) } } switch ($ExecutorType) { 'CommandPrompt' { $ExecutorInstance.name = 'command_prompt' } default { $ExecutorInstance.name = $ExecutorType.ToLower() } } if ($ExecutorCleanupCommand) { $ExecutorInstance.cleanup_command = $ExecutorCleanupCommand $StringsWithPotentialInputArgs.Add($ExecutorCleanupCommand) } if ($ExecutorElevationRequired) { $ExecutorInstance.elevation_required = $True } if ($Dependencies) { foreach ($Dependency in $Dependencies) { $StringsWithPotentialInputArgs.Add($Dependency.description) $StringsWithPotentialInputArgs.Add($Dependency.prereq_command) $StringsWithPotentialInputArgs.Add($Dependency.get_prereq_command) } } if ($DependencyExecutorType) { switch ($DependencyExecutorType) { 'CommandPrompt' { $AtomicTestInstance.dependency_executor_name = 'command_prompt' } default { $AtomicTestInstance.dependency_executor_name = $DependencyExecutorType.ToLower() } } } $AtomicTestInstance.dependencies = $Dependencies [Hashtable] $InputArgHashtable = @{ } if ($InputArguments.Count) { # Determine if any of the input argument names repeat. They must be unique. $InputArguments | Group-Object -Property Name | Where-Object { $_.Count -gt 1 } | ForEach-Object { Write-Error "There are $($_.Count) instances of the $($_.Name) input argument. Input argument names must be unique." return } # Convert each input argument to a hashtable where the key is the Name property. foreach ($InputArg in $InputArguments) { # Create a copy of the passed input argument that doesn't include the "Name" property. # Passing in a shallow copy adversely affects YAML serialization for some reason. $NewInputArg = [AtomicInputArgument]::new() $NewInputArg.default = $InputArg.default $NewInputArg.description = $InputArg.description $NewInputArg.type = $InputArg.type $InputArgHashtable[$InputArg.Name] = $NewInputArg } $AtomicTestInstance.input_arguments = $InputArgHashtable } # Extract all specified input arguments from executor and any dependencies. $Regex = [Regex] '#\{(?[^}]+)\}' [String[]] $InputArgumentNamesFromExecutor = $StringsWithPotentialInputArgs | ForEach-Object { $Regex.Matches($_) } | Select-Object -ExpandProperty Groups | Where-Object { $_.Name -eq 'ArgName' } | Select-Object -ExpandProperty Value | Sort-Object -Unique # Validate that all executor arguments are defined as input arguments if ($InputArgumentNamesFromExecutor.Count) { $InputArgumentNamesFromExecutor | ForEach-Object { if ($InputArgHashtable.Keys -notcontains $_) { Write-Error "The following input argument was specified but is not defined: '$_'" return } } } # Validate that all defined input args are utilized at least once in the executor. if ($InputArgHashtable.Keys.Count) { $InputArgHashtable.Keys | ForEach-Object { if ($InputArgumentNamesFromExecutor -notcontains $_) { # Write a warning since this scenario is not considered a breaking change Write-Warning "The following input argument is defined but not utilized: '$_'." } } } $AtomicTestInstance.executor = $ExecutorInstance return $AtomicTestInstance } function New-AtomicTestDependency { <# .SYNOPSIS Specifies a new dependency that must be met prior to execution of an atomic test. .PARAMETER Description Specifies a human-readable description of the dependency. This should be worded in the following form: SOMETHING must SOMETHING .PARAMETER PrereqCommand Specifies commands to check if prerequisites for running this test are met. For the "command_prompt" executor, if any command returns a non-zero exit code, the pre-requisites are not met. For the "powershell" executor, all commands are run as a script block and the script block must return 0 for success. .PARAMETER GetPrereqCommand Specifies commands to meet this prerequisite or a message describing how to meet this prereq More specifically, this command is designed to satisfy either of the following conditions: 1) If a prerequisite is not met, perform steps necessary to satify the prerequisite. Such a command should be implemented when prerequisites can be satisfied in an automated fashion. 2) If a prerequisite is not met, inform the user what the steps are to satisfy the prerequisite. Such a message should be presented to the user in the case that prerequisites cannot be satisfied in an automated fashion. .EXAMPLE $Dependency = New-AtomicTestDependency -Description 'Folder to zip must exist (#{input_file_folder})' -PrereqCommand 'test -e #{input_file_folder}' -GetPrereqCommand 'echo Please set input_file_folder argument to a folder that exists' .OUTPUTS AtomicDependency Outputs an object representing an atomic test dependency. This object is intended to be supplied to the New-AtomicTest -Dependencies parameter. Note: due to a bug in PowerShell classes, the get_prereq_command property will not display by default. If all fields must be explicitly displayed, they can be viewed by piping output to "Select-Object description, prereq_command, get_prereq_command". #> [CmdletBinding()] [OutputType([AtomicDependency])] param ( [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $Description, [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $PrereqCommand, [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $GetPrereqCommand ) $DependencyInstance = [AtomicDependency]::new() $DependencyInstance.description = $Description $DependencyInstance.prereq_command = $PrereqCommand $DependencyInstance.get_prereq_command = $GetPrereqCommand return $DependencyInstance } function New-AtomicTestInputArgument { <# .SYNOPSIS Specifies an input to an atomic test that is a requirement to run the test (think of these like function arguments). .PARAMETER Name Specifies the name of the input argument. This must be lowercase and can optionally, have underscores. The input argument name is what is specified as arguments within executors and dependencies. .PARAMETER Description Specifies a human-readable description of the input argument. .PARAMETER Type Specifies the data type of the input argument. The following data types are supported: Path, Url, String, Integer, Float. If an alternative data type must be supported, use the -TypeOverride parameter. .PARAMETER TypeOverride Specifies an unsupported input argument data type. Specifying this parameter should not be common. .PARAMETER Default Specifies a default value for an input argument if one is not specified via the Invoke-AtomicTest -InputArgs parameter. .EXAMPLE $AtomicInputArgument = New-AtomicTestInputArgument -Name 'rar_exe' -Type Path -Description 'The RAR executable from Winrar' -Default '%programfiles%\WinRAR\Rar.exe' .OUTPUTS AtomicInputArgument Outputs an object representing an atomic test input argument. This object is intended to be supplied to the New-AtomicTest -InputArguments parameter. #> [CmdletBinding(DefaultParameterSetName = 'PredefinedType')] [OutputType([AtomicInputArgument])] param ( [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $Name, [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $Description, [Parameter(Mandatory, ParameterSetName = 'PredefinedType')] [String] [ValidateSet('Path', 'Url', 'String', 'Integer', 'Float')] $Type, [Parameter(Mandatory, ParameterSetName = 'TypeOverride')] [String] [ValidateNotNullOrEmpty()] $TypeOverride, [Parameter(Mandatory)] [String] [ValidateNotNullOrEmpty()] $Default ) if ($Name -notmatch '^(?-i:[0-9a-z_]+)$') { Write-Error "Input argument names must be lowercase and optionally, contain underscores. Input argument name supplied: $Name" return } $AtomicInputArgInstance = [AtomicInputArgument]::new() $AtomicInputArgInstance.description = $Description $AtomicInputArgInstance.default = $Default if ($Type) { $AtomicInputArgInstance.type = $Type # Validate input argument types when it makes sense to do so. switch ($Type) { 'Url' { if (-not [Uri]::IsWellFormedUriString($Type, [UriKind]::RelativeOrAbsolute)) { Write-Warning "The specified Url is not properly formatted: $Type" } } 'Integer' { if (-not [Int]::TryParse($Type, [Ref] $null)) { Write-Warning "The specified Int is not properly formatted: $Type" } } 'Float' { if (-not [Double]::TryParse($Type, [Ref] $null)) { Write-Warning "The specified Float is not properly formatted: $Type" } } # The following supported data types do not make sense to validate: # 'Path' { } # 'String' { } } } else { $AtomicInputArgInstance.type = $TypeOverride } # Add Name as a note property since the Name property cannot be defined in the AtomicInputArgument # since it must be stored as a hashtable where the name is the key. Fortunately, ConvertTo-Yaml # won't convert note properties during serialization. $InputArgument = Add-Member -InputObject $AtomicInputArgInstance -MemberType NoteProperty -Name Name -Value $Name -PassThru return $InputArgument } ScriptBlock ID: e3c52026-5ff5-4983-b128-c4fd5244dc01 Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\New-Atomic.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=3 Type=Warning ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532380 Keywords=None Message=Creating Scriptblock text (1 of 1): function Invoke-WebRequestVerifyHash ($url, $outfile, $hash) { $success = $false $null = @( New-Item -ItemType Directory (Split-Path $outfile) -Force | Out-Null $ms = New-Object IO.MemoryStream [Net.ServicePointManager]::SecurityProtocol = ([Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12) (New-Object System.Net.WebClient).OpenRead($url).copyto($ms) $ms.seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null $actualHash = (Get-FileHash -InputStream $ms).Hash if ( $hash -eq $actualHash) { $ms.seek(0, [System.IO.SeekOrigin]::Begin) | Out-Null $fileStream = New-Object IO.FileStream $outfile, ([System.IO.FileMode]::Create) $ms.CopyTo($fileStream); $fileStream.Close() $success = $true } else { Write-Host -ForegroundColor red "File hash mismatch, expected: $hash, actual: $actualHash" } ) $success } ScriptBlock ID: 0851aac5-da65-4f30-acf7-a7eaad334db1 Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Invoke-WebRequestVerifyHash.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532377 Keywords=None Message=Creating Scriptblock text (1 of 1): <# .SYNOPSIS Invokes specified Atomic test(s) .DESCRIPTION Invokes specified Atomic tests(s). Optionally, you can specify if you want to list the details of the Atomic test(s) only. .EXAMPLE Check if Prerequisites for Atomic Test are met PS/> Invoke-AtomicTest T1117 -CheckPrereqs .EXAMPLE Invokes Atomic Test PS/> Invoke-AtomicTest T1117 .EXAMPLE Run the Cleanup Commmand for the given Atomic Test PS/> Invoke-AtomicTest T1117 -Cleanup .EXAMPLE Generate Atomic Test (Output Test Definition Details) PS/> Invoke-AtomicTest T1117 -ShowDetails .EXAMPLE Invoke a test and flow the standard/error output to the console PS/> Invoke-AtomicTest T1117 -Interactive .EXAMPLE Invoke a test and keep standard/error output files for later processing. This edge case has specific requirements. See https://github.com/redcanaryco/invoke-atomicredteam/issues/60 PS/> Invoke-AtomicTest T1117 -KeepStdOutStdErrFiles .NOTES Create Atomic Tests from yaml files described in Atomic Red Team. https://github.com/redcanaryco/atomic-red-team/tree/master/atomics .LINK Installation and Usage Wiki: https://github.com/redcanaryco/invoke-atomicredteam/wiki Github repo: https://github.com/redcanaryco/invoke-atomicredteam #> function Invoke-AtomicTest { [CmdletBinding(DefaultParameterSetName = 'technique', SupportsShouldProcess = $true, PositionalBinding = $false, ConfirmImpact = 'Medium')] Param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [ValidateNotNullOrEmpty()] [String] $AtomicTechnique, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $ShowDetails, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $ShowDetailsBrief, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [String[]] $TestNumbers, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [String[]] $TestNames, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [String[]] $TestGuids, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [String] $PathToAtomicsFolder = $( if ($IsLinux -or $IsMacOS) { $Env:HOME + "/AtomicRedTeam/atomics" } else { $env:HOMEDRIVE + "\AtomicRedTeam\atomics" }), [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $CheckPrereqs = $false, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $PromptForInputArgs = $false, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $GetPrereqs = $false, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $Cleanup = $false, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [switch] $NoExecutionLog = $false, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [String] $ExecutionLogPath = $( if ($IsLinux -or $IsMacOS) { "/tmp/Invoke-AtomicTest-ExecutionLog.csv" } else { "$env:TEMP\Invoke-AtomicTest-ExecutionLog.csv" }), [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [switch] $Force, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [HashTable] $InputArgs, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [Int] $TimeoutSeconds = 120, [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [System.Management.Automation.Runspaces.PSSession[]]$Session, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'technique')] [switch] $KeepStdOutStdErrFiles = $false ) BEGIN { } # Intentionally left blank and can be removed PROCESS { $PathToAtomicsFolder = (Resolve-Path $PathToAtomicsFolder).Path Write-Verbose -Message 'Attempting to run Atomic Techniques' Write-Host -ForegroundColor Cyan "PathToAtomicsFolder = $PathToAtomicsFolder`n" $executionPlatform, $isElevated, $tmpDir, $executionHostname, $executionUser = Get-TargetInfo $Session $PathToPayloads = if ($Session) { "$tmpDir`AtomicRedTeam" } else { $PathToAtomicsFolder } function Platform-IncludesCloud { $cloud = ('office-365', 'azure-ad', 'google-workspace', 'saas', 'iaas', 'containers', 'iaas:aws', 'iaas:azure', 'iaas:gcp') foreach ($platform in $test.supported_platforms) { if ($cloud -contains $platform) { return $true } } return $false } function Invoke-AtomicTestSingle ($AT) { $AT = $AT.ToUpper() $pathToYaml = Join-Path $PathToAtomicsFolder "\$AT\$AT.yaml" if (Test-Path -Path $pathToYaml) { $AtomicTechniqueHash = Get-AtomicTechnique -Path $pathToYaml } else { Write-Host -Fore Red "ERROR: $PathToYaml does not exist`nCheck your Atomic Number and your PathToAtomicsFolder parameter" return } $techniqueCount = 0 foreach ($technique in $AtomicTechniqueHash) { $techniqueCount++ $props = @{ Activity = "Running $($technique.display_name.ToString()) Technique" Status = 'Progress:' PercentComplete = ($techniqueCount / ($AtomicTechniqueHash).Count * 100) } Write-Progress @props Write-Debug -Message "Gathering tests for Technique $technique" $testCount = 0 foreach ($test in $technique.atomic_tests) { Write-Verbose -Message 'Determining tests for target platform' $testCount++ if ( -not $(Platform-IncludesCloud) -and -Not $test.supported_platforms.Contains($executionPlatform) ) { Write-Verbose -Message "Unable to run non-$executionPlatform tests" continue } if ( $executionPlatform -eq "windows" -and ($test.executor.name -eq "sh" -or $test.executor.name -eq "bash")) { Write-Verbose -Message "Unable to run sh or bash on $executionPlatform" continue } if ( ("linux", "macos") -contains $executionPlatform -and $test.executor.name -eq "command_prompt") { Write-Verbose -Message "Unable to run cmd.exe on $executionPlatform" continue } if ($null -ne $TestNumbers) { if (-Not ($TestNumbers -contains $testCount) ) { continue } } if ($null -ne $TestNames) { if (-Not ($TestNames -contains $test.name) ) { continue } } if ($null -ne $TestGuids) { if (-Not ($TestGuids -contains $test.auto_generated_guid) ) { continue } } $props = @{ Activity = 'Running Atomic Tests' Status = 'Progress:' PercentComplete = ($testCount / ($technique.atomic_tests).Count * 100) } Write-Progress @props Write-Verbose -Message 'Determining manual tests' if ($test.executor.name.Contains('manual')) { Write-Verbose -Message 'Unable to run manual tests' continue } $testId = "$AT-$testCount $($test.name)" if ($ShowDetailsBrief) { Write-KeyValue $testId continue } if ($PromptForInputArgs) { $InputArgs = Invoke-PromptForInputArgs $test.input_arguments } if ($ShowDetails) { Show-Details $test $testCount $technique $InputArgs $PathToPayloads continue } Write-Debug -Message 'Gathering final Atomic test command' if ($CheckPrereqs) { Write-KeyValue "CheckPrereq's for: " $testId $failureReasons = Invoke-CheckPrereqs $test $isElevated $InputArgs $PathToPayloads $TimeoutSeconds $session Write-PrereqResults $FailureReasons $testId } elseif ($GetPrereqs) { Write-KeyValue "GetPrereq's for: " $testId if ( $test.executor.elevation_required -and -not $isElevated) { Write-Host -ForegroundColor Red "Elevation required but not provided" } if ($nul -eq $test.dependencies) { Write-KeyValue "No Preqs Defined"; continue } foreach ($dep in $test.dependencies) { $executor = Get-PrereqExecutor $test $description = (Merge-InputArgs $dep.description $test $InputArgs $PathToPayloads).trim() Write-KeyValue "Attempting to satisfy prereq: " $description $final_command_prereq = Merge-InputArgs $dep.prereq_command $test $InputArgs $PathToPayloads if ($executor -ne "powershell") { $final_command_prereq = ($final_command_prereq.trim()).Replace("`n", " && ") } $final_command_get_prereq = Merge-InputArgs $dep.get_prereq_command $test $InputArgs $PathToPayloads $res = Invoke-ExecuteCommand $final_command_prereq $executor $TimeoutSeconds $session -Interactive:$true if ($res -eq 0) { Write-KeyValue "Prereq already met: " $description } else { $res = Invoke-ExecuteCommand $final_command_get_prereq $executor $TimeoutSeconds $session -Interactive:$Interactive $res = Invoke-ExecuteCommand $final_command_prereq $executor $TimeoutSeconds $session -Interactive:$true if ($res -eq 0) { Write-KeyValue "Prereq successfully met: " $description } else { Write-Host -ForegroundColor Red "Failed to meet prereq: $description" } } } } elseif ($Cleanup) { Write-KeyValue "Executing cleanup for test: " $testId $final_command = Merge-InputArgs $test.executor.cleanup_command $test $InputArgs $PathToPayloads $res = Invoke-ExecuteCommand $final_command $test.executor.name $TimeoutSeconds $session -Interactive:$Interactive Write-KeyValue "Done executing cleanup for test: " $testId } else { Write-KeyValue "Executing test: " $testId $startTime = get-date $final_command = Merge-InputArgs $test.executor.command $test $InputArgs $PathToPayloads $res = Invoke-ExecuteCommand $final_command $test.executor.name $TimeoutSeconds $session -Interactive:$Interactive Write-ExecutionLog $startTime $AT $testCount $test.name $ExecutionLogPath $executionHostname $executionUser $test.auto_generated_guid Write-KeyValue "Done executing test: " $testId } if ($session) { write-output (Invoke-Command -Session $session -scriptblock { (Get-Content $($Using:tmpDir + "art-out.txt")) -replace '\x00', ''; (Get-Content $($Using:tmpDir + "art-err.txt")) -replace '\x00', ''; if (-not $KeepStdOutStdErrFiles) { Remove-Item $($Using:tmpDir + "art-out.txt"), $($Using:tmpDir + "art-err.txt") -Force -ErrorAction Ignore } }) } elseif (-not $interactive) { # It is possible to have a null $session BUT also have stdout and stderr captured from # the executed command. IF so then write the output to the pipe and cleanup the files. $stdoutFilename = $tmpDir + "art-out.txt" if (Test-Path $stdoutFilename -PathType leaf) { Write-Output ((Get-Content $stdoutFilename) -replace '\x00', '') if (-not $KeepStdOutStdErrFiles) { Remove-Item $stdoutFilename } } $stderrFilename = $tmpDir + "art-err.txt" if (Test-Path $stderrFilename -PathType leaf) { Write-Output ((Get-Content $stderrFilename) -replace '\x00', '') if (-not $KeepStdOutStdErrFiles) { Remove-Item $stderrFilename } } } } # End of foreach Test in single Atomic Technique } # End of foreach Technique in Atomic Tests } # End of Invoke-AtomicTestSingle function if ($AtomicTechnique -eq "All") { function Invoke-AllTests() { $AllAtomicTests = New-Object System.Collections.ArrayList Get-ChildItem $PathToAtomicsFolder -Directory -Filter T* | ForEach-Object { $currentTechnique = [System.IO.Path]::GetFileName($_.FullName) if ( $currentTechnique -match "T[0-9]{4}.?([0-9]{3})?" ) { $AllAtomicTests.Add($currentTechnique) | Out-Null } } $AllAtomicTests.GetEnumerator() | Foreach-Object { Invoke-AtomicTestSingle $_ } } if ( ($Force -or $CheckPrereqs -or $ShowDetails -or $ShowDetailsBrief -or $GetPrereqs) -or $psCmdlet.ShouldContinue( 'Do you wish to execute all tests?', "Highway to the danger zone, Executing All Atomic Tests!" ) ) { Invoke-AllTests } } else { Invoke-AtomicTestSingle $AtomicTechnique } } # End of PROCESS block END { } # Intentionally left blank and can be removed } ScriptBlock ID: 6e17cb0b-1a46-40e0-9091-06ad52d3f44c Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Invoke-AtomicTest.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532374 Keywords=None Message=Creating Scriptblock text (2 of 2): } if (-not ($AtomicTest['executor']['steps'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor.steps' element must be a string." return } $ExecutorInstance = [AtomicExecutorManual]::new() $ExecutorInstance.steps = $AtomicTest['executor']['steps'] $StringsWithPotentialInputArgs.Add($AtomicTest['executor']['steps']) } else { if (-not $AtomicTest['executor'].ContainsKey('command')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor.command' element is required when the '$($ValidExecutorTypes -join ', ')' executors are used." return } if (-not ($AtomicTest['executor']['command'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor.command' element must be a string." return } $ExecutorInstance = [AtomicExecutorDefault]::new() $ExecutorInstance.command = $AtomicTest['executor']['command'] $StringsWithPotentialInputArgs.Add($AtomicTest['executor']['command']) } # cleanup_command element is optional if ($AtomicTest['executor'].ContainsKey('cleanup_command')) { $ExecutorInstance.cleanup_command = $AtomicTest['executor']['cleanup_command'] $StringsWithPotentialInputArgs.Add($AtomicTest['executor']['cleanup_command']) } # elevation_required element is optional if ($AtomicTest['executor'].ContainsKey('elevation_required')) { if (-not ($AtomicTest['executor']['elevation_required'] -is [Bool])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor.elevation_required' element must be a boolean." return } $ExecutorInstance.elevation_required = $AtomicTest['executor']['elevation_required'] } else { # if elevation_required is not present, default to false $ExecutorInstance.elevation_required = $False } $InputArgumentNames = $null # Get all input argument names $InputArgumentNames = $InputArguments.Keys # Extract all input arguments names from the executor # Potential places where input arguments can be populated: # - Dependency description # - Dependency prereq_command # - Dependency get_prereq_command # - Executor steps # - Executor command # - Executor cleanup_command $Regex = [Regex] '#\{(?[^}]+)\}' [String[]] $InputArgumentNamesFromExecutor = $StringsWithPotentialInputArgs | ForEach-Object { $Regex.Matches($_) } | Select-Object -ExpandProperty Groups | Where-Object { $_.Name -eq 'ArgName' } | Select-Object -ExpandProperty Value | Sort-Object -Unique # Validate that all executor input arg names are defined input arg names. if ($InputArgumentNamesFromExecutor.Count) { $InputArgumentNamesFromExecutor | ForEach-Object { if ($InputArgumentNames -notcontains $_) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] The following input argument was specified but is not defined: '$_'" return } } } # Validate that all defined input args are utilized at least once in the executor. if ($InputArgumentNames.Count) { $InputArgumentNames | ForEach-Object { if ($InputArgumentNamesFromExecutor -notcontains $_) { # Write a warning since this scenario is not considered a breaking change Write-Warning "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] The following input argument is defined but not utilized: '$_'." } } } $ExecutorInstance.name = $AtomicTest['executor']['name'] $AtomicTestInstance.executor = $ExecutorInstance $AtomicTests[$i] = $AtomicTestInstance } $AtomicInstance.atomic_tests = $AtomicTests $AtomicInstance } } ScriptBlock ID: a4a69630-66df-417e-beae-b19854f68fa4 Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Get-AtomicTechnique.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532373 Keywords=None Message=Creating Scriptblock text (1 of 2): filter Get-AtomicTechnique { <# .SYNOPSIS Retrieve and validate an atomic technique. .DESCRIPTION Get-AtomicTechnique retrieves and validates one or more atomic techniques. Get-AtomicTechnique supports retrieval from YAML files or from a raw YAML string. This function facilitates the following use cases: 1) Validation prior to execution of atomic tests. 2) Writing code to reason over one or more atomic techniques/tests. 3) Representing atomic techniques/tests in a format that is more conducive to PowerShell. ConvertFrom-Yaml returns a large, complicated hashtable that is difficult to work with and reason over. Get-AtomicTechnique helps abstract those challenges away. 4) Representing atomic techniques/tests in a format that can be piped directly to ConvertTo-Yaml. .PARAMETER Path Specifies the path to an atomic technique YAML file. Get-AtomicTechnique expects that the file extension be .yaml or .yml and that it is well-formed YAML content. .PARAMETER Yaml Specifies a single string consisting of raw atomic technique YAML. .EXAMPLE Get-ChildItem -Path C:\atomic-red-team\atomics\* -Recurse -Include 'T*.yaml' | Get-AtomicTechnique .EXAMPLE Get-Item C:\atomic-red-team\atomics\T1117\T1117.yaml | Get-AtomicTechnique .EXAMPLE Get-AtomicTechnique -Path C:\atomic-red-team\atomics\T1117\T1117.yaml .EXAMPLE $Yaml = @' --- attack_technique: T1152 display_name: Launchctl atomic_tests: - name: Launchctl description: | Utilize launchctl supported_platforms: - macos executor: name: sh command: | launchctl submit -l evil -- /Applications/Calculator.app/Contents/MacOS/Calculator '@ Get-AtomicTechnique -Yaml $Yaml .INPUTS System.IO.FileInfo The output of Get-Item and Get-ChildItem can be piped directly into Get-AtomicTechnique. .OUTPUTS AtomicTechnique Outputs an object representing a parsed and validated atomic technique. #> [CmdletBinding(DefaultParameterSetName = 'FilePath')] [OutputType([AtomicTechnique])] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'FilePath')] [String] [Alias('FullName')] [ValidateScript({ Test-Path -Path $_ -Include '*.yaml', '*.yml' })] $Path, [Parameter(Mandatory, ParameterSetName = 'Yaml')] [String] [ValidateNotNullOrEmpty()] $Yaml ) switch ($PSCmdlet.ParameterSetName) { 'FilePath' { $ResolvedPath = Resolve-Path -Path $Path $YamlContent = Get-Content -Path $ResolvedPath -Raw $ErrorStringPrefix = "[$($ResolvedPath)]" } 'Yaml' { $YamlContent = $Yaml $ErrorStringPrefix = '' } } $ParsedYaml = $null $ValidSupportedPlatforms = @('windows', 'macos', 'linux', 'office-365', 'azure-ad', 'google-workspace', 'saas', 'iaas', 'containers', 'iaas:aws', 'iaas:azure', 'iaas:gcp') $ValidInputArgTypes = @('Path', 'Url', 'String', 'Integer', 'Float') $ValidExecutorTypes = @('command_prompt', 'sh', 'bash', 'powershell', 'manual') # ConvertFrom-Yaml will throw a .NET exception rather than a PowerShell error. # Capture the exception and convert to PowerShell error so that the user can decide # how to handle the error. try { [Hashtable] $ParsedYaml = ConvertFrom-Yaml -Yaml $YamlContent } catch { Write-Error $_ } if ($ParsedYaml) { # The document was well-formed YAML. Now, validate against the atomic red schema $AtomicInstance = [AtomicTechnique]::new() if (-not $ParsedYaml.Count) { Write-Error "$ErrorStringPrefix YAML file has no elements." return } if (-not $ParsedYaml.ContainsKey('attack_technique')) { Write-Error "$ErrorStringPrefix 'attack_technique' element is required." return } $AttackTechnique = $null if ($ParsedYaml['attack_technique'].Count -gt 1) { # An array of attack techniques are supported. foreach ($Technique in $ParsedYaml['attack_technique']) { if ("$Technique" -notmatch '^(?-i:T\d{4}(\.\d{3}){0,1})$') { Write-Warning "$ErrorStringPrefix Attack technique: $Technique. Each attack technique should start with the letter 'T' followed by a four digit number." } [String[]] $AttackTechnique = $ParsedYaml['attack_technique'] } } else { if ((-not "$($ParsedYaml['attack_technique'])".StartsWith('T'))) { # If the attack technique is a single entry, validate that it starts with the letter T. Write-Warning "$ErrorStringPrefix Attack technique: $($ParsedYaml['attack_technique']). Attack techniques should start with the letter T." } [String] $AttackTechnique = $ParsedYaml['attack_technique'] } $AtomicInstance.attack_technique = $AttackTechnique if (-not $ParsedYaml.ContainsKey('display_name')) { Write-Error "$ErrorStringPrefix 'display_name' element is required." return } if (-not ($ParsedYaml['display_name'] -is [String])) { Write-Error "$ErrorStringPrefix 'display_name' must be a string." return } $AtomicInstance.display_name = $ParsedYaml['display_name'] if (-not $ParsedYaml.ContainsKey('atomic_tests')) { Write-Error "$ErrorStringPrefix 'atomic_tests' element is required." return } if (-not ($ParsedYaml['atomic_tests'] -is [System.Collections.Generic.List`1[Object]])) { Write-Error "$ErrorStringPrefix 'atomic_tests' element must be an array." return } $AtomicTests = [AtomicTest[]]::new($ParsedYaml['atomic_tests'].Count) if (-not $ParsedYaml['atomic_tests'].Count) { Write-Error "$ErrorStringPrefix 'atomic_tests' element is empty - you have no tests." return } for ($i = 0; $i -lt $ParsedYaml['atomic_tests'].Count; $i++) { $AtomicTest = $ParsedYaml['atomic_tests'][$i] $AtomicTestInstance = [AtomicTest]::new() $StringsWithPotentialInputArgs = New-Object -TypeName 'System.Collections.Generic.List`1[String]' if (-not $AtomicTest.ContainsKey('name')) { Write-Error "$ErrorStringPrefix 'atomic_tests[$i].name' element is required." return } if (-not ($AtomicTest['name'] -is [String])) { Write-Error "$ErrorStringPrefix 'atomic_tests[$i].name' element must be a string." return } $AtomicTestInstance.name = $AtomicTest['name'] $AtomicTestInstance.auto_generated_guid = $AtomicTest['auto_generated_guid'] if (-not $AtomicTest.ContainsKey('description')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].description' element is required." return } if (-not ($AtomicTest['description'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].description' element must be a string." return } $AtomicTestInstance.description = $AtomicTest['description'] if (-not $AtomicTest.ContainsKey('supported_platforms')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].supported_platforms' element is required." return } if (-not ($AtomicTest['supported_platforms'] -is [System.Collections.Generic.List`1[Object]])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].supported_platforms' element must be an array." return } foreach ($SupportedPlatform in $AtomicTest['supported_platforms']) { if ($ValidSupportedPlatforms -cnotcontains $SupportedPlatform) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].supported_platforms': '$SupportedPlatform' must be one of the following: $($ValidSupportedPlatforms -join ', ')." return } } $AtomicTestInstance.supported_platforms = $AtomicTest['supported_platforms'] $Dependencies = $null if ($AtomicTest['dependencies'].Count) { $Dependencies = [AtomicDependency[]]::new($AtomicTest['dependencies'].Count) $j = 0 # dependencies are optional and there can be multiple foreach ($Dependency in $AtomicTest['dependencies']) { $DependencyInstance = [AtomicDependency]::new() if (-not $Dependency.ContainsKey('description')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].dependencies[$j].description' element is required." return } if (-not ($Dependency['description'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].dependencies[$j].description' element must be a string." return } $DependencyInstance.description = $Dependency['description'] $StringsWithPotentialInputArgs.Add($Dependency['description']) if (-not $Dependency.ContainsKey('prereq_command')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].dependencies[$j].prereq_command' element is required." return } if (-not ($Dependency['prereq_command'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].dependencies[$j].prereq_command' element must be a string." return } $DependencyInstance.prereq_command = $Dependency['prereq_command'] $StringsWithPotentialInputArgs.Add($Dependency['prereq_command']) if (-not $Dependency.ContainsKey('get_prereq_command')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].dependencies[$j].get_prereq_command' element is required." return } if (-not ($Dependency['get_prereq_command'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].dependencies[$j].get_prereq_command' element must be a string." return } $DependencyInstance.get_prereq_command = $Dependency['get_prereq_command'] $StringsWithPotentialInputArgs.Add($Dependency['get_prereq_command']) $Dependencies[$j] = $DependencyInstance $j++ } $AtomicTestInstance.dependencies = $Dependencies } if ($AtomicTest.ContainsKey('dependency_executor_name')) { if ($ValidExecutorTypes -notcontains $AtomicTest['dependency_executor_name']) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].dependency_executor_name': '$($AtomicTest['dependency_executor_name'])' must be one of the following: $($ValidExecutorTypes -join ', ')." return } if ($null -eq $AtomicTestInstance.Dependencies) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] If 'atomic_tests[$i].dependency_executor_name' is defined, there must be at least one dependency defined." } $AtomicTestInstance.dependency_executor_name = $AtomicTest['dependency_executor_name'] } $InputArguments = $null # input_arguments is optional if ($AtomicTest.ContainsKey('input_arguments')) { if (-not ($AtomicTest['input_arguments'] -is [Hashtable])) { $AtomicTest['input_arguments'].GetType().FullName Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].input_arguments' must be a hashtable." return } if (-not ($AtomicTest['input_arguments'].Count)) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].input_arguments' must have at least one entry." return } $InputArguments = @{} $j = 0 foreach ($InputArgName in $AtomicTest['input_arguments'].Keys) { $InputArgument = [AtomicInputArgument]::new() if (-not $AtomicTest['input_arguments'][$InputArgName].ContainsKey('description')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].input_arguments['$InputArgName'].description' element is required." return } if (-not ($AtomicTest['input_arguments'][$InputArgName]['description'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].input_arguments['$InputArgName'].description' element must be a string." return } $InputArgument.description = $AtomicTest['input_arguments'][$InputArgName]['description'] if (-not $AtomicTest['input_arguments'][$InputArgName].ContainsKey('type')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].input_arguments['$InputArgName'].type' element is required." return } if ($ValidInputArgTypes -notcontains $AtomicTest['input_arguments'][$InputArgName]['type']) { Write-Warning "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].input_arguments['$InputArgName'].type': '$($AtomicTest['input_arguments'][$InputArgName]['type'])' should be one of the following: $($ValidInputArgTypes -join ', ')" } $InputArgument.type = $AtomicTest['input_arguments'][$InputArgName]['type'] if (-not $AtomicTest['input_arguments'][$InputArgName].ContainsKey('default')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].input_arguments['$InputArgName'].default' element is required." return } $InputArgument.default = $AtomicTest['input_arguments'][$InputArgName]['default'] $InputArguments[$InputArgName] = $InputArgument $j++ } } $AtomicTestInstance.input_arguments = $InputArguments if (-not $AtomicTest.ContainsKey('executor')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor' element is required." return } if (-not ($AtomicTest['executor'] -is [Hashtable])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor' element must be a hashtable." return } if (-not $AtomicTest['executor'].ContainsKey('name')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor.name' element is required." return } if (-not ($AtomicTest['executor']['name'] -is [String])) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].description.name' element must be a string." return } if ($AtomicTest['executor']['name'] -notmatch '^(?-i:[a-z_]+)$') { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].description.name' element must be lowercased and underscored." return } if ($ValidExecutorTypes -notcontains $AtomicTest['executor']['name']) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].description.name': '$($AtomicTest['executor']['name'])' must be one of the following: $($ValidExecutorTypes -join ', ')" return } if ($AtomicTest['executor']['name'] -eq 'manual') { if (-not $AtomicTest['executor'].ContainsKey('steps')) { Write-Error "$ErrorStringPrefix[Atomic test name: $($AtomicTestInstance.name)] 'atomic_tests[$i].executor.steps' element is required when the 'manual' executor is used." return ScriptBlock ID: a4a69630-66df-417e-beae-b19854f68fa4 Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Get-AtomicTechnique.ps1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532367 Keywords=None Message=Creating Scriptblock text (1 of 1): #requires -Version 5.0 #Get public and private function definition files. $Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -Recurse -ErrorAction SilentlyContinue ) $Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -Recurse -Exclude "AtomicClassSchema.ps1" -ErrorAction SilentlyContinue ) # Make sure the Atomic Class Schema is available first (a workaround so PSv5.0 doesn't give errors) . "$PSScriptRoot\Private\AtomicClassSchema.ps1" #Dot source the files Foreach ($import in @($Public + $Private)) { Try { . $import.fullname } Catch { Write-Error -Message "Failed to import function $($import.fullname): $_" } } ScriptBlock ID: 8dca2bf0-ca1c-4faf-8af5-70cd44455af1 Path: C:\AtomicRedTeam\invoke-atomicredteam\Invoke-AtomicRedTeam.psm1 09/14/2021 04:30:49 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123532364 Keywords=None Message=Creating Scriptblock text (1 of 1): class AtomicDependency { [String] $description [String] $prereq_command [String] $get_prereq_command } class AtomicInputArgument { [String] $description [String] $type [String] $default } class AtomicExecutorBase { [String] $name [Bool] $elevation_required # Implemented to facilitate improved PS object display [String] ToString(){ return $this.Name } } class AtomicExecutorDefault : AtomicExecutorBase { [String] $command [String] $cleanup_command } class AtomicExecutorManual : AtomicExecutorBase { [String] $steps [String] $cleanup_command } class AtomicTest { [String] $name [String] $auto_generated_guid [String] $description [String[]] $supported_platforms # I wish this didn't have to be a hashtable but I don't # want to change the schema and introduce a breaking change. [Hashtable] $input_arguments [String] $dependency_executor_name [AtomicDependency[]] $dependencies [AtomicExecutorBase] $executor # Implemented to facilitate improved PS object display [String] ToString(){ return $this.name } } class AtomicTechnique { [String[]] $attack_technique [String] $display_name [AtomicTest[]] $atomic_tests } ScriptBlock ID: 9a0bab58-7b40-43f2-81ca-7fca8f2003ca Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\AtomicClassSchema.ps1 09/14/2021 04:30:56 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533500 Keywords=None Message=Creating Scriptblock text (1 of 1): { Test-Path -Path $_ -Include '*.yaml', '*.yml' } ScriptBlock ID: ecd6a9f9-2da6-4ed0-9c90-aaaf2975a9ec Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Get-AtomicTechnique.ps1 09/14/2021 04:30:56 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533496 Keywords=None Message=Creating Scriptblock text (1 of 1): function Invoke-AtomicTestSingle ($AT) { $AT = $AT.ToUpper() $pathToYaml = Join-Path $PathToAtomicsFolder "\$AT\$AT.yaml" if (Test-Path -Path $pathToYaml) { $AtomicTechniqueHash = Get-AtomicTechnique -Path $pathToYaml } else { Write-Host -Fore Red "ERROR: $PathToYaml does not exist`nCheck your Atomic Number and your PathToAtomicsFolder parameter" return } $techniqueCount = 0 foreach ($technique in $AtomicTechniqueHash) { $techniqueCount++ $props = @{ Activity = "Running $($technique.display_name.ToString()) Technique" Status = 'Progress:' PercentComplete = ($techniqueCount / ($AtomicTechniqueHash).Count * 100) } Write-Progress @props Write-Debug -Message "Gathering tests for Technique $technique" $testCount = 0 foreach ($test in $technique.atomic_tests) { Write-Verbose -Message 'Determining tests for target platform' $testCount++ if ( -not $(Platform-IncludesCloud) -and -Not $test.supported_platforms.Contains($executionPlatform) ) { Write-Verbose -Message "Unable to run non-$executionPlatform tests" continue } if ( $executionPlatform -eq "windows" -and ($test.executor.name -eq "sh" -or $test.executor.name -eq "bash")) { Write-Verbose -Message "Unable to run sh or bash on $executionPlatform" continue } if ( ("linux", "macos") -contains $executionPlatform -and $test.executor.name -eq "command_prompt") { Write-Verbose -Message "Unable to run cmd.exe on $executionPlatform" continue } if ($null -ne $TestNumbers) { if (-Not ($TestNumbers -contains $testCount) ) { continue } } if ($null -ne $TestNames) { if (-Not ($TestNames -contains $test.name) ) { continue } } if ($null -ne $TestGuids) { if (-Not ($TestGuids -contains $test.auto_generated_guid) ) { continue } } $props = @{ Activity = 'Running Atomic Tests' Status = 'Progress:' PercentComplete = ($testCount / ($technique.atomic_tests).Count * 100) } Write-Progress @props Write-Verbose -Message 'Determining manual tests' if ($test.executor.name.Contains('manual')) { Write-Verbose -Message 'Unable to run manual tests' continue } $testId = "$AT-$testCount $($test.name)" if ($ShowDetailsBrief) { Write-KeyValue $testId continue } if ($PromptForInputArgs) { $InputArgs = Invoke-PromptForInputArgs $test.input_arguments } if ($ShowDetails) { Show-Details $test $testCount $technique $InputArgs $PathToPayloads continue } Write-Debug -Message 'Gathering final Atomic test command' if ($CheckPrereqs) { Write-KeyValue "CheckPrereq's for: " $testId $failureReasons = Invoke-CheckPrereqs $test $isElevated $InputArgs $PathToPayloads $TimeoutSeconds $session Write-PrereqResults $FailureReasons $testId } elseif ($GetPrereqs) { Write-KeyValue "GetPrereq's for: " $testId if ( $test.executor.elevation_required -and -not $isElevated) { Write-Host -ForegroundColor Red "Elevation required but not provided" } if ($nul -eq $test.dependencies) { Write-KeyValue "No Preqs Defined"; continue } foreach ($dep in $test.dependencies) { $executor = Get-PrereqExecutor $test $description = (Merge-InputArgs $dep.description $test $InputArgs $PathToPayloads).trim() Write-KeyValue "Attempting to satisfy prereq: " $description $final_command_prereq = Merge-InputArgs $dep.prereq_command $test $InputArgs $PathToPayloads if ($executor -ne "powershell") { $final_command_prereq = ($final_command_prereq.trim()).Replace("`n", " && ") } $final_command_get_prereq = Merge-InputArgs $dep.get_prereq_command $test $InputArgs $PathToPayloads $res = Invoke-ExecuteCommand $final_command_prereq $executor $TimeoutSeconds $session -Interactive:$true if ($res -eq 0) { Write-KeyValue "Prereq already met: " $description } else { $res = Invoke-ExecuteCommand $final_command_get_prereq $executor $TimeoutSeconds $session -Interactive:$Interactive $res = Invoke-ExecuteCommand $final_command_prereq $executor $TimeoutSeconds $session -Interactive:$true if ($res -eq 0) { Write-KeyValue "Prereq successfully met: " $description } else { Write-Host -ForegroundColor Red "Failed to meet prereq: $description" } } } } elseif ($Cleanup) { Write-KeyValue "Executing cleanup for test: " $testId $final_command = Merge-InputArgs $test.executor.cleanup_command $test $InputArgs $PathToPayloads $res = Invoke-ExecuteCommand $final_command $test.executor.name $TimeoutSeconds $session -Interactive:$Interactive Write-KeyValue "Done executing cleanup for test: " $testId } else { Write-KeyValue "Executing test: " $testId $startTime = get-date $final_command = Merge-InputArgs $test.executor.command $test $InputArgs $PathToPayloads $res = Invoke-ExecuteCommand $final_command $test.executor.name $TimeoutSeconds $session -Interactive:$Interactive Write-ExecutionLog $startTime $AT $testCount $test.name $ExecutionLogPath $executionHostname $executionUser $test.auto_generated_guid Write-KeyValue "Done executing test: " $testId } if ($session) { write-output (Invoke-Command -Session $session -scriptblock { (Get-Content $($Using:tmpDir + "art-out.txt")) -replace '\x00', ''; (Get-Content $($Using:tmpDir + "art-err.txt")) -replace '\x00', ''; if (-not $KeepStdOutStdErrFiles) { Remove-Item $($Using:tmpDir + "art-out.txt"), $($Using:tmpDir + "art-err.txt") -Force -ErrorAction Ignore } }) } elseif (-not $interactive) { # It is possible to have a null $session BUT also have stdout and stderr captured from # the executed command. IF so then write the output to the pipe and cleanup the files. $stdoutFilename = $tmpDir + "art-out.txt" if (Test-Path $stdoutFilename -PathType leaf) { Write-Output ((Get-Content $stdoutFilename) -replace '\x00', '') if (-not $KeepStdOutStdErrFiles) { Remove-Item $stdoutFilename } } $stderrFilename = $tmpDir + "art-err.txt" if (Test-Path $stderrFilename -PathType leaf) { Write-Output ((Get-Content $stderrFilename) -replace '\x00', '') if (-not $KeepStdOutStdErrFiles) { Remove-Item $stderrFilename } } } } # End of foreach Test in single Atomic Technique } # End of foreach Technique in Atomic Tests } ScriptBlock ID: 19c9d752-7e7f-40bb-9b16-fa5a08c4ef8a Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Invoke-AtomicTest.ps1 09/14/2021 04:30:56 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533492 Keywords=None Message=Creating Scriptblock text (1 of 1): function Get-TargetInfo($Session) { $tmpDir = "$env:TEMP\" $isElevated = $false $targetHostname = hostname $targetUser = whoami if ($Session) { $targetPlatform, $isElevated, $tmpDir, $targetHostname, $targetUser = invoke-command -Session $Session -ScriptBlock { $targetPlatform = "windows" $tmpDir = "/tmp/" $targetHostname = hostname $targetUser = whoami if ($IsLinux) { $targetPlatform = "linux" } elseif ($IsMacOS) { $targetPlatform = "macos" } else { # windows $tmpDir = "$env:TEMP\" $isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } if ($IsLinux -or $IsMacOS) { $isElevated = $false $privid = id -u if ($privid -eq 0) { $isElevated = $true } } $targetPlatform, $isElevated, $tmpDir, $targetHostname, $targetUser } # end ScriptBlock for remote session } else { $targetPlatform = "linux" if ($IsLinux -or $IsMacOS) { $tmpDir = "/tmp/" $isElevated = $false $privid = id -u if ($privid -eq 0) { $isElevated = $true } if ($IsMacOS) { $targetPlatform = "macos" } } else { $targetPlatform = "windows" $isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } } $targetPlatform, $isElevated, $tmpDir, $targetHostname, $targetUser } ScriptBlock ID: a8f28b8f-4297-44bd-a524-54c4693debae Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Get-TargetInfo.ps1 09/14/2021 04:30:56 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533487 Keywords=None Message=Creating Scriptblock text (1 of 1): Invoke-AtomicTest t1069.001 -ShowDetailsBrief ScriptBlock ID: f9cdda21-d2b5-4ff7-bd30-7de69693cc27 Path: 09/14/2021 04:30:57 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533717 Keywords=None Message=Creating Scriptblock text (1 of 1): prompt ScriptBlock ID: 813e15fe-605d-48f5-bd56-40add497008b Path: 09/14/2021 04:30:57 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533691 Keywords=None Message=Creating Scriptblock text (1 of 1): function Write-KeyValue ($key, $value) { Write-Host -ForegroundColor Cyan -NoNewline $key $split = $value -split "(#{[a-z-_A-Z]*})" foreach ($s in $split){ if($s -match "(#{[a-z-_A-Z]*})"){ Write-Host -ForegroundColor Red -NoNewline $s } else { Write-Host -ForegroundColor Green -NoNewline $s } } Write-Host "" } ScriptBlock ID: e7a57014-1669-4801-ad06-9e15629db5a6 Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Write-KeyValue.ps1 09/14/2021 04:30:57 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533680 Keywords=None Message=Creating Scriptblock text (1 of 1): function Platform-IncludesCloud { $cloud = ('office-365', 'azure-ad', 'google-workspace', 'saas', 'iaas', 'containers', 'iaas:aws', 'iaas:azure', 'iaas:gcp') foreach ($platform in $test.supported_platforms) { if ($cloud -contains $platform) { return $true } } return $false } ScriptBlock ID: eb0bd74a-2b12-4a68-b5ee-4234412fbf53 Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Invoke-AtomicTest.ps1 09/14/2021 04:30:57 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533656 Keywords=None Message=Creating Scriptblock text (1 of 1): { $Regex.Matches($_) } ScriptBlock ID: 096178c9-923c-4a1c-8938-4cbd4ff175bb Path: C:\AtomicRedTeam\invoke-atomicredteam\Public\Get-AtomicTechnique.ps1 09/14/2021 04:30:57 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533653 Keywords=None Message=Creating Scriptblock text (1 of 1): ScriptBlock ID: 595262a3-12c4-4cba-9fbe-b161774229a6 Path: 09/14/2021 04:30:57 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123533649 Keywords=None Message=Creating Scriptblock text (1 of 1): ScriptBlock ID: e08ddb6c-66e9-411f-b006-1f56fc34199c Path: 09/14/2021 04:31:02 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123534774 Keywords=None Message=Creating Scriptblock text (1 of 1): Invoke-AtomicTest t1069.001 ScriptBlock ID: b98be8ca-3e16-4eab-89a3-807dbaff4e5f Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536153 Keywords=None Message=Creating Scriptblock text (1 of 1): $global:? ScriptBlock ID: 2e226f08-f6e5-4fce-8c04-50171bb027dd Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536144 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $_.OriginInfo } ScriptBlock ID: e0d957ee-a4f4-46b0-aaba-48ac0341f8d8 Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536140 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $_.ErrorCategory_Message } ScriptBlock ID: ba2bb846-8cb4-4f12-96a0-aeacebab51f2 Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536134 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $this.Exception.InnerException.PSMessageDetails } ScriptBlock ID: 226c90c8-4157-448d-9746-9f967f4f876b Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536130 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $_.PSMessageDetails } ScriptBlock ID: c2b3186e-9a61-48d1-92d3-fa48355e71ed Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536121 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $_.OriginInfo } ScriptBlock ID: 0668e7e1-22b2-4cdf-ab6a-06288a8a60cb Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536117 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $_.ErrorCategory_Message } ScriptBlock ID: 05ae4d9f-f2ab-429c-ab27-070cdb29761f Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536111 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $this.Exception.InnerException.PSMessageDetails } ScriptBlock ID: 38a6026c-a66f-4db1-a2cc-970bc7e01dd6 Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123536107 Keywords=None Message=Creating Scriptblock text (1 of 1): { Set-StrictMode -Version 1; $_.PSMessageDetails } ScriptBlock ID: a3bfbb63-a54f-49e4-b7c0-ceb0fd02a7d2 Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123535571 Keywords=None Message=Creating Scriptblock text (1 of 1): {get-localgroup Get-LocalGroupMember -Name "Administrators"} ScriptBlock ID: 233c8c7d-a3e6-4c37-bb5a-5642af061d4c Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123535569 Keywords=None Message=Creating Scriptblock text (1 of 1): & {get-localgroup Get-LocalGroupMember -Name "Administrators"} ScriptBlock ID: a1e142d9-06c3-4bd0-899b-3940975b1d4e Path: 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123534995 Keywords=None Message=Creating Scriptblock text (1 of 1): function Write-ExecutionLog($startTime, $technique, $testNum, $testName, $logPath, $targetHostname, $targetUser, $guid) { if (!(Test-Path $logPath)) { New-Item $logPath -Force -ItemType File | Out-Null } $timeUTC = (Get-Date($startTime).toUniversalTime() -uformat "%Y-%m-%dT%H:%M:%SZ").ToString() $timeLocal = (Get-Date($startTime) -uformat "%Y-%m-%dT%H:%M:%S").ToString() [PSCustomObject][ordered]@{ "Execution Time (UTC)" = $timeUTC; "Execution Time (Local)" = $timeLocal; "Technique" = $technique; "Test Number" = $testNum; "Test Name" = $testName; "Hostname" = $targetHostname; "Username" = $targetUser "GUID" = $guid } | Export-Csv -Path $LogPath -NoTypeInformation -Append } ScriptBlock ID: 24e5dacc-c7a9-4443-8892-308a39d2c11a Path: C:\AtomicRedTeam\invoke-atomicredteam\Private\Write-ExecutionLog.ps1 09/14/2021 04:31:03 PM LogName=Microsoft-Windows-PowerShell/Operational SourceName=Microsoft-Windows-PowerShell EventCode=4104 EventType=5 Type=Verbose ComputerName=win-dc-291.attackrange.local User=NOT_TRANSLATED Sid=S-1-5-21-2453051693-1864363570-3931539573-500 SidType=0 TaskCategory=Execute a Remote Command OpCode=On create calls RecordNumber=123534986 Keywords=None Message=Creating Scriptblock text (1 of 1): function Invoke-ExecuteCommand ($finalCommand, $executor, $TimeoutSeconds, $session = $null, $interactive) { $null = @( if ($null -eq $finalCommand) { return 0 } $finalCommand = $finalCommand.trim() Write-Verbose -Message 'Invoking Atomic Tests using defined executor' if ($executor -eq "command_prompt" -or $executor -eq "sh" -or $executor -eq "bash") { $execPrefix = "-c" $execExe = $executor if ($executor -eq "command_prompt") { $execPrefix = "/c"; $execExe = "cmd.exe"; $execCommand = $finalCommand -replace "`n", " & " } else { $finalCommand = $finalCommand -replace "[\\`"]", "`\$&" $execCommand = $finalCommand -replace "(?