
Доказательство концепции эксплойта для CVE-2020-12124, нацеленного на маршрутизатор Wavlink AC1200, демонстрирующее неаутентифицированную инъекцию команд и переполнение буфера стека в CGI-интерфейсах.
изначально на https://www.klogixsecurity.com/scorpion-labs-blog/anatomy-of-an-iot-exploit-from-hands-on-to-rce
автор: David E. Baker, опубликовано 1 июня 2023 года
Данное исследование касается прошивки беспроводного гигабитного роутера Wavlink Wireless-AC1200 по состоянию на июнь 2020 года. Обсуждаемые здесь уязвимости могли быть или не быть исправлены производителем, но это пример исследования уязвимостей, где главная ценность — сам процесс, а не его результат. Автор провёл это исследование до публичного раскрытия уязвимостей, но после того, как они были независимо обнаружены другими исследователями и сообщены производителю.
Производитель предоставляет прошивки для своих продуктов в разделе поддержки на своём веб-сайте; это распространённый способ получения IoT-прошивок и полезная альтернатива извлечению их из памяти устройства. Прошивка не зашифрована, поэтому её можно легко извлечь с помощью binwalk. Динамический анализ проводился при наличии физического образца устройства, а статический анализ — через Ghidra.
Веб-интерфейс беспроводного гигабитного роутера Wavlink Wireless-AC1200 содержит несколько уязвимых конечных точек, которые позволяют неограниченно копировать предоставленные пользователем данные в стек приложения или даже напрямую в командную строку для выполнения произвольных команд.
Первоначальное сканирование устройства показало, что единственным открытым ресурсом была административная веб-консоль, доступная аутентифицированным пользователям в локальной сети через HTTP на TCP-порту 80. Устройство может предоставлять и другие услуги, но они не включены по умолчанию. Поэтому данное исследование сосредоточено только на веб-интерфейсе.
Результат nmap-сканирования образца устройства, показывающий только
прослушивание веб-интерфейса.
Обычные тесты — такие как типичные инъекции команд на диагностических
панелях устройств, позволяющие внедрить команду в параметры ping
или traceroute, — не дали немедленно интересных результатов,
что было разочаровывающе.
Доступные опции управления после аутентификации в
административной веб-панели. «USB Storage» виден как второй пункт.
Первый (в конечном счёте эксплуатируемый) интерфейс, который был исследован, находился на панели «USB storage» (второй пункт на скриншоте выше). Устройство имеет порт USB рядом с разъёмами 802.2 Ethernet, что предполагает возможность работы в качестве сетевого хранилища (NAS).
Фотография задней панели реального образца, показывающая
наличие USB.
Простой принцип исследования уязвимостей: чем больше компонентов взаимодействует с кодом и чем больше движущихся частей, тем вероятнее наличие эксплуатируемого кода. Наличие функций NAS многообещающе, поскольку указывает на код, который одновременно взаимодействует с программным уровнем устройства, аппаратным уровнем и подключённой периферией (самим USB-накопителем).
Интерфейс управления консолью USB Storage показан ниже.
Одно только поле «Workgroup» обнадёживает, так как предполагает,
что этот WiFi-роутер может даже пытаться взаимодействовать через
Server Message Block (SMB) — большая задача для IoT-роутера.
Я не могу сосчитать, сколько раз видел, как пользовательский ввод
напрямую передавался в командную строку в качестве аргумента
функции Unix smbpasswd.
Опции USB-накопителя, доступные аутентифицированным пользователям.
Первоначальные попытки изменить эти настройки не удались из-за того, что устройство не обнаружило USB-диск, как показано ниже.
Изменения конфигурации параметров USB Storage не будут
сохранены, пока в USB-порт устройства не будет вставлен
отформатированный соответствующим образом диск.
Однако, как только правильно отформатированный диск был вставлен, устройство позволило задать FTP-имя пользователя и пароль. Как и предполагалось, оно поместило этот пользовательский ввод в командную строку:
Инъекция команды в поле «password» даёт первый доступ к
оболочке непосредственно к операционной системе устройства.
Хотя это и интересно, но данная уязвимость не вызывает чрезмерного восторга: она требует не только аутентифицированного доступа к административному интерфейсу устройства, но и физического доступа к устройству для манипуляций с USB-диском. Описанный выше эксплойт позволяет исследователю взаимодействовать с отдельными компонентами операционной системы (и извлекать их для целей обратной разработки).
Устройство работало на базе Linux с BusyBox, веб-интерфейс
обеспечивался Lighttpd. Функциональность Common Gateway Interface (CGI)
предоставлялась отдельными бинарными файлами в /etc_ro/lighttpd/www/cgi-bin/,
причём веб-запросы к CGI URI запускали эти бинарники напрямую.
Быстрый взгляд на nas.cgi в Ghidra показывает инъекцию команды
на строке 38 ниже, которая отправляет предоставленный пользователем
пароль напрямую в функцию do_system (сама по себе это просто
обёртка вокруг стандартного системного вызова libc).
Пользовательский ввод помещается в командную строку как аргумент
скрипта chpasswd.sh на строке 38, что приводит к инъекции команды
и доступу к оболочке непосредственно к операционной системе устройства.
Просмотр каталога /cgi-bin/ сводит задачу поиска более
интересного эксплойта к перечислению доступных пользователю
CGI-интерфейсов, показанных ниже:
Полный список CGI-бинарников, доступных на устройстве,
полученный из оболочки, установленной с помощью эксплойта, описанного
в этом разделе. Пользовательский ввод помещается в командную строку как аргумент
скрипта chpasswd.sh на строке 38, что приводит к инъекции команды
и доступу к оболочке непосредственно к операционной системе устройства.
При первоначальном рассмотрении выделяются несколько моментов.
Первое важное замечание: CGI-бинарники часто вызывают функцию
check_valid_user. Этот метод проверяет, сохранён ли IP-адрес,
с которого поступает запрос, в определённом временном файле в
файловой системе. Минимальное тестирование показывает, что статус
аутентификации клиента не проверяется до вызова этого метода,
поэтому вся поверхность кода в каждом CGI-бинарнике до вызова
этой функции доступна без аутентификации.
Декомпиляция adm.cgi, показывающая метод check_valid_user.
Весь код до этого вызова выполняется до проверки статуса
аутентификации запрашивающей стороны.
Ещё одно интересное наблюдение — большое количество методов,
копирующих пользовательский ввод напрямую в стек. Например,
декомпиляция wireless.cgi показывает параметр NewName,
взятый из тела веб-запроса на строках 14 и 15, за которым следует
незащищённый strcpy этого пользовательского ввода в стек
на строке 34, показано ниже:
Строки 14 и 34 демонстрируют незащищённый strcpy параметра
NewName из тела запроса непосредственно в стек программы.
Декомпиляция adm.cgi, показывающая метод check_valid_user. Весь
код до этого вызова выполняется до проверки статуса аутентификации
клиента.
Само по себе копирование пользовательского ввода в стек указывает на эксплойт повреждения памяти, который можно проверить следующей командой:
curl –XPOST --data "page=SetName&NewName=\`python3 –c 'print(\\"A\\"\*(512)'\`" http://target-ip/cgi-bin/wireless.cgi
Хотя это многообещающе, ROP-атака здесь неоптимальна. Используя уже полученный доступ к оболочке операционной системы, следующая команда выводит «1», показывая, что рандомизация адресного пространства (ASLR) слабая, что даёт ROP-цепочке шанс 1 к 256 попасть в нужный гаджет:
# cat /proc/sys/kernel/randomize\_va\_space
1
Более того, команда ниже показывает, что little-endian бинарники скомпилированы с установленным байтом защиты стека, поэтому только последний гаджет в цепочке может оказаться в предопределённом месте внутри бинарника. Если присутствует процесс watchdog, перезапускающий веб-сервер после сбоя, то можно многократно применять return-oriented эксплойт и в конечном счёте ожидать успеха, но также возможно, что в коде есть более подходящие ошибки.
xxd /etc\_ro/lighttpd/www/cgi-bin/wireless.cgi | head -n 10
Продолжая просматривать CGI-функции, в конечном счёте доберёшься
до live_api.cgi. Этот бинарник не вызывает check_valid_user,
поэтому любые веб-запросы к URI /cgi-bin/live_api.cgi выполняют
это CGI-приложение без аутентификации. Строка 9 на рисунке 11
показывает, что переменная окружения QUERY_STRING (которая,
согласно спецификации Apache CGI, является частью URI запроса
сразу после вопросительного знака и, следовательно, предоставляется
пользователем) сохраняется в pcVar1 и на строке 19 рисунка 11
передаётся методу satellite_status.
Декомпиляция live_api.cgi, показывающая пользовательский ввод,
взятый из URI на строке 9 и передаваемый в satellite_status на строке 19.
Декомпиляция adm.cgi, показывающая метод check_valid_user. Весь
код до этого вызова выполняется до проверки статуса аутентификации
клиента.
Декомпиляция метода satellite_status, показанная на рисунке 12,
демонстрирует, что сама строка запроса (теперь param_1) разбирается
на параметры page, id и ip. Параметр ip копируется с помощью
функции sprintf в локальную переменную на строке 38 рисунка 12 и
на строке 39 передаётся функции do_system. Отсутствие вызова
check_user_auth указывает на то, что произвольный ввод клиента
без аутентификации в URI будет помещён непосредственно в командную
строку через параметр URI ip, что подтверждается ниже:
Декомпиляция функции satellite_status, показывающая, что
строка запроса (теперь param_1) разбирается на параметр ip на строках
22 и 23, а затем следует вызов do_system на строках 38 и 39.
Доказательство — в пудинге: показано использование эксплойта
для удалённого захвата управления устройством.
Поиск эксплойтов в только что появившемся на рынке IoT-устройстве может показаться лёгкой добычей, как упоминалось в начале этого поста, но ценность данного исследования заключалась в процессе, а не в результате.
Понимание способа обхода аутентификации и местоположения инъекции команд было бы маловероятным или даже невозможным без статического анализа прошивки. Если бы она не была доступна онлайн, для её получения потребовался бы динамический доступ к операционной системе устройства, на который были бы слабые надежды. Сам такой уровень доступа требовал как физического доступа к устройству, так и либо специального оборудования, либо обоснованного предположения о вероятных местах в коде, где могли быть допущены ошибки.
Мы надеемся, что вам понравилось это путешествие, и что вы вернётесь за новыми материалами. Удачного хакинга!
Автор:
David Baker, старший консультант по безопасности, отдел тестирования, K logix