FortiGate SSLVPN CVE-2024-21762 Анализ эксплуатации уязвимостей

  • Автор темы Автор темы ADMIN
  • Дата начала Дата начала

ADMIN

Administrator
ADMIN
Регистрация
18 Окт 1989
Сообщения
1,408
Реакции
93
Баллы
48
☑ ДЕПОЗИТ
0$
1.
В феврале компания FortiGate выпустила обновление версии, исправляющее множество уязвимостей среднего и высокого риска. дикая природа. В этой статье будет представлен авторский анализ процесса эксплуатации этой уязвимости для удаленного выполнения кода.


2.
В этой статье для анализа уязвимостей используется среда FGT_VM64-v7.4.2.F-build2571.

различий 2.1
Сравнивая бинарники исправленных версий (7.4.2 и 7.4.3), в результате анализа выяснилось, что код восстановления находится в функции sub_18F4980(7.4.2).



Анализируя эту функцию, нетрудно обнаружить, что логика этой функции заключается в чтении данных тела HTTP POST-запроса. При этом Transfer-Encodingпо заголовку запроса определяется, читать в формате чанка или на основе Content-Lengthчтения. По результатам сравнения графов потока управления имеются две модификации кода:

При анализе формата фрагмента вызовите ap_getlineдлину прочитанного фрагмента и проверьте ap_getline, больше ли возвращаемое значение 16. Если оно больше 16, это считается недопустимой длиной фрагмента.
При чтении трейлера чанка источником записанного \r\nсмещения является line_offисточник назначения, line_offзначение до восстановления — from , а возвращаемое значение после *(_QWORD *)(a1 + 744)восстановления — . line_offap_getline
Продолжая трассировку вперед, *(_QWORD *)(a1 + 744)значение, которое можно найти, представляет собой длину поля длины фрагмента первой проверки.



В то же время чтение кода может сказать, что когда значение поля длины чанка равно 0 после шестнадцатеричного декодирования, он войдет в логику чтения трейлера чанка.

2.2 Запуск
Проанализировав патч, можно сделать следующие выводы:

1. Если при анализе фрагмента шестнадцатеричное декодированное значение поля длины фрагмента равно 0, начните читать трейлер фрагмента.

2. После вызова ap_getline для чтения трейлера чанка он будет записан в буфер в соответствии с длиной поля длины чанка \r\n.

Таким образом, если в поле длины фрагмента передано много нулей, а длина нулей превышает 1/2 оставшейся длины буфера, будет инициирована запись за пределами буфера \r\n. Путем отладки мы можем узнать, что целевой буфер находится в стеке (функция sub_1A111E0), а адрес возврата хранится по смещению 0x2028. Если записано по смещению 0x202e \r\n, retпроизойдет сбой из-за неправильного адреса, когда функция вернется для выполнения инструкции для возобновления копирования.

Сбой PoC:

pkt = b"""\
GET / HTTP/1.1
Host: %s
Transfer-Encoding: chunked

%s\r\n%s\r\n\r\n""" % (hostname.encode(), b"0"*((0x202e//2)-2), b"a")

ssock = create_ssock(hostname, port)
ssock.send(pkt)
ssock.recv(4096)

Место крушения:



3.
Анализируя причину уязвимости, можно увидеть, что уязвимость может использоваться для записи \r\nдвух байтов за пределы стека, а диапазон выхода за пределы стека близок к 0x2000. Поскольку письменный контент очень ограничен, RCE не может быть достигнут путем прямого перехвата рипа. Поэтому вам нужно сосредоточиться на указателе памяти, сохраненном в стеке.

3.1 Неудачные
Проще всего подумать о том, чтобы перехватить rbp и перезаписать младший байт rbp, чтобы rbp просто указывал на контролируемую область памяти. Когда функция верхнего уровня возвращается для выполнения leave retинструкции, rip может быть полностью перехвачен. Однако при проверке выяснилось, что даже если rbp в стеке перезаписан, rsp и rip невозможно перехватить, и программа даже не вылетит. Продолжая трассировку резервной копии, мы находим sub_1A111E0родительскую функцию . Эта функция вызывается не для восстановления rsp sub_1A26040при возвращении , а напрямую , поэтому она не может достичь ожидаемого эффекта.leave retadd rsp, 0x18



3.2 Найдите
Как видно из предыдущего раздела, sub_1A26040функция сохраняет в стеке значения пяти регистров rbx и r12-r15 и восстанавливает эти регистры при возврате из функции. Продолжайте возврат, чтобы найти родительскую функцию sub_1A27650. Вы можете видеть, что в r13 сохраняются именно параметры a1.



a1Это указатель на структуру. При отладке вы также можете увидеть, что в стеке сохраняется r13адрес кучи.



Если память в красной области на рисунке перезаписана записью за пределами границ, то sub_1A26040регистр r13 восстанавливается при возвращении функции, и значение указателя может быть изменено a1. Если память в куче можно расположить так, что a1 укажет на заранее организованную область памяти, то вся структура a1 может быть перехвачена. В то же время, благодаря анализу логики кода sub_1A27650и sub_1A26040, существует большое количество динамических вызовов функций членов многоуровневой структуры a1, поэтому будет больше возможностей для перехвата a1.

3.3 Взлом
Согласно предположению, после перезаписи младшего байта указателя a1 \r\nон может указывать на заранее подготовленную память. как показано на картинке:



Для достижения такого эффекта необходимо соблюдение следующих условий:

Адрес структуры a1 выше адреса области распыления кучи, и разрыв между ними очень мал.
0x7fxxxxxxx0a0dДолжно указывать на кованую конструкцию.
При отладке можно обнаружить, что размер структуры a1 равен 0x730.Согласно правилам выравнивания jemalloc, будет выделен блок кучи размером 0x800. Блок кучи 0x800 обычно не используется во время обработки запроса, поэтому легко исчерпать блок кучи 0x800 в tcache и в то же время применить новые блоки 0x800, чтобы они могли войти в tcache после освобождения. Внедрение кучи также выбирает блоки кучи необычных размеров, чтобы вновь применяемые блоки кучи были непрерывными и близкими к вновь примененному 0x800; внедрение кучи выбирает использование более крупных блоков кучи, чтобы гарантировать, что их адреса выровнены с 0x800, чтобы можно было легко убедитесь, что младшие 12 бит каждого адреса кованой структуры равны 0xa0d; диапазон динамического распыления составляет не менее 0x10000, чтобы гарантировать, что он 0x7fxxxxxxx0a0dуказывает на область динамического распыления. Эффект после угона следующий:



3.4 Поиск доступных многоуровневых
С помощью вышеописанных операций можно осуществить захват структуры a1. Просматривая код sub_1A27650функции sub_1A26040sum, можно обнаружить множество динамических вызовов указателя второго уровня и указателя третьего уровня члена структуры a1, например:



*(_BYTE *)(a1+0x20*(N+6)+0x10)&6==0Когда (0 < N < 5) выполняется , он будет вызываться динамически *(__int64 (__fastcall **)(__int64))(*(_QWORD *)(*(_QWORD *)(a1 + 0x298)+0x70)+0xC0)(a1). Поэтому a1 + 0x298член необходимо превратить в многоуровневый указатель, который в конечном итоге указывает на функцию, которую мы хотим вызвать. Поскольку в целевом двоичном файле не включена защита PIE, в целевом двоичном файле можно найти квалифицированные многоуровневые указатели. Анализируя бинарный файл, мы можем обнаружить, что первое поле каждой записи в разделе Relocation relocation указывает на адрес таблицы GOT соответствующей функции.

Запись о переезде Rela

ПОЛУЧИЛ стол

Поэтому systemна примере функций можно найти квалифицированные многоуровневые указатели.



Во время распыления кучи изменение значения по смещению структуры 0x298 0x4368d0может использоваться для вызова системной функции. Эффект следующий:



Как показано на рисунке, параметр динамического вызова равен a1, а память, на которую указывает, является управляемой. На этом этапе вы обычно можете использовать системную функцию для выполнения любой команды. Однако в FortiGate /bin/shфайл не имеет возможности выполнять команды, поэтому использование системной функции для выполнения команд не может быть успешно выполнено.

3.5 Взлом
Поскольку системная функция не может выполнять команды, мы можем найти только другие способы выполнения RCE. Существующее условие состоит в том, что любая функция таблицы GOT может быть вызвана, а память, на которую указывает первый параметр функции, является управляемой. Следовательно, если в таблице GOT есть функция, которая будет вызывать член параметра, то это шанс добиться перехвата RIP. Легко вспомнить функции, которые часто использовались в предыдущих эксплойтах FortiGate SSL_do_handshake.

int SSL_do_handshake(SSL *s)
{
// ...

s->method->ssl_renegotiate_check(s, 0);

if (SSL_in_init(s) || SSL_in_before(s)) {
if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) {
struct ssl_async_args args;

args.s = s;

ret = ssl_start_async_job(s, &args, ssl_do_handshake_intern);
} else {
ret = s->handshake_func(s);
}
}
return ret;
}

Вам нужно только построить структуру SSL так, чтобы условия были соблюдены, и s->handshake_func(s)был сделан последний вызов для реализации перехвата рипа и перехвата рипа на 0xdeadbeef, как показано на рисунке:



3.6
Основная программа FortiGate представляет собой бинарный файл All-in-One размером более 70 МБ.Есть большое количество гаджетов, которые можно использовать.Реализовать RCE с помощью ROP несложно, поэтому не буду вдаваться в подробности.

4. Используйте
Хотя веб-режим по умолчанию отключен в SSL VPN версии 7.4.2, а доступ через браузер возвращает 403, эту уязвимость все равно можно использовать в конфигурации по умолчанию.

cve-2024-21762.mp4 1920x1080

5.
Эта уязвимость похожа на уязвимость переполнения кучи, вызванную XOR в прошлом году CVE-2023-27997. Обе они кажутся бесполезными уязвимостями переполнения. Процесс эксплуатации более сложен и больше похож на вопрос CTF. Однако по сравнению с традиционными проблемами CTF, которые атакуют менеджеры кучи, реальные уязвимости требуют использования большего количества контекстных структур и логики кода.
 
Независимый арбитраж: В случае возникновения споров или несоответствия товара описанию, сервис выступает в качестве арбитра, принимая объективное решение на основе заранее оговоренных условий.
Назад
Сверху