Обсудить проект

Business Logic Flaws: баги, которые сканер никогда не найдёт

13.07.2026

«Твой Burp Scanner нашёл 0 уязвимостей? Поздравляю, приложение технически безопасно и полностью сломано одновременно.» 💀

Бизнес-логика — это единственный класс уязвимостей, где код работает абсолютно правильно, но правила, которые он реализует, дырявые как решето. Sqlmap не найдёт баг в checkout-флоу, который пропускает проверку оплаты. Nuclei не поймает race condition в купонах. Это работа только для человека с мозгами и пониманием контекста.

🧠 Почему сканеры здесь бессильны

Сканеры ищут сигнатуры — известные паттерны инъекций, XSS-маркеры, дефолтные креды. Бизнес-логика не оставляет сигнатур, потому что запрос технически валиден.

Сканер видит:
POST /api/checkout {"item_id": 42, "coupon": "SAVE10"}
→ 200 OK, валидный JSON, никаких инъекций
→ "Всё чисто!" ✅

Человек видит:
→ А что если отправить купон 100 раз параллельно?
→ А что если пропустить шаг оплаты и перейти прямо на confirm?
→ А что если id товара отрицательный?

🔥 Главный принцип охоты за логическими багами: «Am I allowed to do this?» — спрашивай себя это на КАЖДОМ шаге. Не ищи баг в коде, ищи баг в предположениях разработчика.

📋 OWASP Business Logic Abuse Top 10 (2025) — новый стандарт

OWASP выкатил отдельный список именно для бизнес-логики. Это не замена основному Top 10, это дополнение, потому что логические баги — отдельная категория боли.

# Класс Суть
BLA1 Action Limit Overrun Race condition обходит одноразовые операции (купоны, рефанды)
BLA2 Workflow Order Bypass Финальный шаг флоу выполняется до предыдущих
BLA3 Object State Manipulation Незащищённые поля объекта переопределяют роли/права
BLA4 Malicious Logic Loop Отсутствие лимитов на циклы/рекурсию → DoS
BLA5 Artifact Lifetime Exploitation Токены/сессии живут дольше положенного
BLA6 Missing Transition Validation Пропуск проверок в многошаговом workflow
BLA7 Resource Quota Violation Нет rate-limit на дорогие операции
BLA8 Internal State Disclosure Разные ответы для valid/invalid раскрывают внутреннюю логику
BLA9 Broken Access Control Спуфинг роли через логические дыры
BLA10 Shadow Function Abuse Незащищённые internal API/test-утилиты в проде

💰 BLA1: Race Condition — многократное использование одноразового

Классика бизнес-логики. Купон на «одно использование» на самом деле можно применить N раз, если отправить N параллельных запросов быстрее, чем сервер успеет обновить статус.

# Атака Race Condition через параллельные запросы
for i in {1..20}; do
  curl -X POST https://target.com/api/apply-coupon \
    -H "Authorization: Bearer $TOKEN" \
    -d '{"coupon":"SAVE50"}' &
done
wait

# Или через Burp Suite — Turbo Intruder с race condition template
# https://github.com/PortSwigger/turbo-intruder

💡 Лайфхак: ищи любую операцию с формулировкой «только один раз» — reset пароля, применение купона, голосование, лайк. Везде, где есть проверка if not used: use(), есть TOCTOU-окно (time-of-check to time-of-use).

Реальный пример: известны случаи, когда исследователи получали скидку 90%+ на товар, отправляя купон параллельно 50 раз — сервер применял его N раз до того, как флаг «использован» успевал обновиться.

🔀 BLA2: Пропуск шагов в workflow

Многошаговые процессы (checkout, KYC, onboarding) часто проверяют состояние только на клиенте, а не на сервере. Если можно вызвать шаг 5, не проходя шаги 1–4 — добро пожаловать в баг.

# Нормальный флоу checkout:
POST /api/cart/add → POST /api/cart/verify → POST /api/payment → POST /api/confirm

# Атака — прыгаем прямо к confirm, минуя оплату
POST /api/confirm
{"order_id": 12345}

# Если сервер не проверяет, был ли реально совершён платёж — 
# товар твой бесплатно 🎯
# Тестирование пропуска шагов через Burp Repeater
# 1. Пройди нормальный флоу, запиши все запросы
# 2. Начни новую сессию
# 3. Вызывай финальные эндпоинты, пропуская промежуточные
# 4. Смотри, проверяет ли сервер prerequisite state

🎭 BLA3: Object State Manipulation — Mass Assignment 2.0

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

// Обычный запрос обновления профиля
POST /api/user/update
{"name": "John", "email": "john@test.com"}

// Атака — добавляем поле, которого "не должно быть" в форме
POST /api/user/update
{"name": "John", "email": "john@test.com", "role": "admin", "is_verified": true, "balance": 999999}

🧠 Где искать: любой PUT/PATCH эндпоинт с ORM (Django, Rails, Sequelize) без явного whitelist полей. Проверь через Burp — сохрани обычный запрос, добавь по одному потенциально «скрытому» полю (role, admin, verified, balance, discount) и смотри на реакцию сервера.

⏳ BLA5: Artifact Lifetime — токены живут вечно

Password reset токен должен умирать после первого использования или через 15 минут. На практике — сплошь и рядом живёт часами, а то и не инвалидируется вообще.

# Тестируем lifetime токена сброса пароля
1. Запроси сброс пароля → получи токен в письме
2. Используй токен → смени пароль
3. Попробуй использовать ТОТ ЖЕ токен снова
   curl -X POST https://target.com/reset-password \
     -d '{"token":"OLD_TOKEN", "new_password":"hacked123"}'
4. Если сработало — токен не инвалидируется после использования 💀

🔓 BLA9: Broken Access Control через логику, не через код

Отличается от классического IDOR тем, что здесь все проверки прав технически на месте, но их порядок или условие сформулированы неправильно.

Пример:
→ API проверяет "является ли пользователь владельцем заказа" 
→ НО не проверяет "является ли заказ активным/принадлежит текущей организации"
→ Результат: можно менять чужие заказы в других тенантах (multi-tenancy leak)
# Найден в реальном пентесте: смена статуса заказа другой компании
PATCH /api/orgs/456/orders/789/status
Authorization: Bearer <token_org_123>
{"status": "cancelled"}

# Сервер проверяет только "существует ли заказ 789"
# Не проверяет "принадлежит ли org 456 текущему пользователю из org 123"

👻 BLA10: Shadow Function Abuse — забытые внутренние функции

Разработчики оставляют «временные» эндпоинты для тестирования, debug-инструменты, internal API — и забывают их удалить перед продом.

# Поиск shadow-функций через JS файлы (см. статью про разведку)
cat wayback.txt | grep -E "internal|debug|test|admin_only|_temp"

# Типичные находки:
/api/internal/reset-all-users
/api/debug/impersonate?user_id=1
/api/test/create-admin
/api/v1/_deprecated/bypass-payment

💣 Лайфхак: ищи различия между тем, что видно во фронтенде, и тем, что доступно через API. Frontend роутинг ≠ backend авторизация. Если фронт скрывает кнопку — это не значит, что эндпоинт защищён.

🛠️ Методология охоты за логическими багами

Три правила от практикующих пентестеров:

  1. Изучи бизнес-домен глубоко — как пользователи реально взаимодействуют, какие права нужны для каждого действия
  2. Постоянно спрашивай «а что если…?» — не ставь себе искусственных лимитов при тестировании
  3. Действуй, будто препятствий не существует — ошибка 403 это не стена, это флаг, что механизм защиты можно обойти

Практический чеклист:

□ Многошаговые процессы: пробуй пропускать шаги, менять порядок
□ Одноразовые операции: атакуй параллельными запросами (race condition)
□ Формы с "скрытыми" полями: добавляй role/admin/balance/discount
□ Токены/сессии: проверяй lifetime, инвалидацию после использования
□ Multi-tenancy: проверяй изоляцию между организациями/аккаунтами
□ Отрицательные/нулевые значения: цена -1, количество 0, скидка 999%
□ Параллельные вызовы одной операции: 2 раза оплатить один заказ
□ JS-файлы и wayback: ищи shadow/internal/debug эндпоинты
# Быстрый тест на отрицательные значения в ценообразовании
curl -X POST https://target.com/api/cart/add \
  -d '{"item_id": 1, "quantity": -5, "price": -100}'
# Если сервер вычтет отрицательное количество из общей суммы — 
# ты можешь получить отрицательный total = сервер тебе должен денег 🎯

🏆 Как защититься (если ты по другую сторону)

  • Валидируй всё на сервере, никогда не доверяй client-side проверкам состояния
  • Явно документируй все предположения о поведении пользователя в дизайн-документах — то, что не задокументировано, будет сломано
  • Whitelist полей в API, никогда не bind напрямую JSON к внутренним моделям
  • Атомарные операции с блокировками для всего, что должно случиться «только один раз»
  • Регулярный manual pentest — automated scanning тут просто не работает

Итог: Business logic flaws — это баги в предположениях, а не в синтаксисе. Пока разработчики думают «пользователь не будет делать так» — ты делаешь именно так. Сканер ищет паттерны, ты ищешь дыры в здравом смысле дизайнера системы. 😈