[Перевод] Взлом мобильного WiFi-роутера

У меня имеется Alcatel MW41 — мобильный WiFi-роутер. К его функционированию у меня претензий нет, но то, как он устроен, вызывает некоторые вопросы. Возникает такое ощущение, что на нём работает некое ПО (точнее — веб-сервер, предоставляющий интерфейс для выполнения настроек устройства). Это предположение заставило меня задаться следующими вопросами:
  1. Работает ли на устройстве Linux?
  2. Если Linux на роутере работает, то можно ли получить на нём root-права?

qfr_jqpatzu-xe0ouyulbvlb8rg.jpeg
Некоторые изыскания привели меня к положительному ответу на первый вопрос. На устройстве, и правда, работает Linux. А ещё я узнал, что есть специальная программа, способная дать root-доступ к мобильному роутеру. Это — TCL-SWITCH-TOOL. Единственное упоминание этой программы я нашёл на одном русском форуме. О том, откуда взялась эта программа, не сообщается. Программа выглядит сыроватой, но — лишь немного. Вышеприведённая ссылка ведёт на страницу, посвящённую устройству, похожему на моё — Alcatel MW40V, но программа подошла и для моего Alcatel MW41, и, похоже, подойдёт для других подобных устройств. Работа программы, видимо, основана на том, что роутер, при подключении его к компьютеру, выглядит как внешний жёсткий диск. TCL-SWITCH-TOOL что-то делает с устройством, оно переходит в режим отладки, что даёт нам доступ к командной оболочке с root-правами.

Программа предназначена исключительно для Windows. Но она, скорей всего, должна заработать и под Wine. Я загрузил программу, запустил её, нажал кнопку SWITCH : 0x16 0xF9(debug) для переключения устройства в режим отладки и…

0d67b569130e6b5bc1e90fa0014fe2e4.png

50

50? Странно… Закрытие этого окна приводит к появлению ещё одной ошибки.

b304608e08fae24bb6604eb4b4c6050d.png

Программа не работает

В терминале, который я использовал для запуска программы, я увидел предупреждение следующего содержания:

0009:fixme:ntdll:server_ioctl_file Unsupported ioctl 4d014 (device=4 access=3 func=405 method=0)

Похоже, что тут нам сообщают о том, что программа использует некую возможность, которую не полностью поддерживает Wine.

Тут я мог бы отыскать Windows-компьютер и воспользоваться им. Но мы не ищем лёгких путей! Можно ли узнать о том, как именно работает эта программа, и воспроизвести это самостоятельно?

Как оно, вообще, работает?


Я решил исследовать программу с помощью Ghidra, инструмента для реверс-инжиниринга ПО. В ходе поиска по тексту предыдущего сообщения об ошибке, switch device error, я обнаружил несколько ссылок на соответствующую строку. Все они выглядели как составные части сравнительно сложных функций. Конечно, и этот подход позволил бы разобраться с тем, как именно работает программа. Но, может, есть более лёгкий способ это выяснить?
c2ed5c8f69b27654e2aa6b4c9a6c38c8.png

В списке XREF видно, что ссылки на строку есть в четырёх различных функциях

Учитывая то, что программа запрашивает букву диска, соответствующую роутеру, кажется, что разумным будет предположить то, что она каким-то образом отправляет диску некие специальные команды, используя существующее соединение. То есть — не пользуется неким экзотическим USB-протоколом. Такое ощущение, что это подтверждает присутствие в коде такой строки: \\.\PHYSICALDRIVE%c. Это, возможно, указывает на использование некоего механизма для прямого доступа к диску.

42b31920f4b9b28ba602782d2e102676.png

Таинственная строка \\.\PHYSICALDRIVE%c

Представим, что программа каким-то образом открывает диск и затем, путём выполнения неких таинственных действий, переключает роутер в отладочный режим. Сложность TCL-SWITCH-TOOL при этом не важна, так как, в итоге, для выполнения обеих этих операций нужно воспользоваться Windows API, которое, по отношению к программе, является внешним ресурсом.

Мониторить вызовы API можно, воспользовавшись отладочными возможностями Wine для включения отладочного канала relay. Эта система делает запись в журнале каждый раз, когда TCL-SWITCH-TOOL обращается к внешней библиотеке (в Windows такие библиотеки называют DLL). Я надеялся, что можно будет обнаружить вызов некоей DLL Windows, ответственной за открытие диска, и передачу этой DLL соответствующих команд. Это смогло бы прояснить ситуацию с тем, как именно работает программа.

Это очень похоже на то, как в Linux функционирует strace. Но тут весь код работает в пользовательском режиме, а мы просто наблюдаем за пересечением границ между двумя разными DLL. А strace, с другой стороны, отслеживает системные вызовы, когда запрашивается использование неких возможностей ядра. Более близкий аналог этого из мира Linux называется ltrace.

Итак, я выполнил следующую команду:

WINEDEBUG=relay wine TCL-SWITCH-TOOL.exe &> tcllog.log

Далее, я ввёл имя диска и нажал на кнопку для перевода роутера в отладочный режим. В результате в моём распоряжении оказался довольно-таки длинный файл журнала (а именно — его размеры составляли 150 Мб). Я поискал в нём по ключевому слову PHYSICALDRIVE, известному мне из предыдущих экспериментов, и… ничего. Так как этот подход не сработал, я решил поискать по имени диска, которое ввёл (F), полагая, что программа, вероятно, должна как-то передать имя диска Windows.
7112a3f7f70dae329696379baa48d41a.png

Вызовы разных функций, записанные в журнал

А вот это, похоже, сработало! В журнале каждая строка, соответствует либо вызову функции (это — строки с меткой Call), либо — возврату из неё (строки с меткой Ret). В верхней части предыдущего рисунка можно видеть, как TCL-SWITCH-TOOL вызывает функцию CreateFileA, являющуюся частью KERNEL32, передавая \\.\\F: (в файле журнала присутствует удвоение символов обратной косой черты, что является результатом экранирования символов внутри двойных кавычек). Затем Wine-реализация KERNEL32 сама вызывает некоторые функции, после чего решает, что пришло время вернуть некое значение (строка 0009:Ret KERNEL32.CreateFileA() ближе к нижней части вышеприведённого фрагмента журнала). После этого TCL-SWITCH-TOOL выполняет ещё один вызов. На этот раз вызывается функция DeviceIoControl из KERNEL32. Эта функция похожа на ту, что мы ищем. Ей передаётся единственный параметр — 4d014. Это значение соответствует сообщению об ошибке, которое мы постоянно получали от Wine в самом начале. Я, правда, тогда не разобрался с тем, что произошло со строкой \\.\PHYSICALDRIVE%c. А позже оказалось, что это — один из способов обращения к дискам в Windows, когда используется индекс диска, а не его имя. Вероятно, эта строка использовалась в каком-то другом компоненте программы.

А что такое DeviceIoControl? Если заглянуть в официальную документацию Microsoft, то получается, что это — довольно-таки универсальная функция, которая выполняет некие действия, опираясь на параметр dwIoControlCode (в целом, её можно назвать Windows-версией ioctl). В нашем случае значением dwIoControlCode является 0x4d014, что соответствует IOCTL_SCSI_PASS_THROUGH_DIRECT, а это то, что, в соответствии с документацией, «позволяет приложению отправлять почти любые SCSI-команды целевому устройству».

Поиск команды


В этот момент у меня появилось такое ощущение, что суть работы TCL-SWITCH-TOOL составляет именно обращение к этой функции. А какую именно SCSI-команду программа посылает устройству? Я снова воспользовался инструментом Ghidra для того чтобы узнать о том, откуда вызывается DeviceIoControl.
ae930769b8890859dfa6052e20140eb9.png

Ссылка на DeviceIoControl из KERNEL32.dll

В правой нижней части предыдущего рисунка есть строка, начинающаяся с XREF. Она сообщает нам о том, что во всей программе есть лишь одна ссылка на эту функцию (то есть — нам сообщают о том, что функция в программе вызывается лишь один раз). Двойной щелчок по этой строке переносит нас в код, который выполняет вызов DeviceIoControl.

3c9211b15823ff1a6222f7821c743def.png

Единственное место в программе, где используется DeviceIoControl

Теперь можно пройтись по сборке в Ghidra и узнать о том, как конструируются различные аргументы для DeviceIoControl. Но то же самое можно сделать и проще. Мы знаем о том, что вызов функции находится по адресу 0x4031d4. Поэтому можно воспользоваться отладчиком Wine для того чтобы поставить на этот адрес точку останова. Потом можно запустить программу, она остановится там, где нам нужно, и мы сможем вывести аргументы функции. Обычно адреса функций рандомизируются из-за использования ASLR. Но GDB автоматически отключает ASLR. Поэтому об этом мы можем не беспокоиться.

Итак, я выполнил команду winedbg --gdb TCL-SWITCH-TOOL.exe и воспользовался командой b *0x4031d4 для установки точки останова. После этого я снова попробовал воспользоваться программой.

c2786c6a99342e0bdf17c2ebccc995a8.png

Установка (и срабатывание!) точки останова

Точка останова сработала! Как теперь разобраться с аргументами функции?

В системах Windows x86 используется соглашение о вызовах функций cdecl (то есть — набор правил, которые, кроме прочего, указывают на то, куда попадают аргументы функций и возвращаемые ими значения). А это значит, что аргументы должны были быть помещены в стек. GDB позволяет взглянуть на текущее содержимое стека.

026e3f4182e3aa240c485da882e4dc71.png

Содержимое стека

Команда, которую мы тут используем, x/10x $sp, предлагает GDB вывести 10 шестнадцатеричных слов, начиная с адреса, хранящегося в регистре sp (stack pointer, указатель стека). Этот подход работает благодаря тому, что в x86-системах стек растёт вниз. Подробности об этом смотрите здесь.

Значения из стека можно сопоставить с аргументами DeviceIoControl (их описание, повторюсь, можно найти здесь). Документация сообщает нам о том, что IOCTL_SCSI_PASS_THROUGH_DIRECT принимает SCSI-команду для отправки устройству через входной буфер, который должен соответствовать третьему аргументу DeviceIoControl — lpInBuffer. В нашем случае третий аргумент — это 0x32ed90. А четвёртый аргумент, nInBufferSize, содержит сведения о размере этого входного буфера. Здесь это 0x50 байтов. Поэтому давайте взглянем на 0x50 байтов из 0x32ed90.

67f766384c2b77e7c20282a74f0b6617.png

Содержимое lpInBuffer

Итак, это ли та самая команда, благодаря которой программа позволяет взломать роутер? Страница документации по IOCTL говорит нам о том, что это должна быть структура SCSI_PASS_THROUGH_DIRECT. Вот как она выглядит:

typedef struct _SCSI_PASS_THROUGH_DIRECT {
  USHORT Length;
  UCHAR  ScsiStatus;
  UCHAR  PathId;
  UCHAR  TargetId;
  UCHAR  Lun;
  UCHAR  CdbLength;
  UCHAR  SenseInfoLength;
  UCHAR  DataIn;
  ULONG  DataTransferLength;
  ULONG  TimeOutValue;
  PVOID  DataBuffer;
  ULONG  SenseInfoOffset;
  UCHAR  Cdb[16];
} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;

Данные, полученные из GDB, можно сопоставить с полями этой структуры. В Windows тип данных ULONG представляет собой 32-битное целое число. Кроме того, при отладке 32-битных приложений нужно учитывать то, что PVOID — это тоже 32-битное значение.
Обратите внимание на то, что тут нужно учесть три байта, используемых для выравнивания данных (это — PADDING в таблице). Дело в том, что C-компилятор Microsoft должен выравнивать DataTransferLength по 4-байтным границам.

Воспроизведение команды


Теперь, когда мы расшифровали вышеописанную структуру, давайте разберёмся с тем, какие именно действия эта структура предлагает выполнить Windows. Большинство полей нам неинтересны. Самое главное для нас поле — это Cdb. Оно, в соответствии с документацией, «задаёт блок дескриптора SCSI-команды, который нужно отправить на целевое устройство». Это, другими словами, должна быть та самая команда, которая переводит роутер в отладочный режим! Затем, после отправки этой команды, так как флаг DataIn установлен в значение 0x01 (что эквивалентно SCSI_IOCTL_DATA_IN), Windows прочитает с устройства 0xc0, или 192 байта (значение DataTransferLength). В архитектуре x86 используется формат little-endian. Поэтому байты 0xc0 0x00 0x00 0x00 интерпретируются как 32-битное целое число 0x000000c0.

Некоторые изыскания вывели меня на команду sg_raw — на Linux-инструмент, который позволяет отправлять SCSI-устройствам произвольные команды (в Ubuntu это средство можно найти в пакете sg3-utils). Если использовать значение из Cdb, то интересующая нас команда будет выглядеть так:

sudo sg_raw /dev/sgX 16 f9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -v

Здесь X — это номер, соответствующий диску роутера. Это будет, вероятно, либо 1, либо — 2. Для того чтобы просмотреть список SCSI-устройств, подключённых к компьютеру, можно воспользоваться командой ls /dev/sg*.

Я попробовал выполнить эту команду.

a19c67bc2d8b12743a711f53adb0fdb4.png

Успешные результаты выполнения команды

Получилось! Мы наконец-то нашли нужную команду. В этот момент у нас появляется полный root-доступ к роутеру. А это значит, что сделать с ним можно практически всё, что угодно.

Итоги


Если вы внимательно читали мой рассказ, то вы, возможно, заметили, что первые два байта команды (0×16 0xf9) — это то же самое, что написано на кнопке TCL-SWITCH-TOOL, SWITCH : 0x16 0xF9(debug), используемой для перевода роутера в режим отладки (это можно видеть на скриншоте в начале статьи). Но, хотя решение нашей задачи изначально было так близко, я не считаю, что проделал всю эту работу зря. Всё это помогло мне разобраться с тем, как именно формируются эти байты. Это, кроме того, означает, что, вероятно, устройство можно переключить и в «диагностический режим», «diag», так как на ещё одной кнопке, SWITCH : 0x16 0xF5(diag) есть соответствующая надпись (я думаю, что слово «diag» на кнопке имеет отношение к протоколу Qualcomm DIAG, но я всё ещё занимаюсь изучением этого вопроса).

Кроме того, наша итоговая команда сработала лишь из-за того, что отладочный протокол сравнительно прост. После того, как мы обнаружили нужную команду, мы смогли просто воспроизвести её самостоятельно. Если бы отладочный протокол был бы устроен сложнее (например, если бы команда могла бы меняться под воздействием некоего параметра, который, скажем, содержал бы сведения о текущем времени), тогда пришлось бы проводить более глубокий анализ программы.

И ещё одно — для доступа к командной оболочке роутера используется adb (Android Debug Bridge, Отладочный мост Android). Хотя роутер и работает не под управлением Android, в нём, вероятно, используется adbd, а так же некоторые другие Android-компоненты. Детальное исследование всех тех программ, которые работают на Alcatel MW41, возможно, станет темой одной из моих следующих статей.

Что бы вы сделали с мобильным WiFi-роутером, если бы у вас был root-доступ к нему?

oug5kh6sjydt9llengsiebnp40w.png

© Habrahabr.ru