Як дослідник в Tenable, ми маємо кілька періодів протягом року, коли можна працювати над темою на свій вибір, за умови, що це представляє інтерес для команди. Особисто я вирішив провести огляд коду на проекті Ruby on Rails.
Основна мета — зосередитись на перевірці коду, його розумінні та взаємодії між компонентами.
Я обрав Solidus, який є відкритим фреймворком для електронної комерції для піонерів галузі. Спочатку цей проект був форком Spree.
Розроблений на основі Ruby on Rails, Solidus складається з кількох гемів. Коли ви підключаєте гем solidus у вашому Gemfile, Bundler встановить всі наступні геми:
- solidus_api (RESTful API)
- solidus_backend (Адмін-панель)
- solidus_core (Основні моделі, поштові системи та класи)
- solidus_sample (Приклад даних)
Всі ці геми створені для того, щоб працювати разом, надаючи повноцінну платформу для електронної комерції.
https://www.tenable.com/research
Вибір проекту
Solidus не був моїм першим вибором, я спочатку хотів вибрати Zammad, який є веб-системою підтримки клієнтів з відкритим кодом, також розробленою на Ruby on Rails.
Проект досить популярний, і, після швидкого огляду, має хороший потенціал для атак. Цей тип проектів також цікавий, бо для багатьох бізнесів компонент підтримки/тестування критичний, тому виявлення вразливості в проекті на кшталт Zammad майже гарантовано забезпечить цікаву вразливість!
З різних причин, чи то на моєму професійному, чи то на особистому ноутбуці, мені потрібно запускати проект у Docker, що сьогодні досить поширено для веб-проектів, але:
Zammad — це проект, що потребує багатьох компонентів, таких як Elasticsearch, Memcached, PostgresQL і Redis. І хоча проект надає готовий docker-compose, як тільки я спробував використовувати його в режимі розробки, проект не ініціалізувався належним чином.
Замість того, щоб витрачати занадто багато часу, я вирішив відкласти його на потім (безсумнівно) і вибрати інший проект, який здався простішим для старту.
Переглядаючи Github, я натрапив на Solidus, який не тільки пропонує інструкції для налаштування середовища розробки всього в кількох рядках, але й має деякі публічні вразливості.
Для нас це загалом хороший знак з точки зору комунікації у разі виявлення проблем. Це показує, що видавець готовий до обміну, що, на жаль, не завжди так.
Насправді, я також мав кілька проблем з Dockerfile для Solidus, але переглянувши issues та зробивши деякі модифікації самостійно, я зміг швидко запустити проект.
Проект запустився за допомогою команди bin/dev
Архітектура Ruby on Rails та поверхня для атак
Як і багато веб-фреймворків, Ruby on Rails використовує архітектуру MVC, хоча це не є темою цього блогу, короткий нагадування не завадить, щоб ви краще зрозуміли решту:
- Модель містить дані та логіку, що стосується цих даних (перевірка, реєстрація тощо)
- Представлення показує результат користувачу
- Контролер обробляє дії користувача та змінює дані моделі і представлення
Це є зв’язок між моделлю та представленням.
Ще один важливий аспект Ruby on Rails — це те, що цей фреймворк керується принципом “Конвенція понад конфігурацію”, що означає, що багато рішень прийнято за вас, і що всі середовища будуть мати схожості, що полегшує розуміння проекту з точки зору атакувальника, якщо ви знаєте, як працює фреймворк.
У проекті Ruby on Rails маршрутизація застосунку керується безпосередньо файлом ‘config/routes.rb’. У цьому файлі визначено всі можливі дії!
Як було пояснено в розділі огляду, Solidus складається з набору гемів (Core, Backend та API), розроблених для того, щоб працювати разом і забезпечити повноцінну платформу для електронної комерції.
Ці три компоненти незалежні один від одного, тому коли ми перевіряємо проект Github Solidus/Solidus, ми фактично перевіряємо кілька проектів з різними поверхнями атак, які більш чи менш взаємопов’язані.
Solidus має три основних файли маршрутів:
- Admin Простір імен SolidusAdmin::Engine
- Backend Простір імен Spree::Core::Engine
- API Простір імен Spree::Core::Engine
Два з трьох файлів знаходяться в одному просторі імен, тоді як Admin є більш відокремленим.
Простір імен можна розглядати як “групу”, що містить класи, константи чи інші модулі. Це дозволяє структурувати ваш проект. Тут важливо розуміти, що API та Backend безпосередньо пов’язані, але не можуть взаємодіяти безпосередньо з Admin.
Якщо ми уважніше подивимось на файл, то побачимо, що маршрути визначені кількома способами. Не вдаючись у всі деталі та нюанси, ви можете визначити маршрут безпосередньо, наприклад:
get '/orders/mine', to: 'orders#mine', as: 'my_orders'
Це означає “Запит GET на /orders/mine” буде надіслано до методу “mine” контролера “Orders” (ми не будемо звертати увагу на “as: ‘my_orders’” тут).
module Spree
module Api
class OrdersController < Spree::Api::BaseController
#[...]
def mine
if current_api_user
@orders = current_api_user.orders.by_store(current_store).reverse_chronological.ransack(params[:q]).result
@orders = paginate(@orders)
else
render "spree/api/errors/unauthorized", status: :unauthorized
end
end
#[...]
Або через систему CRUD, використовуючи щось на кшталт:
resources :customer_returns, except: :destroy
Для пояснень я перейду безпосередньо до того, що пояснюється в документації Ruby on Rails:
“У Rails ресурсний маршрут забезпечує відповідність між HTTP дієсловами та URL для дій контролера. За умовчанням кожна дія також відповідає конкретній операції CRUD в базі даних.”
Отже, тут ресурс :customer_returns буде зв’язаний з контролером CustomerReturns для таких URL:
- GET /customer_returns
- GET /customer_returns/new
- POST /customer_returns
- GET /customer_returns/:id
- GET /customer_returns/:id/edit
- PATCH/PUT /customer_returns/:id
- ̶D̶E̶L̶E̶T̶E̶ ̶/̶c̶u̶s̶t̶o̶m̶e̶r̶_̶r̶e̶t̶u̶r̶n̶s̶/̶:̶i̶d̶ ігнорується через “except: :destroy”
Отже, з цього можна легко побачити, що Solidus має значну поверхню для атак.
Статичний аналіз коду
Цей проект також дає мені можливість протестувати різні інструменти для статичного аналізу коду.
Я не очікую багато від цих інструментів, але оскільки я не використовую їх регулярно, це дозволяє мені побачити, що нового і що розвивається.
Перевага відкритого проекту на Github полягає в тому, що багато інструментів для статичного аналізу можна запустити через Github Action, принаймні… теоретично.
Не рахуючи всіх перевірених інструментів, CodeQL — єдиний, який я зміг запустити “з коробки” через Github Action, результати потім безпосередньо видні у вкладці Security.
Витяг з вразливостей, виявлених за допомогою CodeQL
Обробка результатів від усіх інструментів займає багато часу, багато з них є зайвими, і я також спостерігав, що деякі платні інструменти насправді є лише обгортками відкритих інструментів, таких як Semgrep (результати точно такі ж / ті ж самі фрази).
Особливе згадування заслуговує Brakeman, інструмент, присвячений аналізу коду Ruby on Rails. Цей інструмент дозволяє швидко і просто знайти деякі цікаві шляхи для дослідження у зручному вигляді.
Витяг з вразливостей, виявлених за допомогою Brakeman
Не розглядаючи всі відкриття, які я відкинув (шляхи для дослідження). Деякі вразливості легко виключити. Наприклад, виявлення “Поліноміальний регулярний вираз, використаний на неконтрольованих даних” від CodeQL:
Окрім того, що це здається неексплуатованим для мене, цей випадок не є дуже цікавим, оскільки він стосується адміністративної частини, і для його використання потрібні підвищені привілеї.
Тепер розглянемо цей приклад “SQL Injection” від Brakeman:
Оскільки аналіз позбавлений контексту, він не знає, що насправді “pricetablename” не відповідає введенню користувача, а є викликом методу, який повертає назву таблиці (яка, отже, не піддається контролю з боку користувача).
Проте ці інструменти залишаються цікавими, оскільки вони можуть надати швидкий огляд областей для подальшого дослідження.
Ідентифікація сайту на Solidus
Перед тим як зануритися в деталі теми, може бути цікаво визначити, чи використовує відвіданий сайт Solidus, і для цього існує кілька методів.
На головній сторінці магазину можна шукати такі патерни:
Powered by Solidus
/assets/spree/frontend/solidus_starter_frontend
Або перевірити, чи доступні такі функції JS:
Solidus()
SolidusPaypalBraintree
Або, врешті-решт, відвідати адміністративну панель, доступну за адресою ‘/admin/login’, і шукати один з наступних патернів:
Після того як замовлення зроблено, дії користувача досить обмежені
- Переглянути свої замовлення
- Переглянути конкретне замовлення (але без PDF або відстеження)
- Оновити свою інформацію (тільки email і пароль)
Додамо, що коли замовлення оформлено, користувачеві надсилається email, а новий email відправляється, коли продукт відправлено.
Адміністративна панель також досить обмежена, окрім класичних функцій інтернет-магазину (управління замовленнями, продуктами, запасами тощо), є лише невелика кількість налаштувань, які можна зробити безпосередньо з панелі.
Наприклад, неможливо налаштувати SMTP, це налаштування потрібно робити безпосередньо в конфігурації Rails проекту.
Аутентифікація та управління сесіями
Аутентифікація є важливим аспектом безпеки веб-додатків. Вона гарантує, що лише авторизовані особи мають доступ до функцій додатку та чутливої інформації.
Devise — це популярний, високонастроюваний та надійний Ruby on Rails gem для аутентифікації користувачів. Цей gem забезпечує повне рішення для управління аутентифікацією, включаючи створення акаунтів, вхід, вихід та скидання паролю.
Однією з причин, чому Devise вважається надійним рішенням, є його здатність підтримувати розширені функції безпеки, такі як перевірка email, двофакторна аутентифікація та управління сесіями. Крім того, Devise регулярно оновлюється для виправлення вразливостей безпеки та покращення своїх функцій.
Коли я налаштовував свій проект Solidus, була використана версія 4.9.3 Devise, тобто остання доступна версія, тому я не витратив багато часу на цю частину, яка одразу здавалася мені тупиковою.
Авторизація та управління дозволами
Авторизація та управління дозволами — ще один критично важливий аспект безпеки веб-додатків. Вона гарантує, що користувачі мають доступ лише до тих функцій та даних, до яких їм дозволено доступати на основі їх ролі чи дозволів.
За замовчуванням, Solidus має лише дві визначені ролі:
- SuperUser: Тобто адміністратор, який має доступ до всіх функцій
- DefaultCustomer: Роль, яка за замовчуванням присвоюється під час реєстрації, базова роль, яка просто дозволяє робити покупки на сайті
Для управління цією частиною, Solidus використовує gem під назвою CanCanCan. Як і Devise, CanCanCan вважається надійним рішенням завдяки здатності підтримувати складні сценарії авторизації, такі як ієрархічні ролі та динамічні дозволи. Крім того, CanCanCan дуже настроюється.
Також, CanCanCan добре протестований та надійний, що робить його безпечним вибором для критичних застосунків.
Він також має активну спільноту розробників, які можуть надати допомогу та поради, якщо це необхідно.
Декілька цікавих тем
1/ Не дуже цікаве міжсайтове скриптування (Cross-Site Scripting)
Знаходити вразливості — це цікаво, особливо якщо вони критичні, але багато статей не пояснюють, що пошук займає багато часу, і багато спроб не призводять до результату.
Розкопуючи ці вразливості, навіть знаючи, що вони не приведуть до нічого, не завжди марно.
Візьмемо, наприклад, виявлення Brakeman:
Незважаючи на наявність :target => “_blank”
, що ускладнює використання XSS (або через божевільні комбінації, такі як колесо миші), мені було цікаво розібратися в цій частині коду і зрозуміти, як можна здійснити таку ін'єкцію, просто тому, що це стосується адміністративної частини.
Ось як можна було б експлуатувати цю вразливість:
1/ Адміністратор повинен змінити методи доставки, щоб додати javascript:alert(document.domain)
як URL для відстеження.
2/ Користувач повинен оформити замовлення.
3/ Адміністратор повинен підтвердити замовлення та додати номер для відстеження.
4/ URL для відстеження буде відповідати payload, який можна буде активувати через колесо миші.
За замовчуванням, оскільки єдиною можливістю є роль адміністратора, єдина можливість — це коли один адміністратор заманює іншого адміністратора... іншими словами, це далеко не цікаво.
Примітка: Згідно з документацією Solidus, у налаштуванні, яке відрізняється від базового, користувач з меншими привілеями міг би експлуатувати цю вразливість.
Хоча вплив і експлуатація дуже низькі, ми вказали на цю вразливість для Solidus. Незважаючи на кілька спроб зв'язатися з ними, ми не отримали відповіді.
Вразливість була опублікована під CVE-2024–4859
2/ Solidus, Машина станів (State Machine) та умови гонки (Race Conditions)
У веб-магазині я вважаю, що тестування умов гонки — це корисна річ, оскільки певні функції підходять для такого тесту, наприклад, знижки.
Але перед тим, як говорити про умови гонки, потрібно зрозуміти концепцію Машини станів (State Machine).
Машина станів — це модель поведінки, що використовується в розробці програмного забезпечення для представлення різних станів, у яких може перебувати об'єкт або система, а також переходів між цими станами. У контексті веб-додатку, машина станів може використовуватися для моделювання різних станів, в яких може перебувати користувач або ресурс, а також дій, які можуть бути виконані для переходу між цими станами.
Наприклад, у Solidus користувачі можуть оформляти замовлення. Користувач може бути в одному з кількох станів щодо замовлення, таких як "очікує", "обробляється" або "відправлено". Машина станів визначатиме ці стани та переходи між ними, такі як "оформити замовлення" (що переходить з "очікує" в "обробляється"), "відмінити замовлення" (що повертає з "обробляється" в "очікує"), та "відправити замовлення" (що переходить з "обробляється" в "відправлено").
Використання машини станів у веб-додатку дає кілька переваг. По-перше, це допомагає забезпечити стабільність і передбачуваність роботи додатку, оскільки поведінка системи чітко визначена та контролюється. По-друге, це полегшує розуміння додатку та відлагодження проблем, оскільки стан системи можна легко перевірити та зрозуміти.
По-третє, це може допомогти спростити кодову базу, оскільки складну логіку можна інкапсулювати в машині станів (State Machine).
Якщо я говорю про це, то тому, що документація Solidus має розділ, присвячений цьому, і я вважаю, що це досить рідко виділяється!
Тепер ми можемо спробувати подивитися, чи є приховані умови гонки (Race Conditions) у використанні промо-коду.
Цей розділ коду все ще належить Spree (попереднику Solidus), тому я одразу не зміг до нього дістатися, але в разі whitebox-аудиту іноді легше відслідкувати код через помилку на сайті.
У цьому випадку, при дворазовому застосуванні того самого промо-коду, сайт виводить помилку “The coupon code has already been applied to this order”
Тоді просто шукаємо цю помилку в усьому коді проекту і відслідковуємо її використання назад до методу, який перевіряє використання купона.
Досить важко вникнути в деталі та пояснити всі перевірки, але можна підсумувати, що купон асоціюється з конкретним замовленням, і як тільки ми намагаємось застосувати новий купон, код перевіряє, чи вже асоційований він із замовленням чи ні.
Отже, підсумовуючи, цей код не виглядав вразливим до умов гонки (Race Conditions).
Представлення всіх проведених тестів було б нудним, але з цих рядків ми розуміємо, що основні будівельні блоки Solidus досить надійні, і на стандартній установці, на жаль, мені не вдалося знайти багато чого цікавого.
Тож, можливо, цікавіше зосередитись на кастомних розробках, зокрема на розширеннях. У Solidus можна організувати розширення за трьома типами:
- Офіційні інтеграції: Перелічено на основному сайті, здебільшого це розширення для обробки платежів
- Розширення від спільноти: Перелічено в окремому репозиторії на GitHub, це різноманітні розширення, які більш-менш підтримуються
- Інші розширення: Розширення, знайдені в інших місцях, але немає гарантії, що вони працюватимуть або будуть підтримуватись
Майже всі офіційні розширення потребують інтеграції з третіми сторонами і тому робитимуть запити до сторонніх сервісів, чого я хотів уникнути тут.
Замість цього я звернувся до розширень від спільноти, щоб протестувати кілька розширень, для яких мені б хотілося мати рідну функціональність на сайті, наприклад, експорт рахунків у форматі PDF.
Для цього я знайшов плагін Solidus Print Invoice, який не підтримується вже 2 роки. Можна було б подумати, що це хороший знак з точки зору атакувальника, але насправді цей плагін не призначений для роботи з Solidus 4, тому першим кроком було зробити його сумісним, щоб його можна було встановити...
Як вказано в документації, цей плагін додає лише генерацію PDF на стороні адміністратора.
Довго не розповідаючи, цей плагін не дав мені нічого нового, і я витратив більше часу на його встановлення, ніж на розуміння того, що я не отримаю від нього жодних вразливостей.
Я ще не перевіряв це, але цікаво зазначити, що інші плагіни, такі як Solidus Friendly Promotions, згідно з документацією, заміняють основні функції Solidus, і тому вони природно більш схильні до виникнення вразливостей.
Висновок
Представлення всіх тестів, які можуть бути проведені, також займає занадто багато часу.
Аналіз коду справді займає багато часу, тому стверджувати, що я повністю проаналізував всю програму, було б неправдою. Але, після кількох днів роботи з Solidus, я думаю, що це дуже цікава програма з точки зору безпеки.
Звісно, я б хотів детально описати кілька вразливостей, але цей блог-пост намагається показати, що не завжди можна отримати результат.
Перекладено з: Solidus — Code Review