如何用GzipStream实现PowerShell免杀?
TOPIC SOURCE
venom的powershell免杀技术分析
在红队的日常里,PowerShell 的灵活性早已不是什么新鲜事,但要让它在目标机器上悄无声息地执行,却仍然是个技术活。传统的 Base64 + -enc 方式已经被大多数防病毒引擎列入特征库,压缩混淆则提供了一条相对隐蔽的通路。本文聚焦于 System.IO.Compression.GzipStream,展示如何把完整的脚本压缩、转码,再在内存中解压执行,从而实现硬盘免杀。
GzipStream 在内存加载中的原理
GzipStream 本质上是 .NET 的流式压缩解压实现,它接受任意二进制数据并返回原始内容。PowerShell 可以直接实例化该类,对 MemoryStream 包装的压缩字节进行 Decompress,随后把解压后的字符串喂给 [scriptblock]::Create(),整个过程全程留在进程内存,磁盘上只留下一段 Base64 编码的压缩块。
实现步骤
- 将目标 PowerShell 脚本保存为
.ps1,使用gzip或 .NET 代码压缩为二进制。 - 把压缩得到的字节数组通过
[Convert]::ToBase64String()转为字符串,写入加载脚本。 - 在加载脚本里使用
[Convert]::FromBase64String()还原字节,交给GzipStream解压。 - 将解压结果交给
[scriptblock]::Create()并调用.Invoke(),完成执行。
完整示例代码
$payload = @"
H4sIAAAAAAAAC+2Z227bMBBG/5WqUuXJQ0hQRLJQpQZgVHSYhV0Vg2VnY0v
...
"@ #(此处为压缩后 Base64,省略)
$bytes = [System.Convert]::FromBase64String($payload)
$mem = New-Object System.IO.MemoryStream($bytes, $false)
$gzip = New-Object System.IO.Compression.GzipStream($mem, [System.IO.Compression.CompressionMode]::Decompress)
$reader= New-Object System.IO.StreamReader($gzip)
$script= $reader.ReadToEnd()
[scriptblock]::Create($script).Invoke()
实战案例:Mimikatz 免杀
把公开的 Mimikatz.ps1 按上述流程压缩后,生成的 Base64 体积约为 12 KB。将完整的加载代码写入一次性执行的 powershell -nop -w hidden -c 参数里,目标机器的 AV 在运行时只能捕获到 GzipStream 的调用,却难以关联到已知的 Mimikatz 行为。实验室的 8 款主流防护产品均未触发告警,唯一的异常是进程列表里出现了 powershell.exe 的隐蔽启动。
注意事项与检测规避
压缩本身并不等同于加密,若仅靠 Base64 隐蔽,仍有被解码插件捕获的风险。可以在压缩前对脚本做轻度异或或 ROT13 处理,再在加载阶段先恢复后再交给 GzipStream;此外,把 System.IO.Compression 的调用包装在 Invoke-Expression 的字符串里,能够进一步分散特征。

参与讨论
这个方法之前试过,确实能过部分杀软
压缩后再做一次异或处理会不会更隐蔽?
有没有人试过用DeflateStream替代GzipStream?
我之前也搞过类似的内存加载,不过用的不是GzipStream
这个在Win11上能正常跑吗?