一例cobalt Strike 反射式注入payload的分析

一例cobalt Strike payload 反射式dll注入的分析

QakBot(Qbot)与cobalt Strike恶意流量样本分析 | Demon (ggsec.cn)这篇博客中末尾提到了一个cobastrick的payload,这是一段shellcode,主要功能是的解密出一个dll,采用反射式注入的方式启动这个dll。本文主要是分析这段payload,有助于理解reflectiveinject的原理。

反射式注入 reflectiveInject的介绍可参考 反射式dll注入(ReflectiveDLLInjection)_随心动,随风行的博客-CSDN博客_reflectivedllinjection
github上有相应的实现
https://github.com/stephenfewer/ReflectiveDLLInjection

样本信息

MD5: 7c01dd99cee0668afc9b32308b8132f5
SHA1: dd1d89906e903caab123586f9b3369326200aeef
SHA256: 09ffa300c2343d68f7c9cc1a7e8f136c5c99cabd5e69a7326efabfece7aff59d
SHA512: dec9e255cb79c13e40539a024e2ffc290249810f7d51a418e62bf60a37d4cbf546e4662f4559c78a4832e637f71a91cbffbbb5042ba3823e24532c334c8a37ef
CRC32: 124f37e5

分析环境

  • win7 x64 虚拟机

  • IDA Pro6.8

  • OD

  • vs2010

静态分析

对样本进行进行静态分析,主要功能是在内存中解密并执行一段shellcode,将跳转执行,如下图所示
在这里插入图片描述

由上面的分析可知,加密的shellcode大小为0x00033000,在文件中的偏移为0x42,基于上图中的分析,可对这段shellcode进行解密。下面是提取并解密shellcode的代码

#include <stdio.h>
#include <windows.h>

int main(int argc,char** argv){

    FILE* file  = fopen("shellcode.bin","rb");
    DWORD sc_size = 0x00033000;
    DWORD offset = 0x42;
    fseek(file,offset,SEEK_SET);
    char* buf = (char*)malloc(sc_size);
    memset(buf,0,sc_size);

    size_t readbytes = 0;
    while(readbytes < sc_size){
        size_t r = fread(buf + readbytes,1,sc_size-readbytes,file);
        if(r < 0) break;
        readbytes += r;
    }
    fclose(file);

    if (sc_size == readbytes){

        //解密
        DWORD key = 0x04DBA13B;
        DWORD *_buf = (DWORD*)buf;
        DWORD _sc_size = sc_size / sizeof(DWORD);
        for(int i = 0;i< _sc_size;i++){
            _buf[i] ^= key;
            key ^= _buf[i];
        }

        //导出
        FILE* outfile = fopen("shellcode1.bin","wb");
        fwrite(buf,1,sc_size,outfile);
        fclose(outfile);
    }

    free(buf);
    return 0;
}

下面对shellcode1.bin进行分析,这个文件比较特殊的地方是即是一段shellcode,又是一个合法的dll,先使用IDA用binary file模式打开

在这里插入图片描述

进入了sub_8157看一下

上面是以binary方式打开的,下面以pe形式打开定位到这个函数。

0x8157是文件偏移,我们需要转化成VA,使用LordPE的位置计算机计算出VA为0x10008D57

在这里插入图片描述

用OD使用pe模式打开shellcode1.bin,定位到函数 0x10008D57,发现这是dll的一个导出函数,函数名为ReflectiveLoader,功能为一个以shellcode的方式来加加载dll。
在这里插入图片描述

下面我们来分析一个这个函数
首先找到PE的起始位置
在这里插入图片描述

下面这段是shellcode的通用操作,通过PEB找到kernel32.dll在内存中的基址,可参考[原创]PEB结构:获取模块kernel32基址技术及原理分析-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

LDR_DATA_TABLE_ENTRY 结构体可参考Vergilius Project | _LDR_DATA_TABLE_ENTRY

在这里插入图片描述

从kernerl32.dll的导出表中找到下列几个api的地址

LoadLibraryA 
GetProcAddress 
VirtualAlloc 
VirtualProtect 
LoadLibraryExA 
GetModuleHandleA

在这里插入图片描述

下面这段代码复现了上面通过PEB获取api地址的过程,并导出api的hash表

#include <stdio.h>
#include <windows.h>
#include <winternl.h> 

//计算哈希值
#define ROTR32(value, shift)    (((DWORD) value >> (BYTE) shift) | ((DWORD) value << (32 - (BYTE) shift)))

//重新定义PEB结构。winternl.h中的结构定义是不完整的。
typedef struct _MY_PEB_LDR_DATA {
    ULONG Length;
    BOOL Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
} MY_PEB_LDR_DATA, *PMY_PEB_LDR_DATA;

typedef struct _MY_LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
} MY_LDR_DATA_TABLE_ENTRY, *PMY_LDR_DATA_TABLE_ENTRY;



#define ROTR32(value, shift)    (((DWORD) value >> (BYTE) shift) | ((DWORD) val
ue << (32 - (BYTE) shift)))
//导出api的hash值
void ExportProcAddressHash()
{
    PPEB PebAddress;
    PMY_PEB_LDR_DATA pLdr;
    PMY_LDR_DATA_TABLE_ENTRY pDataTableEntry;
    PVOID pModuleBase;
    PIMAGE_NT_HEADERS pNTHeader;
    DWORD dwExportDirRVA;
    PIMAGE_EXPORT_DIRECTORY pExportDir;
    PLIST_ENTRY pNextModule;
    DWORD dwNumFunctions;
    USHORT usOrdinalTableIndex;
    PDWORD pdwFunctionNameBase;
    PCSTR pFunctionName;
    UNICODE_STRING BaseDllName;
    DWORD dwModuleHash;
    DWORD dwFunctionHash;
    PCSTR pTempChar;

    PebAddress = (PPEB)__readfsdword(0x30);
    pLdr = (PMY_PEB_LDR_DATA)PebAddress->Ldr;
    pNextModule = pLdr->InLoadOrderModuleList.Flink;
    pDataTableEntry = (PMY_LDR_DATA_TABLE_ENTRY)pNextModule;



    while (pDataTableEntry->DllBase != NULL)
    {
        dwModuleHash = 0;
        pModuleBase = pDataTableEntry->DllBase;
        BaseDllName = pDataTableEntry->BaseDllName;
        pNTHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)pModuleBase + ((PIMAGE_DOS_HEADER)pModuleBase)->e_lfanew);
        dwExportDirRVA = pNTHeader->OptionalHeader.DataDirectory[0].VirtualAddress;
        //获取下一个模块地址
        pDataTableEntry = (PMY_LDR_DATA_TABLE_ENTRY)pDataTableEntry->InLoadOrderLinks.Flink;


        // 如果当前模块不导出任何函数,则转到下一个模块 加载模块入口
        if (dwExportDirRVA == 0)
        {
            continue;
        }


        //计算dll名的哈希值
        for (int i = 0; i < BaseDllName.Length; i++)
        {
            pTempChar = ((PCSTR)BaseDllName.Buffer + i);
            dwModuleHash = ROTR32(dwModuleHash, 13);

            if (*pTempChar >= 0x61)
            {
                dwModuleHash += *pTempChar - 0x20;
            }
            else
            {
                dwModuleHash += *pTempChar;
            }
        }




        pExportDir = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)pModuleBase + dwExportDirRVA);

        dwNumFunctions = pExportDir->NumberOfNames;
        pdwFunctionNameBase = (PDWORD)((PCHAR)pModuleBase + pExportDir->AddressOfNames);

        for (int i = 0; i < dwNumFunctions; i++)
        {
            dwFunctionHash = 0;
            pFunctionName = (PCSTR)(*pdwFunctionNameBase + (ULONG_PTR)pModuleBase);
            pdwFunctionNameBase++;

            pTempChar = pFunctionName;
            //计算函数名称的hash
            do
            {
                dwFunctionHash = ROTR32(dwFunctionHash, 13);
                dwFunctionHash += *pTempChar;
                pTempChar++;
            } while (*pTempChar);

            printf("%ws 0x%.8X %s 0x%.8X\n",BaseDllName.Buffer,dwModuleHash,pFunctionName,dwFunctionHash);

        }




    }

}

导出的结果如下图所示,得到的hash与样本分析的一致。

在这里插入图片描述

使用VirtualAlloc分配一段内存

在这里插入图片描述

拷贝PE头部到分配的内存

在这里插入图片描述

根据节表将各个节拷贝到内存相应位置
在这里插入图片描述

根据导入表,加载相应的动态库,更新IAT,页面还有解密dll名称和函数名称的操作

在这里插入图片描述

地址重定位

在这里插入图片描述

将.text节属性修改为可执行,定位dll的入口点,执行DllMain(selfHanlde,DLL_PROCESS_ATTCH,.),最后返回DllEntryPoint的地址
在这里插入图片描述

在汇编语言中如何获取当前指令的地址,可以用这种方法,在这个样本中,两次用到了这个技术。

call    $+5    ;将当前指令的下一条指令的地址入栈
pop     eax    ;将栈顶的值给eax,获取当前指令的地址

动态分析

因为样本是一段shellcode,无法自己运行,需要加载,手动完成一个shellcode加载器,开发环境是vs2010,代码如下 :

#include <stdio.h>
#include <windows.h>

int LoadShellCode(int argc,char** argv){
    //打开shellcode文件 
    FILE* file = fopen("shellcode.bin","rb");

    //读取文件大小
    fseek(file,0,SEEK_END);
    size_t filesize = ftell(file);
    rewind(file);

    //分配内存,注意页面属性为PAGE_EXECUTE_READWRITE
    char* buf  = (char*)VirtualAlloc(NULL,filesize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    memset(buf,0,filesize);

    //读取文件内容
    size_t readbytes = 0;
    while(readbytes < filesize){
        size_t r = fread(buf + readbytes,1,filesize-readbytes,file);
        if(r < 0) break;
        readbytes += r;
    }

    //跳转到shellcode起始位置执行
    if(readbytes == filesize){
        _asm{
            jmp buf
        }
    }

    //关闭文件,释放内存
    fclose(file);
    VirtualFree(buf,0,MEM_RELEASE);
    return 0;
}

编译生成LoadShellcode.exe,使用IDA打开,找到上面jmp buf语句的VA为0x004114DA

在这里插入图片描述

使用OD调试LoadShellcode.exe,在0x004114DA处中断

在这里插入图片描述

F7单步进入shellcode,用IDA以binary模式打开shellcode.bin文件,反汇编后的代码与OD中的一致,说明已经成功的跳转到shellcode的起始位置

在这里插入图片描述

解密出来一个PE文件,地址为0x00230042 大小为0x00033000

在这里插入图片描述

使用pchunter将这个PE导出来

在这里插入图片描述

其实这个dll是一个corbastrike payload,我们直接动态调试可获取其CC服务器

在这里插入图片描述

检索这个域名 amajai-technologies.work,就可知道这是一个QakBot家族的恶意样本。

在这里插入图片描述

VT上的搜索结果

在这里插入图片描述

参考资料

QakBot(Qbot)与cobalt Strike恶意流量样本分析 | Demon (ggsec.cn)

Malware-Traffic-Analysis.net - 2020-12-07 - Qakbot (Qbot) infection with Cobalt Strike (Beacon) and spambot activity

反射式dll注入(ReflectiveDLLInjection)_随心动,随风行的博客-CSDN博客_reflectivedllinjection

[原创]PEB结构:获取模块kernel32基址技术及原理分析-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

Vergilius Project | _LDR_DATA_TABLE_ENTRY
https://github.com/stephenfewer/ReflectiveDLLInjection

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值