• Курсы Академии Кодебай, стартующие в мае - июне, от команды The Codeby

    1. Цифровая криминалистика и реагирование на инциденты
    2. ОС Linux (DFIR) Старт: 16 мая
    3. Анализ фишинговых атак Старт: 16 мая Устройства для тестирования на проникновение Старт: 16 мая

    Скидки до 10%

    Полный список ближайших курсов ...

Гостевая статья Эксплуатация TP-LINK ARCHER A7 на PWN2OWN ТОКИО

Во время соревнования Педро Рибейро (@ pedrib1337) и Радек Доманский (@RabbitPro) использовали уязвимость внедрения команд как часть цепочки эксплойтов, которую они использовали для выполнения кода на беспроводном маршрутизаторе TP-Link Archer A7, который заработал им 5000 долларов. Ошибка, использованная в этом эксплойте, была недавно исправлена, и Педро и Радек любезно собрали этот пост в блоге, описывающий уязвимость внедрения команд.

В этой статье описывается уязвимость внедрения команд, которую мы обнаружили и представили на конкурсе Pwn2Own Tokyo в ноябре 2019 года.
Уязвимость существует в демоне tdpServer ( /usr/bin/tdpServer), работающем на маршрутизаторе TP-Link Archer A7 (AC1750), версия оборудования 5, архитектура MIPS, версия прошивки 190726. Эта уязвимость может быть использована только злоумышленником на стороне локальной сети маршрутизатора , но аутентификация не нужна. После эксплуатации злоумышленник может выполнить любую команду от имени пользователя root, включая загрузку и выполнение двоичного файла с другого хоста. Эта уязвимость была назначена CVE-2020-10882 и была устранена TP-Link с версией прошивки .
Все смещения функций и фрагменты кода в этой статье взяты из /usr/bin/tdpServerверсии прошивки 190726.

Фон на tdpServer

В tdpServerдемон прослушивает порт UDP 20002 на интерфейсе 0.0.0.0. На данный момент общая функциональность демона не до конца понята авторами, так как это было ненужным для эксплуатации. Тем не менее, демон, по-видимому, является мостом между мобильным приложением TP-Link и маршрутизатором, позволяющим установить какой-либо канал управления из мобильного приложения.
Демон связывается с мобильным приложением посредством использования пакетов UDP с зашифрованной полезной нагрузкой. Мы изменили формат пакета, и он показан ниже:

Picture1 (3).png

Рисунок 1 - Обратный формат пакета tdpServer

Тип пакета определяет, какая служба в демоне будет вызвана. Тип 1 приведет к тому, что демон вызовет службу tdpd , которая просто ответит пакетом с определенным хеш-значением TETHER_KEY. Поскольку это не относится к уязвимости, мы не исследовали ее подробно.

Другой возможный тип - 0xf0, который вызывает службу onemesh . В этом сервисе и заключается уязвимость.
, по-видимому, является запатентованной сеточной технологией, которая была представлена TP-Link в последних версиях прошивки для ряда их маршрутизаторов.

Другие поля в пакете относительно хорошо объяснены в комментариях выше.

Понимание уязвимости

После запуска устройства первая соответствующая соответствующая функция - tdpd_pkt_handler_loop () (смещение 0x40d164), которая открывает сокет UDP, прослушивающий порт 20002. После получения пакета эта функция передает пакет в tpdp_pkt_parser () (0x40cfe0), какой фрагмент показан ниже:

Picture2 (3).png


Рисунок 2 - tdpd_pkt_parser () # 1

В этом первом фрагменте мы видим, что синтаксический анализатор сначала проверяет, равен ли размер пакета, сообщаемый сокетом UDP, по крайней мере 0x10, что является размером заголовка. Затем он вызывает tdpd_get_pkt_len () (0x40d620), который возвращает длину пакета, как объявлено в заголовке пакета ( поле len ). Эта функция возвращает -1, если длина пакета превышает 0x410.

Окончательная проверка будет выполняться с помощью tdpd_pkt_sanity_checks () (0x40c9d0), который не будет показан для краткости, но выполняет две проверки. Во-первых, он проверяет, равна ли версия пакета ( поле версии , первый байт в пакете) 1. Затем он вычисляет контрольную сумму пакета, используя пользовательскую функцию контрольной суммы: tpdp_pkt_calc_checksum () (0x4037f0).

Чтобы лучше понять, что происходит, используйте функцию calc_checksum () , которая является частью кода эксплойта lao_bomb . Это показано вместо tpdp_pkt_calc_checksum (), поскольку это легче понять.

Picture3 (3).png

Рисунок 3 - calc_checksum () из кода эксплойта lao_bomb

Расчет контрольной суммы довольно прост. Он начинается с установки магической переменной 0x5a6b7c8dв поле контрольной суммы пакета, а затем использует reference_tbl , таблицу с 1024 байтами, для вычисления контрольной суммы по всему пакету, включая заголовок.

Как только контрольная сумма проверена и все правильно, tdpd_pkt_sanity_checks () возвращает 0, и мы затем вводим следующую часть tdpd_pkt_parser () :

Picture4 (3).png

Рисунок 4 - tdpd_pkt_parser () # 2

Здесь проверяется второй байт пакета, поле типа , чтобы определить , равен ли он 0 (tdpd) или 0xf0 (onemesh). В последней ветви он также проверяет, установлена ли глобальная переменная onemesh_flag в 1, что по умолчанию. Это та ветка, которой мы хотим следовать. Затем мы вводим onemesh_main () (0x40cd78).
onemesh_main () не будет здесь показан для краткости, но его работа состоит в том, чтобы вызывать другую функцию, основанную на поле кода операции пакета . Чтобы достичь нашей уязвимой функции, поле opcode должно быть установлено в 6, а поле flags должно быть установлено в 1. В этом случае будет вызываться onemesh_slave_key_offer () (0x414d14).

Это наша уязвимая функция, и поскольку она очень длинная, будут показаны только соответствующие части.

Picture5 (2).png

Рисунок 5 - onemesh_slave_key_offer () # 1

В этом первом фрагменте onemesh_slave_key_offer () мы видим, что он передает полезную нагрузку пакета в tpapp_aes_decrypt () (0x40b190). Эта функция также не будет показана для краткости, но легко понять, что она делает из имени и его аргументов: она расшифровывает полезную нагрузку пакета, используя алгоритм AES и статический ключ «TPONEMESH_Kf! Xn? Gj6pMAt-wBNV_TDP» .

Это шифрование было сложно воспроизвести в эксплойте lao_bomb. Мы объясним это подробно в следующем разделе.

Сейчас мы предположим, что tpapp_aes_decrypt смог успешно расшифровать пакет, поэтому мы переходим к следующему соответствующему фрагменту в onemesh_slave_key_offer () :

Picture6 (2).png

Рисунок 6 - onemesh_slave_key_offer () # 2

В этом фрагменте мы видим, что некоторые другие вызываемые функции (в основном, установка объекта onemesh) сопровождаются началом анализа фактической полезной нагрузки пакета.

Ожидаемая полезная нагрузка - это объект JSON, такой как показан ниже:

Picture7 (1).png

Рисунок 7 - Пример полезной нагрузки JSON для onemesh_slave_key_offer ()

На рисунке 6 мы видим, как код сначала выбирает ключ JSON метода и его значение, а затем запускает объект JSON проанализированных данных .

Следующий фрагмент показывает, что каждый ключ объекта данных обрабатывается по порядку. Если один из необходимых ключей не существует, функция просто завершается:

Picture8 (1).png

Рисунок 8 - onemesh_slave_key_offer () # 3

Как видно выше, значение каждого ключа JSON анализируется, а затем копируется в переменную стека ( slaveMac , slaveIp и т. Д.).

После анализа объекта JSON функция начинает подготовку ответа, вызывая create_csjon_obj () (0x405fe8).

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

Picture9 (1).png


Рисунок 9 - onemesh_slave_key_offer () # 4

И вот наша уязвимость во всей красе. Возвращаясь к рисунку 8 выше, вы можете видеть, что значение ключа JSON slave_mac было скопировано в переменную стека slaveMac . На рисунке 9 slaveMac копируется sprintf в переменную systemCmd, которая затем передается в system () .
эксплуатация

Достижение уязвимой функции


Первое, что нужно определить, - как достичь этой команды. После проб и ошибок мы обнаружили, что отправка структуры JSON, показанной на рисунке 7 выше, всегда попадает в уязвимый путь кода. В частности, метод должен быть slave_key_offer , а want_to_join должен быть false . Другие значения могут быть выбраны произвольно, хотя некоторые специальные символы в полях, отличных от slave_mac, могут привести к преждевременному завершению работы уязвимой функции и не обрабатывать наше внедрение.
Что касается заголовка пакета, как описано выше, мы должны установить тип 0xf0, код операции 6 и флаги 1, а также получить правильное поле контрольной суммы .

Шифрование пакета

Как объяснялось в предыдущем разделе, пакет шифруется с помощью AES с фиксированным ключом TPONEMESH_Kf!xn?gj6pMAt-wBNV_TDP. Однако в этой головоломке есть еще несколько недостающих частей. Шифр используется в режиме CBC, а IV является фиксированным значением 1234567890abcdef1234567890abcdef. Кроме того, несмотря на наличие 256-битного ключа и IV, в действительности используется алгоритм AES-CBC со 128-битным ключом, поэтому половина ключа и IV не используются.

Достижение выполнения кода

Теперь мы знаем, как найти уязвимый путь кода, можем ли мы просто отправить пакет с командой и получить выполнение кода? Есть две проблемы, которые нужно преодолеть:
im. Strncpy() копирует только 0x11 байт из slave_mac_info ключа в slaveMac переменной, и что включает в себя завершающий нулевой байт.
II. Нам нужно выполнить некоторое экранирование, поскольку значение в slaveMac будет заключено в одинарные и двойные кавычки.
С учетом этих двух ограничений фактическое доступное пространство довольно ограничено.
Чтобы избежать аргументов и выполнить полезную нагрузку, мы должны добавить следующие символы:

';<PAYLOAD>'

Мы только что потеряли 3 символа, и у нас осталось всего 13 байтов для построения нашей полезной нагрузки. С 13 байтами (символами) практически невозможно выполнить что-либо значимое.
Кроме того, во время тестирования мы обнаружили, что ограничение на самом деле составляет 12 байтов. Мы не до конца понимали, почему, но, похоже, это связано с побегом.
Наше решение состояло в том, чтобы вызвать ошибку многократно, создавая требуемый командный файл на целевом символе по одному за раз. Затем мы запускаем ошибку в последний раз, чтобы выполнить командный файл как скрипт оболочки. Тем не менее, эта техника намного сложнее, чем кажется на первый взгляд.
Рассмотрим, например, что для добавления символа «a» в файл с именем «z» мы можем просто сделать это:

printf a>>z

Обратите внимание, что даже в этом простом случае требуется 11 байтов.
Если мы хотим написать цифру, методика, показанная выше, не работает. Это связано с тем, что оболочка интерпретирует цифру как дескриптор файла. Точно так же специальные символы, такие как «.» или ';' которые интерпретируются оболочкой, не могут быть записаны в файл с помощью метода, описанного выше. Для обработки этих случаев нам нужно сделать следующее:

printf '1'>x

Если вы заметили, это фактически не добавляет символ к существующему файлу, а вместо этого создает новый файл с именем «x» (перезаписывает любой существующий файл с таким именем), содержащий только символ «1». Поскольку эта полезная нагрузка уже содержит 12 символов, нет никакого способа добавить дополнительное «>», которое позволило бы нам добавить 1к командному файлу, который мы создаем.
Тем не менее, есть решение. Каждый раз, когда нам нужно выдать цифру или специальный символ, мы сначала записываем символ в новый файл, а затем используем cat, чтобы добавить содержимое этого нового файла в создаваемый командный файл:

cat x*>>z*

Вы можете задаться вопросом, зачем нам нужен '*' в конце каждого имени файла. Это потому, что, несмотря на то, что мы всегда экранируем отправляемую команду, последние несколько байтов сценария lua, который должен был быть выполнен, заканчиваются в имени файла. Это означает, что когда мы пытаемся создать файл с именем «z», в действительности он будет называться «z»}) '. Добавление полного имени файла в нашу команду будет занимать слишком много байтов. К счастью для нас, оболочка выполняет автозаполнение с помощью специального символа '*'.
Проницательные читатели заметят, что мы не изменили / tmp , так как это часто требуется во встроенных устройствах, так как корень файловой системы обычно недоступен для записи. Опять нам повезло. Корневая файловая система монтируется для чтения-записи, что является серьезной ошибкой безопасности TP-Link. Если бы он был смонтирован только для чтения, что является нормальным для большинства встроенных устройств, использующих файловую систему SquashFS, эта конкретная атака была бы невозможна, поскольку при добавлении cd tmpпотребовалось бы слишком много из 12 доступных символов.
И с этим у нас есть все инструменты, необходимые для выполнения произвольных команд. Мы посылаем команду побайтно, добавляя их в командный файл 'z', а затем отправляем полезную нагрузку:

sh z

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

Истчоник:
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!