Думаете спам-фильтры спасут от взлома? Как бы не так.
Фишинговые рассылки давно перестали выглядеть как кривые письма с битой версткой и ссылкой на подозрительный домен. В кампании, которую исследователи отслеживают под именем GTFire, злоумышленники опираются на инфраструктуру Google и прячут вредоносные переходы за привычными сервисами.
Схема держится на двух сервисах Google. Для размещения фишинговых страниц используют Google Firebase Hosting , конкретно бесплатный хостинг на домене web.app. Для маскировки ссылок используют Google Translate , который выступает промежуточным слоем и скрывает конечный адрес. В итоге ссылка в письме выглядит как адрес переводчика, проходит часть фильтров за счет репутации Google и уже потом раскрывается на фишинговый хостинг.
У кампании есть прием, который снижает настороженность систем защиты. После ввода данных на поддельной странице пользователя часто перенаправляют на настоящий сайт бренда. Человек видит обычный ресурс и может списать предыдущие странности на ошибку входа, и расследование в компании, соответственно, начнется позже, чем могло бы.
По данным анализа, кампания примечательна не только продуманностью, но и масштабом. В открытых элементах инфраструктуры управления исследователи увидели тысячи украденных учетных данных, связанные более чем с 1 000 организаций. География затронутых жертв охватывает более 100 стран, а отраслей насчитывается больше 200.
Переводческий прокси дает атакующим несколько преимуществ. Конечный адрес фишинговой страницы скрывается до последнего шага. Доверие к домену Google повышает шанс клика. Почтовые шлюзы и веб-фильтры, завязанные на репутацию домена, могут пропускать такую ссылку чаще, чем прямую ссылку на неизвестный хостинг. Дополнительно усложняется автоматический анализ, потому что домен web.app становится виден только после полного разрешения цепочки редиректов, например в сетевых логах или в инструментах разработчика.
В описании цепочки редиректов встречаются характерные элементы: представления международных доменных имен в Punycode, поддомены переводческого прокси, а также динамически сгенерированные сегменты пути. Пока цепочка не закончилась, наблюдатель чаще видит только translate.goog и производные домены, а не финальный *.web.app.
Авторы приводят пример перехода с конкретными URL. В качестве первого слоя жертва получает ссылку translate.goog с длинной строкой параметров и Punycode-фрагментами:
Отдельная линия маскировки связана с параметрами URL. В ссылках часто встречаются Base64-кодированные параметры, которые могут содержать данные, привязанные к жертве, например адрес электронной почты, языковые предпочтения и целевой бренд. В некоторых случаях параметры кодируют дважды, чтобы усложнить разбор и снизить эффективность сигнатурного детектирования. В материале приводится пример строки, которую нужно декодировать в два шага:
Ниже показана механика двойного декодирования Base64. Пример помогает понять, почему простые правила, которые ищут ключевые слова в URL, часто промахиваются:
import base64 s = "WTJGc2JDNTJhWEowZFdGc09VQmlZVzV2Y25SbExtTnZiUT09OkI0SUTNY" first = base64.b64decode(s).decode("utf-8", errors="replace") # В некоторых схемах полезная часть идет до двоеточия payload = first.split(":", 1)[0] second = base64.b64decode(payload).decode("utf-8", errors="replace") print("first:", first) print("second:", second)
Сбор учетных данных у GTFire выглядит простым, но построен так, чтобы вытянуть рабочий пароль и не вызвать подозрений. После попадания на поддельную страницу жертва вводит учетные данные. Страница показывает сообщение о неверном пароле, хотя проверка на стороне реального сервиса не происходит. Первая пара данных уходит атакующим незаметно. Затем интерфейс просит ввести пароль еще раз. Вторая попытка тоже уходит на сервер сбора. После второго ввода жертву перенаправляют на настоящий сайт бренда.
Две попытки повышают шанс получить корректный пароль. При первом вводе люди часто ошибаются. Повтор выглядит как обычная реакция сайта на опечатку, поэтому момент утечки игнорируется. Пароли передают в Base64 и добавляют метаданные: адрес электронной почты жертвы, страну посещения и язык браузера. Такой контекст помогает атакующим фильтровать потоки и дальше работать с учетными записями, например разделять данные по регионам и языкам.
В материале приведена структура запроса, через который уходит сбор:
GET /myown/All-in-1.php?user=&pass=&pass2=&visit=&lang= HTTP/1.1
Основная инфраструктура управления, которую наблюдали исследователи, работает на экземплярах LiteSpeed Web Server. На серверах размещены централизованные PHP-скрипты сбора, в том числе упоминается файл All-in-1.php. Авторы отдельно подчеркивают ставку на готовые коммерческие наборы, так называемые All-in-1 PHP phishing scripts. Такой выбор снижает нагрузку на разработку, ускоряет развертывание и уменьшает число уникальных признаков, по которым кампанию можно уверенно привязать к конкретной группе.
Для защиты кампания показывает неприятную реальность: доверенные сервисы можно превратить в транспорт для фишинга с минимальными затратами, а детектирование только по доменам и репутации перестает работать. Блокировать translate.goog полностью часто невозможно, а полная блокировка web.app ударит по легитимным проектам.
В качестве опорных признаков для охоты в логах подойдут связки из нескольких элементов: входящие сообщения со ссылками translate.goog, длинные параметры, присутствие Base64-строк, редиректы через Punycode-домены, переход на случайные поддомены *.web.app, а затем подозрительные GET-запросы на PHP-коллекторы с параметрами типа user, pass, pass2, visit, lang.
Ниже несколько примеров запросов, которые можно использовать как основу для поиска. Конкретные поля и источники данных нужно подстроить под почтовый шлюз, прокси, SIEM или EDR.
Поиск писем со ссылками translate.goog и слишком длинными URL:
index=mail | search url_domain="translate.goog" OR url="*translate.goog*" | where len(url) > 120 | stats count values(sender) values(subject) by recipient
Корреляция сетевых событий, где после translate.goog появляется переход на .web.app:
let lookback = 7d; DeviceNetworkEvents | where Timestamp > ago(lookback) | where RemoteUrl has "translate.goog" | join kind=inner (DeviceNetworkEvents| where Timestamp > ago(lookback)| where RemoteUrl endswith ".web.app" or RemoteUrl has ".web.app/" ) on DeviceId | project Timestamp, DeviceName, RemoteUrl, InitiatingProcessFileName
Грубая эвристика для поддоменов web.app со случайными именами 6–10 символов:
import re def looks_random(label: str) -> bool:return bool(re.fullmatch(r"[a-z0-9]{6,10}", label)) tests = ["it1lhz", "myapp", "a9k3z1p0", "secure-login"] for t in testsrint(t, looks_random(t))
История GTFire хорошо показывает, как меняется фишинг . Атакующие все чаще прячут вредоносные переходы внутри доверенной инфраструктуры и заставляют защиту смотреть не на один домен, а на цепочку событий. В таких кампаниях решают не блоклисты, а поведенческие признаки, корреляция редиректов и умение замечать злоупотребление легитимными сервисами.