木马杂谈

在红队的这几个月里见识了很多,有不少的收获。如果说做红队跟渗透测试有什么不一样,有一点就是红队会运用到很多针对个人终端的攻击手法。在这其中,木马扮演了十分关键的角色。

写一点我的想法以及研究方向,不涉及具体实现。

命令与控制

木马的基本功能,即攻击者拥有控制木马行为的能力。唯一可以一提的是攻击者操纵木马的自由度,例如cs提供了object file的功能,相当于让攻击者拥有了在木马进程内执行任意代码的能力。

bypass主动防御、权限维持

在有了控制目标终端的能力之后,最重要的事情就是维持住这个权限,这一块难点主要就是在目标机器上有一些杀软的情况下去做权限维持。经过一些摸索和测试发现,对杀软来说,任何对系统关键位置进行修改的敏感操作都需要在可信的进程下去进行,否则会对这个行为进行拦截(对用户进行提示,默认拒绝)。可信的进程不单是文件需要有签名,同时需要这个进程向上的进程链上所有的进程都要有签名。

研究绕过主动防御进行权限维持的方向有

  • 修改一些系统非关键位置来实现一些非常规的权限维持,比如针对一些系统上已有的自启动服务寻找dll劫持的漏洞

  • 将敏感操作放到可信进程中去进行,可以利用已有的进程,也可以自己构造一个可信的进程,当然还可以直接给自己的马整个签名

  • 关掉主动防御,看起来在r3层与r0层对抗是不可能的,但对于一些自保没做好的杀软依然可以做到直接干掉杀毒软件的进程

Fully UnDetectable - 木马的伪装与特征隐藏

一个程序打开不展示任何窗口,没有任何输出,那是个人都会觉得不对劲。而对于杀软来说,一个不创建任何窗口的Win32程序也同样是值得警惕的。因此,给木马捆绑一个正常的程序,既可以让对方放下警戒,也在一定程度上能达到免杀的效果。

大部分红队都是直接使用现成的马,比如cs,然后配合一个loader用于免杀,有些也会在别人开源项目的基础上定制自己的木马。而就算是纯自研的木马,在开发时如果没有考虑到如何快速变异,时间长了也免不了被分析、提取特征。因此对于这种就总会涉及到基于特征的各种查杀。

大体上有三种特征,流量特征、文件特征、内存特征

如何规避查杀?

  • 对于流量特征,例如cs提供了malleable C2的功能允许使用者将木马的流量伪装成正常的流量,配合https以及域前置就可以达到非常的好的效果。
  • 对于文件特征,常用的手段有将shellcode、原始pe进行加密存储,或者当木马运行的时候才将真正的部分从远程加载过来以躲避杀毒软件的静态查杀,核心思想是将真正有特征的部分进行加密或与文件分离存放。
  • 但是上面这些隐藏文件特征的手法对于内存查杀来说都没有意义,加密或者从远端获取的payload最终总会在内存里完全的暴露出来,甚至已经有蓝队开发出了针对cs的内存扫描工具,可以将beacon中的配置完全提取。怎么解决?使用自研/魔改的木马可以轻松躲避所有特征的查杀(废话),而对于使用cs这种并不开源的马的红队来讲,尽管cs提供了一些解决方案,但并不是无死角的,这就是一个很大的问题。 通用的隐藏内存中特征的解决方案是我最近在研究的项目。我把木马内存中的特征分为两类,代码段中的特征与数据段中的特征。对于代码段中的特征,可以使用一些保护商业软件防止被破解的思路,加一些代码混淆、膨胀甚至vm壳,改变了代码段的内容后特征自然就消失了。但是很不幸,大部分的特征实际上都是针对木马的数据段来提取的,因此需要想办法来保护数据段。

除此之外,为了更好的应对特征查杀,木马应具有变异的能力。

anti analysis

主要是就是反沙盒和反人工分析

反沙盒比较重要,我认为基本决定了马能活多长时间,而反人工往往只是为了给分析增加难度(当马已经进入人工分析的阶段那说明这马已经被发现有问题了)。沙盒大概可以分为两种,模拟执行pe的沙盒和完全拟真的虚拟机。前者比如火绒,当用户运行程序前它会将目标程序在它自己的沙盒里跑一遍,没有问题再放行,这种的绕过比较简单,而对于完全拟真的环境就需要去判断。

我们不能单纯的通过判断当前程序是否运行在虚拟环境中来决定是否执行恶意代码,这会造成使用云桌面、或者是一些使用虚拟化服务器的目标没法上线。比较可行的做法是尽可能小心的收集目标系统上的信息(小心是因为获取部分信息的时候会被沙箱标记上虚拟机检测行为),然后对这些信息进行打分,或者由人工来判断是否继续执行恶意代码。

为了防止这种完全拟真的环境,有时我们还可以使用基于白名单的做法,即目标中具有xx特征时才真正执行恶意代码,比如目标必须已安装xx软件,上线ip必须处于某个ip段,甚至绑定目标机器的木马,当然这些都需要与其他手段配合使用。

值得一提的还有很多沙盒与edr对程序的监控。因为内核回调对程序行为的把控有限,它们的监控多是使用r3层的hook,这也就意味着我们可以通过多种方式绕过这种监控,能够起到很好的效果。

反人工分析意义不是很大,方式也无外乎各种反调试。如果非要做的话,我认为应该把隐藏ioc作为反人工的目标,而不是尽可能的阻碍调试。

从木马到rootkit

虽然说木马的功能完全可以都在r3去做,但是我还是想进r0看看

关于语言选择

语言选择需要根据当前红队的开发能力来做,目前比较流行的有python、c#、c/c++,还有最近很火的nim。对于木马开发来讲,我比较倾向于c/c++。python与c#最大的问题是依赖,python打包后体积太大不利于传输或捆绑。c#得益于大部分系统都已经安装了.net,在体积上没有那么劣势,但是.net版本难以协调。有些lib不兼容.net2.0,而有些lib太老早已不维护。并且4.0比2.0多太多新的东西,要想写出兼容性好的代就要放弃这些特性,这又与“便捷”走远了。之前用过一段c#做木马开发,一个组件要维护两个版本,感觉很麻烦,因此我个人不是很喜欢用c#。nim这个语言很好,他实际上是一个代码转译器,将nim代码转为其他代码再通过编译器去编译,它是一个更好的c,因此不存在依赖问题,同时又兼顾了开发的便捷性。我不选择nim其实很大一部分原因是因为我并不熟悉它,不知道它会有怎样的坑,而我在c/c++的领域已经有了一些积累,有很多可以复用的代码让我的开发速度不亚于使用其他语言。另一部分原因是因为c++的模板,它可以做的事情太多了,暂时还没有看到nim里有更好的方式来替代它。c/c++的好处是我可以去干任何事,并且c/c++的代码库很丰富,不管什么功能都可以找到对应的实现。坏处是开发者必须要去干任何事,同时它太底层了,少了很多高级语言的特性,尽管模版可以弥补,但是这就带来了一定的门槛。

其他

两个月前写的文章,因为忙于其他事一直没有写完,这两天修修补补总算是差不多了。做这个方向感觉没什么人可以交流,大部分选手所谓的免杀都仅仅停留在使用不同的loader加载shellcode做个文件免杀,没啥意思。不过木马作为终端攻防的重点,一定会随着需求的增加有所发展,相信之后深入研究这些的人会越来越多。