Skip to content

Commit 882a2b2

Browse files
committed
Adding Invoke-ATHInjectedThread
1 parent 4fd52ec commit 882a2b2

File tree

4 files changed

+933
-9
lines changed

4 files changed

+933
-9
lines changed

AtomicTestHarnesses.psd1

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
RootModule = 'AtomicTestHarnesses.psm1'
55

66
# Version number of this module.
7-
ModuleVersion = '1.4.0.0'
7+
ModuleVersion = '1.5.0.0'
88

99
# ID used to uniquely identify this module
1010
GUID = '195a1637-d4a4-4cb3-8d80-5b5d4e3e930a'
@@ -16,7 +16,7 @@ Author = 'Mike Haag, Jesse Brown, Matt Graeber'
1616
CompanyName = 'Red Canary, Inc.'
1717

1818
# Copyright statement for this module
19-
Copyright = '2020 Red Canary, Inc. All rights reserved.'
19+
Copyright = '2021 Red Canary, Inc. All rights reserved.'
2020

2121
# Description of the functionality provided by this module
2222
Description = 'A module to facilitate the testing of attack techniques and their corresponding procedures.'
@@ -27,6 +27,7 @@ PowerShellVersion = '5.0'
2727
# 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.
2828
FunctionsToExport = 'Invoke-ATHHTMLApplication',
2929
'Invoke-ATHCompiledHelp',
30+
'Invoke-ATHInjectedThread',
3031
'Invoke-ATHMSBuild',
3132
'Invoke-ATHRemoteFXvGPUDisablementCommand',
3233
'Out-ATHPowerShellCommandLineParameter',
@@ -49,6 +50,11 @@ PrivateData = @{
4950

5051
# ReleaseNotes of this module
5152
ReleaseNotes = @'
53+
1.5.0
54+
-----
55+
Added:
56+
* Invoke-ATHInjectedThread
57+
5258
1.4.0
5359
-----
5460
Added:

Readme.md

+12-7
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Rather than placing the `AtomicTestHarnesses` root directory in a module path, i
4040
Import-Module C:\Users\Test\Desktop\AtomicTestHarnesses\AtomicTestHarnesses.psd1
4141
```
4242

43+
The -SkipPublisherCheck and -Force option is typically required for machines that only have v3.4.0 of Pester installed. That particular version of the module was signed by Microsoft to be shipped in-box, which is not the case for later versions which are community maintained and signed with a different certificate. Attempting to install the module without that option may fail.
44+
4345
## Exploring Exposed Functionality
4446

4547
Upon the `AtomicTestHarnesses` module being loaded, to get a sense of what functionality is exposed, you can run the following command:
@@ -88,20 +90,23 @@ If you do not have Pester v5+, you will need to update it.
8890
Install-Module -Name Pester -MinimumVersion 5.0.0 -Scope CurrentUser
8991
```
9092

93+
The `-SkipPublisherCheck` and `-Force` option is typically required for machines that only have `v3.4.0` of Pester installed. That particular version of the module was signed by Microsoft to be shipped in-box, which is not the case for later versions which are community maintained and signed with a different certificate. Attempting to install the module without that option may fail.
94+
9195
## Implemented Test Tags
9296

9397
Specific groups of tests can be run rather than running all available tests. The following tags are exposed:
9498

95-
1. `Module` - Module-wide tests designed to ensure consistency across all exported fcuntions.
99+
1. `Module` - Module-wide tests designed to ensure consistency across all exported functions.
96100
2. `Unit` - Unit tests for exported functions
97101
3. `Technique` - Tests that exercise specific attack technique functionality
98102
4. `T1055` - [Process Injection](https://attack.mitre.org/techniques/T1055/)
99-
5. `T1059.001` - [Command and Scripting Interpreter: PowerShell](https://attack.mitre.org/techniques/T1059/001/)
100-
6. `T1127.001` - [Trusted Developer Utilities Proxy Execution: MSBuild](https://attack.mitre.org/techniques/T1127/001/)
101-
7. `T1134.004` - [Access Token Manipulation: Parent PID Spoofing](https://attack.mitre.org/techniques/T1134/004/)
102-
8. `T1218.001` - [Signed Binary Proxy Execution: Compiled HTML File](https://attack.mitre.org/techniques/T1218/001/)
103-
9. `T1218` - [Signed Binary Proxy Execution](https://attack.mitre.org/techniques/T1218/)
104-
10. `T1218.005` - [Signed Binary Proxy Execution: Mshta](https://attack.mitre.org/techniques/T1218/005/)
103+
5. `T1055.002` - [Process Injection: Portable Executable Injection](https://attack.mitre.org/techniques/T1055/002/)
104+
6. `T1059.001` - [Command and Scripting Interpreter: PowerShell](https://attack.mitre.org/techniques/T1059/001/)
105+
7. `T1127.001` - [Trusted Developer Utilities Proxy Execution: MSBuild](https://attack.mitre.org/techniques/T1127/001/)
106+
8. `T1134.004` - [Access Token Manipulation: Parent PID Spoofing](https://attack.mitre.org/techniques/T1134/004/)
107+
9. `T1218.001` - [Signed Binary Proxy Execution: Compiled HTML File](https://attack.mitre.org/techniques/T1218/001/)
108+
10. `T1218` - [Signed Binary Proxy Execution](https://attack.mitre.org/techniques/T1218/)
109+
11. `T1218.005` - [Signed Binary Proxy Execution: Mshta](https://attack.mitre.org/techniques/T1218/005/)
105110

106111
## Running Tests
107112

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
Set-StrictMode -Version Latest
2+
3+
$TestScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
4+
$ModuleRoot = Resolve-Path "$TestScriptRoot\..\..\"
5+
$ModuleManifest = "$ModuleRoot\AtomicTestHarnesses.psd1"
6+
7+
Remove-Module [A]tomicTestHarnesses
8+
Import-Module $ModuleManifest -Force -ErrorAction Stop
9+
10+
Describe 'Invoke-ATHInjectedThread' {
11+
BeforeAll {
12+
$Help = Get-Help -Name Invoke-ATHInjectedThread -Full
13+
14+
$ExpectedTechniqueID = $null
15+
16+
if ($Help.Synopsis.Split("`r`n")[-1] -match '^(?-i:Technique ID: )(?<TechniqueID>\S+) (?<TechniqueDescription>\(.+\))$') {
17+
$ExpectedTechniqueID = $Matches['TechniqueID']
18+
}
19+
}
20+
21+
Context 'Validating error conditions' -Tag 'Unit', 'T1055.002' {
22+
It 'should execute custom position-independent code' -Tag 'Unit', 'T1055.002' {
23+
$Result = Invoke-ATHInjectedThread -PositionIndependentCodeBytes @(0x90, 0x90, 0x90, 0xC3) # NOP, NOP, NOP, RET
24+
25+
$Result | Should -Not -BeNullOrEmpty
26+
27+
$Result.TechniqueID | Should -BeExactly $ExpectedTechniqueID
28+
$Result.TestSuccess | Should -BeNullOrEmpty
29+
$Result.TestGuid | Should -Not -BeNullOrEmpty
30+
$Result.InjectedCodeBytes | Should -Not -BeNullOrEmpty
31+
$Result.InjectedCodeHash | Should -BeExactly '97E3BFAD17932F638A894351239CA24CB76467E080C5B268307547D36366FE10'
32+
$Result.SourceProcessId | Should -Be $PID
33+
$Result.SourceExecutablePath | Should -Not -BeNullOrEmpty
34+
$Result.SourceCommandLine | Should -Not -BeNullOrEmpty
35+
$Result.TargetProcessId | Should -Not -BeNullOrEmpty
36+
$Result.TargetExecutablePath | Should -Match 'notepad\.exe$'
37+
$Result.TargetCommandLine | Should -BeExactly 'notepad.exe'
38+
$Result.TargetProcessAccess | Should -BeExactly 'PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_QUERY_INFORMATION'
39+
$Result.TargetProcessAccessValue | Should -Be 1082
40+
$Result.TargetBaseAddressHex | Should -Match '^[0-9A-F]{16}$'
41+
$Result.TargetAllocationPageProtect | Should -BeExactly 'PAGE_EXECUTE_READWRITE'
42+
$Result.TargetAllocationPageProtectValue | Should -Be 64
43+
$Result.TargetThreadId | Should -Not -BeNullOrEmpty
44+
$Result.TargetChildProcessId | Should -BeNullOrEmpty
45+
$Result.TargetChildProcessCommandLine | Should -BeNullOrEmpty
46+
}
47+
48+
It 'should inject into itself (the current process)' -Tag 'Unit', 'T1055.002' {
49+
$Result = Invoke-ATHInjectedThread -ProcessId $PID
50+
51+
$Result | Should -Not -BeNullOrEmpty
52+
53+
$Result.TechniqueID | Should -BeExactly $ExpectedTechniqueID
54+
$Result.TestSuccess | Should -BeTrue
55+
$Result.TestGuid | Should -Not -BeNullOrEmpty
56+
$Result.InjectedCodeBytes | Should -Not -BeNullOrEmpty
57+
$Result.InjectedCodeHash | Should -Not -BeNullOrEmpty
58+
$Result.SourceProcessId | Should -Be $PID
59+
$Result.SourceExecutablePath | Should -BeExactly $Result.TargetExecutablePath
60+
$Result.SourceCommandLine | Should -BeExactly $Result.TargetCommandLine
61+
$Result.TargetProcessId | Should -Be $PID
62+
$Result.TargetExecutablePath | Should -Not -BeNullOrEmpty
63+
$Result.TargetCommandLine | Should -Not -BeNullOrEmpty
64+
$Result.TargetProcessAccess | Should -BeExactly 'PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_QUERY_INFORMATION'
65+
$Result.TargetProcessAccessValue | Should -Be 1082
66+
$Result.TargetBaseAddressHex | Should -Match '^[0-9A-F]{16}$'
67+
$Result.TargetAllocationPageProtect | Should -BeExactly 'PAGE_EXECUTE_READWRITE'
68+
$Result.TargetAllocationPageProtectValue | Should -Be 64
69+
$Result.TargetThreadId | Should -Not -BeNullOrEmpty
70+
$Result.TargetChildProcessId | Should -Not -BeNullOrEmpty
71+
$Result.TargetChildProcessCommandLine | Should -Not -BeNullOrEmpty
72+
}
73+
74+
It 'should not inject into a non-existant process ID' -Tag 'Unit', 'T1055.002' {
75+
{ Invoke-ATHInjectedThread -ProcessId 1 -ErrorAction Stop } | Should -Throw
76+
}
77+
78+
It 'should not accept an empty array of position-independent code' -Tag 'Unit', 'T1055.002' {
79+
{ Invoke-ATHInjectedThread -PositionIndependentCodeBytes @() -ErrorAction Stop } | Should -Throw
80+
}
81+
82+
It 'should fail to inject when the template notepad.exe target fails to launch' -Tag 'Unit', 'T1055.002' {
83+
Mock Invoke-CimMethod { return @{ ReturnValue = 1 } }
84+
85+
{ Invoke-ATHInjectedThread -ErrorAction Stop } | Should -Throw
86+
}
87+
88+
It 'should not have access to inject into the System process' -Tag 'Unit', 'T1055.002' {
89+
{ Invoke-ATHInjectedThread -ProcessId 4 -ErrorAction Stop } | Should -Throw
90+
}
91+
92+
It 'should not inject into a 32-bit process' -Tag 'Unit', 'T1055.002' {
93+
$Wow64Notepad = Start-Process -FilePath $Env:windir\SysWOW64\notepad.exe -WindowStyle Hidden -PassThru
94+
95+
{ $Wow64Notepad | Invoke-ATHInjectedThread -ErrorAction Stop } | Should -Throw
96+
97+
$Wow64Notepad | Stop-Process -Force
98+
}
99+
100+
It 'should indicate that the powershell.exe child process failed to launch' -Tag 'Unit', 'T1055.002' {
101+
Mock Wait-Event { return $null }
102+
103+
{ Invoke-ATHInjectedThread -ErrorAction Stop } | Should -Throw
104+
}
105+
}
106+
107+
Context 'Expected artifacts and behaviors when exercising the attack technique' -Tag 'Technique', 'T1055.002' {
108+
BeforeAll {
109+
$Script:FixedTestGuid = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
110+
111+
$Script:TargetNotepadProc = Start-Process -FilePath $Env:windir\System32\notepad.exe -WindowStyle Hidden -PassThru
112+
}
113+
114+
It 'should inject into a process (IsRWXMemory: <IsRWXMemory>, MinimumProcessAccess: <MinimumProcessAccess>, InjectIntoSpecificProcess: <InjectIntoSpecificProcess>)' -Tag 'Technique', 'T1055.002' {
115+
$Arguments = @{}
116+
117+
if ($IsRWXMemory) {
118+
$ExpectedPageProtection = 'PAGE_EXECUTE_READWRITE'
119+
$ExpectedPageProtectionValue = 64
120+
121+
$Arguments['MemoryProtectionType'] = 'ReadWriteExecute'
122+
} else {
123+
$ExpectedPageProtection = 'PAGE_EXECUTE_READ'
124+
$ExpectedPageProtectionValue = 32
125+
126+
$Arguments['MemoryProtectionType'] = 'ReadExecute'
127+
}
128+
129+
if ($MinimumProcessAccess) {
130+
$ExpectedProcessAccess = 'PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_QUERY_INFORMATION'
131+
$ExpectedProcessAccessValue = 1082
132+
133+
$Arguments['ProcessAccessType'] = 'MinimumAccess'
134+
} else {
135+
$ExpectedProcessAccess = 'PROCESS_ALL_ACCESS'
136+
$ExpectedProcessAccessValue = 2097151
137+
138+
$Arguments['ProcessAccessType'] = 'AllAccess'
139+
}
140+
141+
if ($InjectIntoSpecificProcess) {
142+
$ExpectedProcessId = $TargetNotepadProc.Id
143+
144+
$Arguments['ProcessId'] = $TargetNotepadProc.Id
145+
} else {
146+
$ExpectedProcessId = $null
147+
}
148+
149+
$Result = Invoke-ATHInjectedThread -TestGuid $FixedTestGuid @Arguments
150+
151+
$Result | Should -Not -BeNullOrEmpty
152+
153+
$Result.TechniqueID | Should -BeExactly $ExpectedTechniqueID
154+
$Result.TestSuccess | Should -BeTrue
155+
$Result.TestGuid | Should -BeExactly $FixedTestGuid
156+
$Result.InjectedCodeBytes | Should -Not -BeNullOrEmpty
157+
$Result.InjectedCodeHash | Should -Not -BeNullOrEmpty
158+
$Result.SourceProcessId | Should -Be $PID
159+
$Result.SourceExecutablePath | Should -Not -BeNullOrEmpty
160+
$Result.SourceCommandLine | Should -Not -BeNullOrEmpty
161+
162+
if ($InjectIntoSpecificProcess) {
163+
$Result.TargetProcessId | Should -Be $ExpectedProcessId
164+
} else {
165+
$Result.TargetProcessId | Should -Not -BeNullOrEmpty
166+
}
167+
168+
$Result.TargetExecutablePath | Should -Match 'notepad\.exe'
169+
$Result.TargetCommandLine | Should -Match 'notepad\.exe'
170+
$Result.TargetProcessAccess | Should -BeExactly $ExpectedProcessAccess
171+
$Result.TargetProcessAccessHex | Should -BeExactly $ExpectedProcessAccessHex
172+
$Result.TargetBaseAddressHex | Should -Match '^[0-9A-F]{16}$'
173+
$Result.TargetAllocationPageProtect | Should -BeExactly $ExpectedPageProtection
174+
$Result.TargetAllocationPageProtectValue | Should -Be $ExpectedPageProtectionValue
175+
$Result.TargetThreadId | Should -Not -BeNullOrEmpty
176+
$Result.TargetChildProcessId | Should -Not -BeNullOrEmpty
177+
$Result.TargetChildProcessCommandLine | Should -Match $FixedTestGuid
178+
} -TestCases @(
179+
@{ IsRWXMemory = $False; MinimumProcessAccess = $False; InjectIntoSpecificProcess = $False },
180+
@{ IsRWXMemory = $True; MinimumProcessAccess = $False; InjectIntoSpecificProcess = $False },
181+
@{ IsRWXMemory = $False; MinimumProcessAccess = $True; InjectIntoSpecificProcess = $False },
182+
@{ IsRWXMemory = $True; MinimumProcessAccess = $True; InjectIntoSpecificProcess = $False },
183+
@{ IsRWXMemory = $False; MinimumProcessAccess = $False; InjectIntoSpecificProcess = $True },
184+
@{ IsRWXMemory = $True; MinimumProcessAccess = $False; InjectIntoSpecificProcess = $True },
185+
@{ IsRWXMemory = $False; MinimumProcessAccess = $True; InjectIntoSpecificProcess = $True },
186+
@{ IsRWXMemory = $True; MinimumProcessAccess = $True; InjectIntoSpecificProcess = $True }
187+
)
188+
189+
AfterAll {
190+
$Script:TargetNotepadProc | Stop-Process -Force
191+
}
192+
}
193+
}

0 commit comments

Comments
 (0)