Грабим формы!

[img=left]http://im0-tub.yandex.net/i?id=7321772&tov=0[/img]Что такое формграббер? Это программа, которая перехватывает и сохраняет данные, вводимые в формах в браузере. Используются такие программы в основном для перехвата паролей/номеров кредитных карт (или любых других данных) вводимых на сайтах. Конечно, для этой цели можно использовать кейлогер, но разбирать его километровые логи может быть неудобно, поэтому нужна узкоспециализированная система для перехвата исключительно форм. Как такую штуку написать ты можешь узнать в этой статье.Author: Ms-Rem (Ms-Rem@yandex.ru)
Пишем простой формграббер

Что такое формграббер? Это программа, которая перехватывает и сохраняет данные, вводимые в формах в браузере. Используются такие программы в основном для перехвата паролей/номеров кредитных карт (или любых других данных) вводимых на сайтах. Конечно, для этой цели можно использовать кейлогер, но разбирать его километровые логи может быть неудобно, поэтому нужна узкоспециализированная система для перехвата исключительно форм. Как такую штуку написать ты можешь узнать в этой статье.

Что такое вводимая в браузере форма? В каком виде она отправляется по сети? Какие функции при этом вызываются? Прежде чем писать формграббер мы должны ответить на все эти вопросы.

Обмен данными между браузером и веб-сервером происходит по протоколу HTTP. HTTP запрос в общем виде состоит из метода запроса, его заголовков и тела запроса. Например, простейший запрос на получение странички может выглядеть так:

GET /index.htm HTTP/1.1

Host: www.nifiga.net

Connection: close

В данном случае методом запроса является GET, после которого идет краткий URL запроса и протокол (HTTP 1.1). URL может быть представлен как в кратком (/index.htm), так и в полном (www.nifiga.net/index.htm) виде. Все что ниже является заголовками запроса. Например, заголовок Host содержит адрес сервера, к которому будет направлен запрос. Имя и параметры заголовка запроса всегда разделены двоеточием. Заголовок запроса отделен от тела запроса двумя переводами строки (0D0A).

Рассмотрим теперь передачу по сети данных введенных в форму. В этом случае запрос будет подобен этому:

POST http://192.168.0.58/dragon/?.goods.save_goods HTTP/1.1

Referer: http://192.168.0.58/dragon/?.goods.form_edit_goods&category_id=121

Content-Type: multipart/form-data; boundary=—————————7d534bae9d6

Connection: Close

Host: 192.168.0.58

Content-Length: 1024

Cookie: dragon_cookie_index=yes; dragon_cookie_goods=yes

Authorization: Basic Z29sZDp4YXZhRkQz

——————————7d534bae09d6

Content-Disposition: form-data; name=»goods[goodsId]»\’

Data

——————————7d534bae09d6

Content-Disposition: form-data; name=»goods[goodsName]»\’

Name

Content-Disposition: form-data; name=»goods[goodsDescription]»\’

Description

Этот запрос достаточно сложен, хотя из него выброшены все необязательные для понимания принципа работы заголовки, попробуем разобраться в назначении каждой его части. От предыдущего запроса он отличается в первую очередь методом: POST вместо GET. Это означает, что идет запрос не на получение, а на отправку данных. В заголовке запроса присутствует поле Referer, оно определяет страницу, с которой был отправлен запрос. Это очень важная информация, и в формграббере ее нужно обязательно сохранять. Присутствует также поле Content-Type которое определяет тип содержимого запроса. Тип form-data означает, что передаются данные формы, а boundary — строка разделитель полей формы. Поле Cookie содержит посылаемые браузером куки. В них могут храниться авторизационные данные, поэтому содержимое этого поля нас может интересовать. Поле Authorization содержит строку http-авторизации, эти данные также могут представлять для нас интерес. На этом заголовок запроса заканчивается, идет два перевода строки, и начинается тело запроса. Элементы тела запроса разделены между собой строкой, которая передавалась в boundary заголовка запроса.
Каждый элемент имеет строку, идентифицирующую его назначение (Content-Disposition) и одну или несколько строк собственно с данными. Так выглядит запрос, отправленный методом POST. Но содержимое форм может еще передаваться методом GET, при этом параметры передаются прямо в URL запроса. Подобный URL может выглядеть так: http://www.yandex.ru/yandsearch?rpt=rad&text=FormData. Я думаю, ты и сам сможешь понять, что этот запрос значит.

Рассмотрим теперь, что происходит при посылке запроса весьма популярным браузером Internel Explorer. Сначала данные вводятся в поля ввода браузера, при нажатии кнопки Submit происходит отправка этих данных в COM-объект Internet Explorer’а, который является движком браузера. Дальше в COM-объекте происходит формирование запроса и передача его в функциям HttpOpenRequest и HttpSendRequest (WinInet API). Эти функции собирают запрос окончательно и отправляют его через сокеты (send и recv в ws2_32.dll). Далее данные направленные на сокеты отправляются в ядро системы, где после обработки в стеке протоколов TCP/IP они отсылаются через сеть. С другими браузерами дело может обстоять несколько иначе, например они могут не пользоваться функциями WinInet API, а формировать запрос вручную. В любом случае они отправляют данные через сокеты.

Вернемся теперь к нашим баранам (точнее, к формграбберам). Для перехвата данных формы нам нужно вклиниться в описанный выше процесс на любой его стадии. Например, можно получить хэндл окна Internet Explorer, перечислить все дочерние окна, отобрать среди них те, которые нам нужны, и с помощью GetWindowText снять с каждого из них введенный текст. Этот метод применяется в некоторых троянах.

Можно вклиниться и на уровне COM-объекта, через его интерфейсы можно не только снифать посылаемые данные, но даже посылать свои. Этот метод используется в трояне Pinch для обхода файрволов. Недостаток тут только в громоздкости и неудобстве программирования COM-объектов.

Гораздо более перспективным мне кажется метод перехвата функций HttpOpenRequest и HttpSendRequest, так как он позволяет легко получать данные форф. Причем не просто получать, но и сразу же отправлять их, куда надо в обход файрволов. Функция HttpOpenRequest осуществляет открытие соединения, а HttpSendRequest — отсылку самого запроса. Рассмотрим их прототипы:

HINTERNET HttpOpenRequest(

HINTERNET hConnect,

LPCTSTR lpszVerb,

LPCTSTR lpszObjectName,

LPCTSTR lpszVersion,

LPCTSTR lpszReferer,

LPCTSTR* lpszAcceptTypes,

DWORD dwFlags,

DWORD_PTR dwContext

);

hConnect — хэндл открытого соединения.

lpszVerb — строка метода запроса (GET, POST, HEAD etc).

lpszObjectName – URL, к которому направляется запрос. В нем могут передаваться данные формы. Я думаю, ты уже разобрался с форматом этих данных из предыдущего описания.
lpszVersion — версия используемого протокола (HTTP/1.0 или HTTP/1.1).

lpszReferer — содержимое заголовка Referer (адрес страницы с которой был послан запрос).

lpszAcceptTypes — типы принимаемых браузером данных.

dwFlags — комбинация управляющих флагов. Подробнее о них ты можешь почитать в MSDN.

dwContext — дополнительные данные, которые приложение может передать с запросом.

BOOL HttpSendRequest(

HINTERNET hRequest,

LPCTSTR lpszHeaders,

DWORD dwHeadersLength,

LPVOID lpOptional,

DWORD dwOptionalLength

);

hRequest — хэндл запроса, полученный функцией HttpOpenRequest.

lpszHeaders — указатель на заголовки запроса. Формат заголовков HTTP-протокола мы рассмотрели выше.

dwHeadersLength — размер заголовков.

lpOptional — указатель на тело запроса. В нашем случае он будет содержать данные формы. С форматом этих данных мы уже тоже разобрались.

dwOptionalLength — размер данных тела запроса.

Перехватываем WinInet API

Теперь нам нужно научиться перехватывать функции HttpOpenRequest и HttpSendRequest и сохранять в лог проходящие через них данные.

Для этого нам нужно сначала загрузить свою DLL во все процессы системы. Это можно сделать, например, прописав DLL в разделе реестра HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionWindows в ключ AppInit_DLLs (или с помощью хуков или создания удаленных потоков, – прим. ред). Такая библиотека будет подгружена во все процессы, имеющие в своем адресном пространстве user32.dll (а это все GUI приложения). Для перехвата будем использовать метод сплайсинга. Заключается он в перезаписи начала кода перехватываемой функции 5 байтным jmp на свою функцию с предварительным копированием затертых байт в выделенный буфер и установкой после них jmp на продолжение функции. Главная проблема тут состоит в том, что нам нужно скопировать целое количество инструкций, а они могут иметь разный размер, следовательно, нам понадобиться дизассемблер длин. Все вышеописанное уже реализовано в моей библиотеке advApiHook, поэтому можно просто подключить ее и не париться.

Сам же код DLL будет выглядеть приблизительно так:

library FormGrab;

uses

windows,

advApiHook;

var

TrueHttpSendRequest: function(hRequest: dword; lpszHeaders: PChar;

dwHeadersLength: dword; lpOptional: pointer;

dwOptionalLength: dword): boolean; stdcall;

function NewHttpSendRequest(hRequest: dword; lpszHeaders: PChar;

dwHeadersLength: dword; lpOptional: pointer;

dwOptionalLength: dword): boolean; stdcall;

begin

MessageBoxA(0, lpszHeaders, lpOptional, 0);

Result := TrueHttpSendRequest(hRequest, lpszHeaders, dwHeadersLength,

lpOptional, dwOptionalLength);

end;

begin

HookProc(\’wininet.dll\’, \’HttpSendRequestA\’, @NewHttpSendRequest, @TrueHttpSendRequest);

end.

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

Фильтрация полученных данных

При большом количестве машин, на которых установлен формграббер возникают сложности в отсеивании необходимых нам данных. Обычно хакера ведь интересуют данные введенные только в определенных формах и на конкретных сайтах, поэтому требуется сохранять не все полученные данные, а только те, которые реально пригодятся. Для этого можно проанализировать URL и Referer запроса и отсеять только нужные формы. Неплохо будет анализировать не только запрос, но и ответ сервера. Например, при вводе какого-либо пароля нам нужно получить только правильные пароли, а случайные ошибки ввода следует фильтровать. Для этого можно анализировать код ответа сервера, либо содержимое возвращаемой HTML-страницы по определенным ключевым словам. Таким образом работает большинство формграбберов.

Недостаток вышеприведенного способа один — он работает только с Internet Explorer’ом и другими браузерами построенными на его движке (например, Avant Browser). Для преодоления этого недостатка можно перехватывать не WinInet API, а send из ws2_32.dll, после чего нам нужно будет собирать посылаемый запрос в буфере и сохранять его в момент окончания отправки данных. Этот момент можно определить по вызову closesocket для интересующего нас сокета, либо можно извлечь ContentLength из передаваемых заголовков и определить по нему момент окончания передачи тела запроса. Естественно первый метод значительно проще в реализации, да и особых недостатков у него не имеется. Нам просто нужно вести список сокетов, и для каждого сокета строить список принятых пакетов. При закрытии сокета собранные данные обрабатываются. Для всего этого нам нужно перехватывать всего три API-функции: это connect, send и cosesocket. Этот метод будет работать со всеми браузерами без исключения, но он сложен в реализации и имеет один неприятный недостаток — невозможность перехвата шифрованных форм. Большинство банковских служб, интернет-магазинов и других требующих безопасности ресурсов работают не по открытому HTTP-протоколу, а через SSL/TSL соединение, и все данные передаются по сети в шифрованом виде. Естественно, в таком же виде они и посылаются на сокеты. Поэтому нам нужно сохранять только данные, отправляемые по нешифрованому HTTP-протоколу. Отличить простой HTTP от SSL легко: первый имеет стандартный порт сервера 80, а второй 443. Конечно, эти сервисы могут быть и на нестандартных портах, но это случается весьма редко, поэтому нам нужно перехватывать трафик, идущий только на 80 порт, это легко определить по параметрам вызова connect.

Законность.

Ты, наверно, не раз задумывался о том, что будет в случае поимки автора формграбера. Будет ли это дело квалифицировано по статье 273 УК РФ «Создание и распостранение вредоносных програм для ЭВМ» зависит от того, какова функциональность твоего формграббера, и от того, нанес ли он кому-нибудь реальный ущерб. Простой формграббер, сохраняющий на диск все отправляемые через браузер данные, под категорию вредоносных программ вряд ли попадает, так как подобные вещи используют даже в сетях крупных организаций для наблюдения над своими сотрудниками. А вот формграббер, определяющий банковские системы и отправляющий данные на мыло в обход файрволов, под категорию вредоносных программ, несомненно, попадает. Конечно, если никто от хакерского творения не пострадал, то никто никого искать не будет, а формграббер просто добавят в базы антивирусов, но если денег лишился влиятельный человек или солидная организация, то могут развернуться весьма масштабные поиски. В таком случае, если автора поймают, то, скорее всего, дадут не 273, а 159 статью (мошенничество), и наказание за это будет более строгим, чем за написание вредоносных программ. В общем, не рекомендую тебе заниматься чем-либо, что может как-то плохо отразиться на твоей будущей светлой жизни.

WARNING

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