Powershell GzipStream加载技术如何绕过主流杀软?
venom的powershell免杀技术分析
在红队的日常作业里,PowerShell 已不再是单纯的管理工具,而是被打磨成一把兼具灵活性和隐蔽性的“匕首”。当传统的 Invoke-Expression 与 Invoke-Command 已被杀软的行为特征库牢牢捕获时,攻击者往往转向更底层的 .NET 类库——尤其是 System.IO.Compression.GzipStream,用它把脚本压缩、再解压执行,形成一种“压缩后即用”的加载链。
GzipStream 的加载机制
GzipStream 本质上是一个流式压缩器,能够在内存中对任意字节序列进行 Compress 与 Decompress。攻击者先把完整的 PowerShell 脚本保存为 .ps1,随后使用 [System.IO.Compression.GzipStream]::Compress 将其转为二进制,再通过 [System.Convert]::ToBase64String 编码成纯文本。受害机器上,仅需一行 scriptblock::Create 读取 MemoryStream → GzipStream → StreamReader,即可还原出原始脚本并执行。
为何能逃过主流杀软
- 压缩后代码不再呈现原始关键字,例如
Invoke-Expression、DownloadFile,特征匹配失效。 - Base64 只是一层包装,解码行为在内存完成,文件系统上没有可供扫描的可执行体。
- GzipStream 属于 .NET 标准库,几乎所有 Windows 环境默认加载,安全产品难以将其列入黑名单而不影响正常业务。
- 使用
-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden启动 PowerShell,进一步抑制日志与弹窗。
典型攻击示例
$payload = 'H4sIAAAAAAAAC...(省略)...'
$bytes = [System.Convert]::FromBase64String($payload)
$ms = New-Object System.IO.MemoryStream($bytes)
$gs = New-Object System.IO.Compression.GzipStream($ms,[System.IO.Compression.CompressionMode]::Decompress)
$sr = New-Object System.IO.StreamReader($gs)
$script = $sr.ReadToEnd()
Invoke-Command -ScriptBlock ([scriptblock]::Create($script))
上述片段在一次渗透演练中成功加载了经过压缩的 Mimikatz 模块,杀软的实时监控页面只捕获到普通的 powershell.exe 进程,未触发任何告警。唯一的异常是内存中出现了短暂的 gzip 流对象,但多数 AV 并未对其进行深度分析。
防御思路与检测要点
针对 GzipStream 方案,防御侧可以从两条路径入手:其一是对 PowerShell 进程的行为进行监控,尤其是 MemoryStream → GzipStream → StreamReader 的链式调用;其二是对 Base64 解码后立即进入压缩流的内存块进行启发式分析,结合 entropy(熵)阈值与异常 API 调用序列,提升检测准确度。值得注意的是,单纯的签名匹配已难以覆盖此类“无文件”攻击,行为层面的监控才是关键。
在实际运维中,开启 PowerShell 的脚本日志、限制 -ExecutionPolicy 为 RemoteSigned,以及对不可信的 Base64 载荷进行强制审计,能够在早期拦截大部分利用 GzipStream 的免杀尝试。毕竟,安全的本质仍是把“看得见的风险”先行曝光。

参与讨论
这个加载方式挺巧妙的,不过对内存监控要求就高了
之前做渗透测试时用过类似的,确实能过不少杀软
那如果换成DeflateStream效果会不会更好?
压缩加载现在算常规操作了吧,老手都用烂了