浅析挖矿病毒新姿势:无文件挖矿——病毒主体是个powershell脚本文件,且经过了高度混淆和base64编码

[原创]浅析挖矿病毒新姿势:无文件挖矿 
 2019-7-27 17:00  6934

 from:https://bbs.pediy.com/thread-253375.htm

目录

 

前言

现在的病毒主要获利的就是勒索病毒和挖矿病毒,在挖矿病毒中,现在企业比较常见的并且容易中招的就是powershell无文件落地挖矿病毒。powershell病毒与平常分析的C语言、Delphi、易语言编写的病毒语法不一样,分析过程中大部分都是依靠百度去查语法,这里就不仔细分析了。怀着对知识的渴望,去学习了下powershell病毒为什么能实现无文件落地来进行挖矿了————反射型注入。这里以PowerGhost病毒样本作为示例。

样本描述

PowerGhost病毒程序最早发现与2018年, 该病毒主要向大型企业网络, 服务器主机植入挖矿程序, 为攻击者牟取利益。与常规挖矿病毒区别在于PowerGhost更善于隐藏自己,通过无文件技术在受害系统创建矿工程序。从本次捕获的最新版本可见攻击者不仅仅再局限于向Windows感染主机植入矿工程序。在横向传播的过程中, 根据目标平台环境, 针对Linux平台下载Shell脚本并植入后门程序。当Linux主机被SSH弱口令爆破成功后, 劫持常用Linux命令工具, 并且在目标主机中安装”billGates”后门。”billGates”潜伏在用户主机中, 当攻击者需要发起DDoS攻击时, 向感染主机发送攻击命令,此时的感染主机俨然已成为攻击者僵尸网络中的一员。

行为分析

创建了计划任务,对注册表、服务进行操作

 

CPU使用率高达93%

恶意代码

病毒主体是个powershell脚本文件,且经过了高度混淆和base64编码,使分析人员和杀毒软件查杀难度变大。
图片描述

 

Powershell大都使用IEX命令执行代码,但是由于powershell脚本的语言特性,混淆代码在执行时,最终都会被还原为本身的代码。
通过PowerShell的write-output命令来替换掉IEX或. ((gV 'Mdr').NaMe[3,11,2]-JoIN'')等混淆语句。
直接删除”. ((gV 'Mdr').NaMe[3,11,2]-JoIN'')” IEX 执行语句,转储为1.ps1文件
图片描述
可以得到去除混淆后的代码
图片描述
代码还不可读,发现末尾还有IEX的变形命令,再一次进行去混淆并转储为2.ps1文件
最后得到了去混淆后的可读代码
图片描述
这边powershell脚本代码我就不一一去写了,我都是百度语法的。
这里主要介绍了powershell怎么去混淆得到本身的代码。

反射型注入

引言

我们学习逆向的时候,学过了输入法注入,远程线程注入,消息钩子注入,APC注入…..等等,但是这些注入方法都需要在本地存储注入的DLL文件,利用CreateRemoteThread这一函数在目标进程中开始一个新的线程,这个线程执行系统的API函数LoadLibrary,之后DLL就被装载到目标进程中了。然而,由于这一技术被大量的恶意软件利用,各种安全对DLL注入这一块自然是严加看守,十分容易被检测出来。
因此,反射型注入技术开始被恶意程序利用了。它不需要在文件系统存放目标DLL,减少了文件“落地”被删的风险。同时它不需要像常规的DLL注入方式那么套路,因此更容易通过杀软的行为检测。由于反射式注入方式并没有通过LoadLibrary等API来完成DLL的装载,DLL并没有在操作系统中”注册”自己的存在,因此用ProcessExplorer等软件也无法检测出进程加载了该DLL。

核心思路

平时我们注入DLL,都需要LoadLibrary函数,而此函数则需要有落地文件。因此,想要实现无文件落地,我们则需要抛弃LoadLibrary函数,自己实现一个函数的加载。
我们可以为待注入的DLL添加一个导出函数,ReflectiveLoader,这个函数实现的功能就是装载它自身。那么我们只需要将这个DLL文件写入目标进程的虚拟空间中,然后通过DLL的导出表找到这个ReflectiveLoader并调用它,我们的任务就完成了。
于是,我们的重中之重就是怎么样去实现这个ReflectiveLoader函数。

ReflectiveLoader的实现

ReflectiveLoader要完成的任务是对自身的装载,所谓“装载”,最重要的一点就是要将自身合适地展开到虚拟空间中。我们都知道在PE文件包含了许多节,而为了节省存储空间,这些节在PE文件中比较紧密地凑在一起的。而在广阔虚拟空间中,这些节就可以映射到更大的空间中去。更不用说还存在着.bss这样的在PE文件中不占空间,而要在虚拟空间中占据位置的节了。ReflectiveLoader需要做的一件很重要的事就是按照规则去将这些节映射到对应的地址去。
同时,由于DLL中可能会用到其他DLL的函数,装载一个DLL还需要将这个DLL依赖的其他动态库装入内存,并修改DLL的IAT指向到合适的位置,这样对其他DLL函数的引用才能正确运作。
最后,我们还需要修复重定位,来使程序正确运行。

定位DLL文件在内存中的基址

ReflectiveLoader做的第一件事就是查找自身所在的DLL具体被写入了哪个位置。
ReflectiveLoader首先利用一个重定位技巧找到自身所在的大致位置:
ULONG_PTR caller( VOID ) { return(ULONG_PTR)_ReturnAddress(); }
其中函数_ReturnAddress()返回的是当前调用函数的返回地址,也就是caller()的下一条指令的地址。这个地址位于ReflectiveLoader的内部,而ReflectiveLoader位于被注入的DLL文件内部,因此这个地址离DLL文件的头部不远了。
借助上文找到的地址,我们逐字节的向上遍历,当查找到符合PE格式的文件头之后,就可以认为找到了DLL文件在内存中的地址了。

获取所需的系统API。

ReflectiveLoader启动时,目标进程已在正常的运行状态中了,此时目标进程已经装载了一些核心的DLL文件。我们可以搜索这些DLL文件,查找需要的API函数,为后续操作提供方便。具体地,我们需要的函数是kernel32.dll中的LoadLibraryA(), GetProcAddress(), VirtualAlloc()以及ntdll.dll中的NtFlushInstructionCache()函数。

  • PEB
    进程环境块,存放进程相关信息。
    重要字段_PEB_LDR_DATA 进程模块列表
    图片描述

  • TEB
    线程环境块
    图片描述

  • 动态获取kernel32.dll

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    int main()
    {
    DWORD kernel32 = 0;
      PUNICODE_STRING p = 0;
      _asm {
          mov eax, fs:[0x30];             //PEB
          mov eax, [eax + 0x0C];         //ldr PEB_LDR_DATA
          mov eax, [eax + 0x1c];         //LIST_ENTRY.Flink     //ntdll.dll
          mov eax, [eax];                 //kernelbase.dll
          mov edx, [eax + 0x08];         //_LDR_DATA_TABLE_ENTRY.DllBase;
          mov kernel32, edx;           //kernel32.base
          lea eax, [eax + 0x1c];       //dllname  UNINCODE_STRING
          mov p, eax;
       }
      printf("%08X,%S", kernel32,p->buff);
      return 0;
    }

在获取了模块基地址之后,通过对PE文件的解析,找到DLL文件的导出表,再根据导出表就可以找到任一导出函数的地址了。对PE文件的解析有太多文章,这里也不细致阐述了。
在此,我们得到了函数LoadLibraryA(), GetProcAddress(), VirtualAlloc()以及NtFlushInstructionCache()。它们将在之后被用到。

最后工作

最后我们需要对DLL进行重定位,调用DLL入口点即可。这些都可以在百度搜到,网上帖子挺多的。
总的来说流程就是:

  • 使用RWX权限打开目标进程,并为该DLL分配足够大的内存。
  • 将DLL复制到分配的内存空间。
  • 计算DLL中用于执行反射加载的导出的内存偏移量。
  • 调用CreateRemoteThread(或类似的未公开的API函数- RtlCreateUserThread)在远程进程中开始执行,使用反射加载函数的偏移地址作为入口点。
  • 反射加载函数使用适当的CPU寄存器查找目标进程的进程环境块(PEB),并使用它查找内存中的地址kernel32.dll以及任何其他所需的库。
  • 解析的KERNEL32出口目录中找到所需的API功能,如内存地址LoadLibraryA,GetProcAddress和VirtualAlloc。
  • 使用这些函数,然后正确加载DLL(本身)到内存中,并调用它的入口点,DllMain。

总结

反射式DLL注入是一种新型的DLL注入方式,它不需要像传统的注入方式一样需要DLL落地存储,避免了注入DLL被安全软件删除的危险。由于它没有通过系统API对DLL进行装载,操作系统无从得知被注入进程装载了该DLL,所以检测软件也无法检测它。同时,由于操作流程和一般的注入方式不同,反射式DLL注入被安全软件拦截的概率也会比一般的注入方式低。
反射式DLL注入的实现中运用了大量对PE文件结构的解析。了解,以及动手实践这个注入方式会让您对PE文件格式,PE文件加载的理解更加深刻。

 

以上内容是我的学习笔记,如题目所言,属于“浅析”,只是自己的见解以及网上查到的知识,反射式DLL注入用到的技术远不止这里,很多大佬可以玩出骚操作。可能有错与不足,希望大家指出改正,共同学习。

posted @ 2022-02-23 17:45  bonelee  阅读(400)  评论(0编辑  收藏  举报