[Перевод] Джейлбрейкаю навигационную систему моей машины, чтобы поменять время

Джейлбрейкаю навигационную систему моей машины, чтобы поменять время
11 мин
921
Перевод
Я езжу на Renault Latitude 2014 года. Это далеко не самая роскошная машина, да к тому же и не самая новая, но она доставляет меня из точки А в точку Б, справляясь с этим достаточно неплохо, жаловаться не на что.
Когда я покупал машину, её предыдущий владелец спросил, нужна ли мне «навигационная SD‑карта».
Он сказал, что машине эта SD‑карта, похоже, нужна только для изменения времени на часах, но мне это показалось маловероятным: возможно, он просто не смог разобраться, как зайти в нужное меню настроек. В конце концов, в видео про похожие головные устройства можно было заметить меню с опцией настройки часов.
Так как навигационные карты на SD, очевидно, сильно устарели и работали неправильно, я решил, что они мне не нужны, и владелец выбросил их.
Мне они и не были особо нужны. На дисплее отображалось сообщение «Карты не найдены!», занимающее большую часть экрана, но это не сильно мне мешало.
Как и в большинстве европейских стран, в Эстонии используется перевод на летнее время, и недавно мы перешли с летнего времени (UTC+3) на «зимнее» (UTC+2), из‑за чего отображаемое на дисплее время стало торопиться на час. Я зашёл в меню настроек, надеясь с лёгкостью обнаружить там опцию «Настроить время».
Но её нигде не было.
В меню, в котором должен был находиться пункт «Настроить время», он попросту отсутствовал. Я сравнил своё меню с меню из онлайн‑видео, и оказалось, что пункта просто нет. На панели было две кнопки Settings, одна из которых позволяла настраивать только различные параметры Bluetooth и телефонной книги. Вторая кнопка Settings вообще не работала и при каждом нажатии отображала только всплывающее сообщение «Карты не найдены!».
Я потратил около часа на блуждание по разным меню, нажатия на различные кнопки или сочетания кнопок в надежде оказаться в нужном меню; я искал в вебе информацию конкретно об этом устройстве, потому что отказывался верить, что в нём нет опции изменения времени. Ведь я наверняка упускаю нечто очевидное.
Что ж, придётся реверс‑инжинирить
Всё ещё отказываясь верить в отсутствие этой опции, я решил найти прошивку и изучить её, надеясь обнаружить в ней признаки того, как открыть нужное меню.
К счастью, я не первым прошёл этот путь: кто‑то уже занимался реверс‑инжинирингом конкретно этого устройства (https://github.com/andronedev/carminat‑tomtom‑live‑re/blob/main/README.md). Хоть файл написан на французском и, похоже, с большой помощью LLM, он позволил мне найти нужные URL для загрузки самих файлов обновлений.
Распакованные файлы обновления содержат файл ttsystem, который оказался файловой системой ext3, содержащей корневую файловую систему устройства, а именно её я и искал. Само устройство (TomTom Carminat LIVE, кодовое название beaumont) разработано на основе SoC Samsung S5P6440 со специализированной ОС TomTom на основе Linux 2.6.28.10.
Приступив к её реверс‑инжинирингу, я пришёл к выводу, что предыдущий владелец машины был совершенно прав: зайти в нужное меню настроек без навигационной SD‑карты попросту невозможно. На самом деле, определение меню, указывающее на апплет Settings, считывается с самой SD‑карты, и без вставленной карты, содержащей нужное определение меню, устройство даже не будет знать, как войти в апплет Settings.
Это решение, принятое TomTom (или Renault), до сих пор меня озадачивает.
Хуже того: это обязательно должна быть подлинная SD‑карта. В системе есть DRM‑проверки, отклоняющие SD‑карты без правильного CID (уникального ID, прописанного на карте) и файлов с сигнатурами, зависящих от этого конкретного CID, поэтому нельзя было даже скопировать файлы на пустую SD‑карту. Подозреваю, DRM использовалась для защиты от пиратства карт.
Но мне не нужны карты, для навигации у меня есть телефон. Я просто хочу поменять это чёртово время.
Поискав онлайн, я нашёл сторонних продавцов предположительно нужных мне карт, но с отправкой в Эстонию они стоили бы мне примерно 60 евро, которые я не был готов платить.
Что ж. Придётся получить рут и сделать всё самостоятельно.
Лёгкая победа?
Мне не удалось найти сообщений об успешном джейлбрейке этого оборудования. В старых темах на форумах с давно протухшими ссылками на изображения имелась достаточно общая информация о самом устройстве. В целом участники тем пришли к мнению о том, что рут на нём получить сложно.
Я не ожидал, что систему 2011 года на Linux 2.6 будет так уж сложно взломать, поэтому наивно начал искать лёгкие решения.
Первым делом я подумал о внесении изменений в сам образ обновления ext3. Мне казалось, что можно просто примонтировать файловую систему, изменить скрипты инициализации и скопировать всё это на пустую SD‑карту, после чего устройство найдёт мой модифицированный образ и прошьёт его.
При установке ванильных, немодицифированных образов ttsystem система без проблем показывала утвердительные сообщения, но мне так и не удалось обновить её моим модифицированным образом, и я понятия не имел, почему. Я проверил логику программы обновления в самом файле обновления, но не смог найти причин отклонения моего образа.
И на этом этапе я настроил эмулятор на базе QEMU, специализированное ядро, также основанное на версии 2.6, добавил несколько патчей и очень простой эмулятор CAN‑шины, чтобы система вообще смогла загрузиться. Правда, загружалась она неохотно и пыталась перезагружаться каждые три секунды. Эту проблему я решил патчем, который просто игнорировал системные вызовы перезагрузки.
Я использовал эту систему, чтобы проверить, удастся ли установить в неё модифицированный образ обновления. Затем я понял, что у меня на самом деле нет раздела recovery, в который загружается устройство для выполнения обновления, и пришёл к выводу, что там должен быть какой‑то уровень проверки, который я упускаю из виду.
Более внимательное изучение образа ttsystem практически подтвердило это: к нему были добавлены данные с высокой энтропией, за которыми следовали несколько блоков нулей, а затем ещё 40 байт данных с высокой энтропией. Я предположил, что это какая‑то цифровая подпись (40 байт могли быть подписью DSA с SHA-1), но, не имея доступа к самому исполняемому файлу, который выполнял проверку, я не мог толком понять, как обойти эту защиту и возможно ли это вообще.
Возвращаемся в начало
Мне очень не хотелось трудиться над сборкой эмулятора, но он оказался крайне полезен для тестирования различных видов атак и анализа общего поведения системы.
Например, я выяснил, что большинство разделов было смонтировано или только для чтения, или как noexec, или как и то, и другое. Точки монтирования в рабочей системе:
rootfs on / type rootfs (rw)
none on /sys type sysfs (rw,nosuid,noexec)
none on /proc type proc (rw,nosuid,noexec)
/dev/mmcblk0p5 on /content type ext3 (rw,nosuid,nodev,noexec,noatime,nodiratime,errors=continue,data=ordered)
/dev/loop0 on / type ext2 (ro,noatime,nodiratime,errors=continue)
none on /dev type tmpfs (rw,nosuid,noexec,size=1024k)
none on /tmp type tmpfs (rw,nosuid,noexec,size=2048k)
none on /var type tmpfs (rw,nosuid,noexec,size=5120k)
none on /dev/pts type devpts (rw,nosuid,noexec,gid=5,mode=620)
none on /var/log type tmpfs (rw,nosuid,noexec,size=1024k)
/dev/mmcblk0p3 on /mnt/flash type ext3 (rw,sync,nosuid,nodev,noexec,errors=continue,data=ordered)
/dev/mmcblk1p1 on /media/sdcard type vfat (rw,sync,nosuid,nodev,noexec,noatime,nodiratime,fmask=0022,dmask=0022,codepage=cp437,iocharset=iso8859-1)
/dev/loop1 on /media/loopback type ext3 (rw,nosuid,nodev,noexec,noatime,nodiratime,errors=continue,commit=3600,data=journal)
none on /mnt/movi type unionfs (rw,nosuid,nodev,noexec,noatime,nodiratime,dirs=/media/loopback=rw:/media/sdcard=ro)Примечательно, что базовая файловая система FAT32 на SD‑карте была смонтирована в /media/sdcard, а поверх неё система монтировала UnionFS в /mnt/movi. У этой UnionFS был доступный для записи слой, указывающий на файловую систему ext3, которая хранилась в виде файла на внешней SD‑карте. Я не до конца понимаю, зачем понадобилась такая схема (возможно, чтобы использовать режим data=journal, который лучше сохраняет данные при отключении питания?), но, как бы то ни было, все эти файловые системы были смонтированы с параметром noexec.
Выглядело это неожиданно сильной защитой для системы 2011 года.
Ещё одна задача, с которой мне помог эмулятор — определение всех файлов, которые система пыталась прочитать с внешней SD‑карты. Одним из них оказался master.script — скрипт на специальном языке, предназначенном для автоматизации некоторых действий по навигации в пользовательском интерфейсе. Он загружался при каждой загрузке системы, и я надеялся, что с его помощью мне удастся каким‑нибудь образом открыть нужное меню настроек, чтобы установить время.

Я выполнил реверс‑инжиниринг двоичного файла ttn, отвечающего за парсинг этого скрипта; в процессе работы я документировал его функциональность, но, к сожалению, не нашёл способа открыть нужное мне меню.
Снова ищем ветра в поле
Я по‑прежнему думал, что мне в первую очередь нужно заниматься master.script, потому что в нём содержалась большая доля функциональности, которая может стать потенциальным вектором атаки.
Как обычно, я искал любые вызовы system или popen, которые позволили бы мне выполнить инъецирование команд. К сожалению, я не нашёл ни одного вызова, созданного с использованием пользовательского ввода.
Я решил, что мне крупно повезло, когда при реверс‑инжиниринге парсера я наткнулся на команду SHEX/SHELLEXECUTE. Она передавала произвольный аргумент программе launchserver, которая запускала указанную командную строку с помощью execve.
Здесь возникло два препятствия. Первое заключалось в том, что по‑прежнему не было способа заставить систему напрямую запускать пользовательский исполняемый файл: SD‑карта была смонтирована с параметром noexec, запрещающим запуск исполняемых файлов и скриптов с неё. Однако это ограничение можно было обойти, не запуская скрипт напрямую, а выполнив, например, /bin/sh/media/sdcard/payload.sh. В этом случае скрипт запускался бы через /bin/sh из rootfs, которая не была смонтирована с параметром noexec.
Однако второе препятствие оказалось куда серьёзнее. Выяснилось, что launchserver охотно запускает что угодно, но команда SHEX принимает только один токен, отделённый пробелом. Это означало, что я мог управлять лишь тем, какой исполняемый файл будет запущен, но не мог передать ему никаких аргументов. В сочетании с предыдущим препятствием это, казалось, заводило меня в тупик: я мог запускать только скрипты и исполняемые файлы, уже имевшиеся в rootfs. Я просмотрел большую их часть, но ничего полезного не нашёл.
Потерпев неудачу, я переключился на изучение операций записи файлов. Возможно, если бы мне удалось записывать файлы в места, куда у меня не должно было быть доступа, это дало бы мне какое‑нибудь преимущество.
Как и следовало ожидать, я обнаружил команду LOG, которая позволяла обрезать файл (путь к нему задавался через #define LOGFILE log.txt) и записать одну строку в любой выбранный мной файл. Здесь уже допускались пробелы и обычные символы ASCII, но только в диапазоне от 0×20 до 0×7E, поэтому о записи двоичных данных не могло быть и речи. Поскольку ttn запускался от имени рута, все файлы также записывались от имени рута.
Кроме того, здесь присутствовала классическая уязвимость обхода каталогов (path traversal). Если задать LOGFILE, включив в путь ../, можно было записывать файлы за пределами предназначенного для логов каталога. Теоретически это позволяло мне записывать печатные символы ASCII в любое место системы.
Однако и здесь я снова упёрся в ограничения, связанные с конфигурацией точек монтирования. Я не мог перезаписать ничего в rootfs, поскольку она была смонтирована только для чтения, а всё остальное было смонтировано с параметром noexec и в любом случае никогда бы не исполнялось. Я также проверил, нет ли полезных конфигурационных файлов, которые можно было бы перезаписать, но большинство из них имели собственный двоичный формат, в который команда LOG ничего записать не могла, либо были бесполезны по другим причинам. Похоже, и этот путь оказался тупиковым.
Среди прочего, master.script позволял выводить на экран изображения (GIF и BMP). Я потратил некоторое время на настройку среды для фаззинга с использованием Unicorn, чтобы проверить, удастся ли обнаружить ошибки в декодерах изображений, приводящие к повреждению памяти. Мне удалось найти немало случаев чтения за пределами выделенной области памяти (OOB read), но ничего, что позволяло бы записывать данные за её пределами, я не обнаружил.
Позаимствуем трюк у эксплойтов ядра
Я уже довольно долго занимался исследованием и реверс‑инжинирингом устройства, пытаясь придумать разные способы выполнения произвольного кода. К моему большому разочарованию, ни одна из этих стратегий не сработала. Тогда я решил сделать шаг назад и взглянуть на ситуацию в целом, чтобы понять, можно ли как‑то соединить уже имеющиеся у меня фрагменты головоломки.
Среди прочего, я мог:
Исполнять произвольные двоичные файлы, но без аргументов и только те, которые уже есть в rootfs
Записывать ASCII по выбранным мной путям
Тут меня озарило и я вспомнил трюк, часто используемый эксплойтами ядра: почему бы вместо того, чтобы пытаться заставить исполнить наш скрипт пользовательским пространством, выполнить его при помощи ядра?
Эксплойты ядра Linux, имеющие возможность произвольной записи данных в память, часто перезаписывают строку core_pattern. Обычно она определяет, куда сохраняются дампы памяти при аварийном завершении процесса, но также поддерживает специальный синтаксис, позволяющий передавать эти дампы другому процессу через канал (например, |/bin/my-coredump-handler). Когда происходит аварийное завершение процесса, ядро запускает программу, указанную в core_pattern, и передаёт ей данные дампа через канал.
core_pattern также поддерживает передачу аргументов командной строки запускаемому процессу. Здесь мне повезло: эта возможность появилась в версии 2.6.24, а ядро устройства работало на версии 2.6.28.
Очень важно то, что можно также задать core_pattern из пользовательского пространства, записав паттерн в /proc/sys/kernel/core_pattern.
Я могу легко воспользоваться этим, указав в LOGFILE путь ../../proc/sys/kernel/core_pattern, а с помощью команды LOG записать туда строку |/bin/sh/media/sdcard/payload.sh. В результате запустится /bin/sh из корневой файловой системы, который выполнит наш скрипт с правами рута.
Кроме того, возможность запускать произвольные исполняемые файлы оказалась полезной даже без передачи аргументов: мне нужно было каким‑то образом заставить программу аварийно завершиться, чтобы вызвать создание дампа памяти. Использовать уязвимость типа DoS для аварийного завершения парсера скриптов или самого основного пользовательского интерфейса было бы не лучшим решением, поскольку это немедленно приводило бы к перезагрузке всей системы, в результате чего мой скрипт завершал бы работу.
С помощью настроенного эмулятора я запустил практически все исполняемые файлы в системе и обнаружил, что /bin/dbus-monitor генерирует сигнал SIGABRT, если не может подключиться к системной шине (а на устройстве она не была запущена). Это вызывало создание coredump. То, что нужно!
Соединяем всё вместе
Итоговый master.script оказался довольно простым:
#define LOGFILE ../../proc/sys/kernel/core_pattern
LOG |/bin/sh /media/sdcard/payload.sh
SHEX /bin/dbus-monitorСначала я проверил, что всё работает, просто добавив вызов reboot в скрипт payload.sh. Моему восторгу не было предела, когда после загрузки устройства с вставленной SD‑картой и нажатия «Да» в ответ на запрос «Исполнить master.script?» устройство перезагрузилось. Это подтвердило, что мне удалось добиться выполнения собственного кода.
Первым делом я воспользовался этой возможностью, чтобы создать полную резервную копию внутренней памяти eMMC с помощью dd. Получив более полный дамп системы, я довольно быстро выяснил, что интересующие меня настройки даты и времени хранятся в файле /mnt/flash/settings.dat, который имеет собственный двоичный формат. После дальнейшего реверс‑инжиниринга выяснилось, что в устройстве вообще нет понятия часовых поясов. Оно получает время по GPS, а местное смещение хранит в виде числа минут. У меня было установлено значение 180.
Я поменял его на 120.
И вот так мне наконец удалось сменить часовой пояс моего автомобиля.
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.