简单的vshell绕过火绒内存查杀
一、内容
首先火绒内存查杀 高级设置全部打开
首先我们上线一个vshell,观察一下情况
我们可以看到 shellcode的生成并不像cobaltstrike那样存在stager和stagerless的选项,只有固定的一个stager。
我们申请tcp的监听器的,shellcode进行上线
实际测试文件管理操作不会被动态查杀
屏幕操作不会被查杀
当我们点击交互命令的时候,就会秒删
点击非交互式的时候 则会发现执行一个命令过后就会被删除
二、简单探测
通过上面试验的规则我们应该大概了解了一些情况 执行命令的时候会被删除。那么到底是为什么呢??
我们再次研究一下
首先关闭火绒(看看马子运行原理),看看这两个交互式和非交互式具体做了什么
2.1 交互式
我们使用SystemInformer.exe 查看进程属性,发现当启动交互终端的时候创建了一个子进程 winpty-agent.exe
可以发现放在了临时目录下
至于这个到底是什么??这个是一个开源的项目,网上也有很多介绍,我就不多赘述了
https://github.com/rprichard/winpty
关闭终端则这个进程关闭
2.2非交互式
我们使用非交互式执行命令的时候,则没有这个进程
这就证明是这个上线的agent充当了执行的终端
查看内存属性
我们执行查看内存属性会发现,rwx两片 rx一片 并且这里没有物理文件支撑,很大概率就是我们的马子了
第一片rwx属性是Mapped代表的是共享的,这是我们小马存放的地址,为什么呢?因为我的加载器就是申请的映射内存然后写入执行的。
第二片属性是private私有的,并且点开一看
4d5a(MZ标志) 5045(PE标志),就是一个PE头特征,妥妥的PE结构啊 这里应该就是大马的位置了。
后面我拿ida-mcp配合claude逆向这个内存,发现确实是大马,并且里面一堆特征,比如有关oss的字符串,还有那个开源终端,全部能逆向出来。
这片内存是一个很敏感的内存,为什么呢?这就是杀软和edr的一个最基本的检测了,详细可以看鸭哥的文章
三、确定思路
现在思路就很明确了 交互式是上传一个终端程序,非交互式是自己上线的agent来做命令执行。
再加上 vshell的一个不开源的 go语言编译的(很难逆向),所以我不能改他上传终端那个逻辑,而是选择改内存。
那到底怎么做内存免杀呢?cs那里用的是sleepmask,在鹏瑶大佬的udrl发展那个议题中介绍了一些c2的演进在这里面也介绍了一下手法。鸭哥的文章也有介绍一下手法
当然火绒不需要这么费力,本着轻量级的,让用户良好体验的,火绒没有hook机制,也没有堆栈检测机制。
(当然我也只是一个小白,哪里说错了,大佬们也多多包涵)。
我一开始的思路就是找到大内存那片地址,然后抹除PE头就可以了。
我是直接hook了一下 VirtualAlloc 看大内存rwx申请的地址是什么
0000028780000000
然后我们可以将这个地址 存入到全局变量后面再将其抹除PE头,我们可以看到第二次调用virtualAlloc的时候就是申请的这个大内存,那么第一次呢??第一次是我写的hook_loader申请的
随后我写的逻辑是在第二次调用申请内存的时候记录地址和大小放在全局变量中,第三次申请的时候 直接开始抹除第二次申请的PE头,写的有点直接,但是这只是初步试探的过程,后续确
此时我们再运行可以看到成功抹除了
但是运行命令一样会被查杀
那么为什么呢???
四、查看安全日志确定突破口
第一个查杀
第二个内存查杀
第三个内存查杀
发现了吗???他们的虚拟地址是一样的都是0x0000000067700000
我们再运行一下看看该内存
也是很明显的一个PE结构的内存,但是内存属性为RW。研究一下这个吧
加上或这个条件
再看一下0x0000000067700000
[*] VirtualAlloc EXE page: 0000000067700000 size: 8208384
[*] VirtualAlloc flAllocationType : 12288 flProtect: 4
virtualNumber is 262
[*] VirtualAlloc EXE page: 0000000067700000 size: 1024
[*] VirtualAlloc flAllocationType : 4096 flProtect: 4
virtualNumber is 263
以可读可写的权限先以0000000067700000为基质申请了一片大内存 然后再在这之中申请了一个小内存
再加上内存中这是一个PE程序,他应该是先申请了一个rw的程序 再将PE写入内存然后伸展修复,再修复各节的属性。
这也是很常规的一个操作了
于是我们再hook一下 virtualprotec api来查看
virtualNumber is 241
lpAddress is 0000000067701000 dwsize is 3265024,flNewProtect is 32,lpflOldProtect is 563084636
protectNum is 3
lpAddress is 0000000067A1F000 dwsize is 262656,flNewProtect is 4,lpflOldProtect is 563084636
protectNum is 4
lpAddress is 0000000067A60000 dwsize is 4177920,flNewProtect is 2,lpflOldProtect is 563084636
protectNum is 5
lpAddress is 0000000067E5C000 dwsize is 2560,flNewProtect is 2,lpflOldProtect is 563084636
protectNum is 6
lpAddress is 0000000067E5D000 dwsize is 2048,flNewProtect is 2,lpflOldProtect is 563084636
protectNum is 7
lpAddress is 0000000067E5E000 dwsize is 399872,flNewProtect is 4,lpflOldProtect is 563084636
protectNum is 8
lpAddress is 0000000067EC0000 dwsize is 512,flNewProtect is 2,lpflOldProtect is 563084636
protectNum is 9
lpAddress is 0000000067EC1000 dwsize is 5120,flNewProtect is 4,lpflOldProtect is 563084636
protectNum is 10
lpAddress is 0000000067EC3000 dwsize is 512,flNewProtect is 4,lpflOldProtect is 563084636
protectNum is 11
lpAddress is 0000000067EC4000 dwsize is 512,flNewProtect is 4,lpflOldProtect is 563084636
protectNum is 12| # | 地址 | 大小 | 新属性 | 解读 |
|---|---|---|---|---|
| 3 | 0x67701000 | ~3.1 MB | 0x20 (EXECUTE\_READ) | 代码段:可执行+只读,典型的 .text 段属性 |
| 4 | 0x67A1F000 | ~256 KB | 0x04 (READWRITE) | 数据段:可读写,可能是 .data 或堆 |
| 5 | 0x67A60000 | ~4.0 MB | 0x02 (READONLY) | 只读数据:可能是 .rdata 或资源段 |
| 6 | 0x67E5C000 | 2.5 KB | 0x02 (READONLY) | 小块只读数据 |
| 7 | 0x67E5D000 | 2.0 KB | 0x02 (READONLY) | 同上,可能是导入表/重定位表的一部分 |
| 8 | 0x67E5E000 | ~390 KB | 0x04 (READWRITE) | 可读写数据段 |
| 9 | 0x67EC0000 | 512 B | 0x02 (READONLY) | 极小块,可能是某个表/结构的头部 |
| 10 | 0x67EC1000 | 5 KB | 0x04 (READWRITE) | 小块可读写数据 |
| 11 | 0x67EC3000 | 512 B | 0x04 (READWRITE) | 同上 |
| 12 | 0x67EC4000 | 512 B | 0x04 (READWRITE) | 同上 |
我们尝试抹除这片区域的PE头,因为这个地址申请的是固定的0000000067700000,这样就更简单了。
我直接通过CreateTimerQueueTimer创建一个时间回调执行,过5秒后抹除 该地址PE头
运行程序看见Hack The Planet 这个标志就代码已经成功抹除
此时再执行命令就不会被查杀
这就是很简单的手法达成的内存查杀,当然实战中我们还是最后利用veh+sleep做成sleepmask这种吧,毕竟vshell好像没有睡眠机制,为什么呢???因为我hook了sleep发现并没有运行sleep
与此同时交互式的终端运行命令也修复了,交互式运行命令也不会被查杀
五、最终演示视频
相比较原来的免杀的加载器 只添加一个回调函数
一个时间序列5秒后调用,就能简单绕过内存查杀
六、番外
我问了一下我朋友,不仅仅vshell的这片地址申请固定,cs的stager有一片地址申请的也是固定的
当然后续vshell的内存免杀,就可以仿造着sleepmask那样 通过veh异常处理+CreateTimerQueue创建时间序列实现 内存属性浮动和休眠加密 执行解密。毕竟这样直接抹除PE头的手法还是太暴力了。哈哈
本文作者:八幡
本文链接:https://blog.lingxisec.com/archives/simple-vshell-bypass-huorong-memory-detection.html
版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
法律说明:
文章声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任,本人坚决反对利用文章内容进行恶意攻击行为,推荐大家在了解技术原理的前提下,更好的维护个人信息安全、企业安全、国家安全,本文内容未隐讳任何个人、群体、公司。非文学作品,请勿过度理解,根据《计算机软件保护条例》第十七条,本站所有软件请仅用于学习研究用途。
关于凌曦安全(LingXisec):
凌曦安全团队(LingXi Security Team)成立于 2023 年,专注于 SRC 漏洞挖掘、Web 安全、红蓝攻防、渗透测试、云安全、JavaScript 逆向、应急响应等网络安全技术研究与实战实践。团队成员来自安全厂商、互联网企业、高校和独立研究者,持续分享漏洞分析、攻防案例、云安全研究和安全工具经验,并提供面向零基础学员的网络安全培训,课程采用“零基础红队入门、安服与渗透案例、SRC 漏洞挖掘”三合一体系,通过线上录播、直播课程和社群答疑,结合真实案例、内部靶场、漏洞挖掘活动、简历修改与模拟面试,帮助学员建立系统的安全知识和持续学习能力。同时在 Bilibili 提供免费的网络安全入门与案例讲解视频,致力于推动网络安全技术交流。


































