Делаем кастомную прошивку для телефонов Grandstream
Наша компания наконец решила перейти на ip телефонию, и мы закупили ip телефоны Grandstream разных моделей, среди них были модели GXP2130 и GXP2160. Всё бы ничего, но BLF клавиши на этих телефонах, в случае свободной линии, светятся жутко ярким зелёным цветом, сильно раздражая. Ниже расскажу, как я решал эту проблему.Поиск уязвимостиДля начала прогнал прошивку телефона через binwalk и посмотрел в hex редакторе. Итог расстроил, прошивка была зашифрована, значит надо идти с другой стороны, попробовать получить рутовый доступ на сам телефон. В течение почти двух недель, я искал уязвимость в фильтрации полей в веб интерфейсе. В паре мест нашёл возможность передать свои параметры коммандам syslogd и udhcpd. В случае сислога, интереса это не предоставляла, а вот в случае udhcpd, была возможность указать параметр -s, который ссылался на скрипт, запускаемый для настройки интерфейса. Здесь можно было выполнить любую команду, НО ей нельзя было указать свои параметры, она всегда выполнялась с параметром defconfig. С помощью этой уязвимости, сделать ничего путнего так и не удалось. Поэтому поиск уязвимости продолжился. И я её нашёл! Не буду рассказывать где именно, т.к. производители могут её быстро прикрыть, а в будущем она может ещё пригодиться, ну и рут доступ можно получить без неё.Далее я предположил, что производитель наверняка должен был оставить возможность получения рута и для себя, например для отладки, и полез искать это. Проанализировал все скрипты, выполняемые через web, ничего похожего там не нашёл. Далее начал анализ шелла, который доступен по ssh.Анализ шелла Когда мы подключаемся к телефону по ssh, то попадаем в шелл gs_config, из которого доступен небольшой список команд, обрабатываемых самим шеллом. Я предположил, что там могут быть сервисные команды, не описаные в help’е. Для этого я запустил strings gs_config чтобы посмотреть строки внутри бинарника, и увидел нечето любопытное: console fw_setenv console yes gssu /bin/sh Быстро набрал эти команды в шелле, на что увидел следующее: Grandstream GXP2130 Command Shell Copyright 2014 GXP2130> gssu Challenge: fb72f22fc5e233ae Response: Команда требует пароль, сгенерированный на основе некого challengeНе долго думая, грузим gs_config в IDA и ищем там строку gssuдалее переходим по XFER в функцию sub_94BCвидим, что после ввода команды gssu, происходит вызов функции sub_B254, и в зависимости от её результата, выполнияется или не выполняется команда /bin/shПереходим в эту функцию и нажимаем F5, для переключения с ассемблера на C++ псевдокодПробежавшись глазами по коду видно, что вначале идёт генерация challenge, и полученный challenge кладётся в переменную s
printf («Challenge: %s\n», s); потом принимается ввод от пользователя response и начинается сама генерация ResponseДанная строка вытаскивает из nvram админский пароль, по которому вы логинитесь в веб морду и sshДалее он кладётся в переменную v13.Потом анализируется содержимое переменной v1, которая является параметром нашей функции sub_B254. Её значение, говорит о том, для какой команды мы проверяем Response, таких команд должно быть три, но я нашёл только две: gssu и consoleВ случае gssu, мы получаем строку %s: sfTXrhCA2010:%s в переменной v14Далее, через sprintf получаем итоговую строку вида Callenge: sfTXrhCA2010: Password и кладём её в переменную v27Потом считаем от этой строки md5.Дальше идёт цикл do…while на 8 итераций в котором мы проходим половину md5 суммы, и переводим её в hex строчку. Потом сравниваем её с введённым Response.Алгоритм достаточно простой, вот реализация кейгена на питоне: import hashlib import sys
challenge=sys.argv[2] pwd=sys.argv[1] secret=': sfTXrhCA2010:' # /sin/sh #secret=': dspg_cordless_config:' #secret=': a50ba3e905c0627eb0a204d82880fb46:' # console str=challenge+secret+pwd md5=hashlib.md5(str).hexdigest () result=md5[:16] print result Работа с прошивкой Ну вот, получать перманентно рута на телефоне мы научились, теперь пора изучить, как он распаковывает прошивку.Беглым осмотром исполняемых скриптов на телефоне находим скрипт /sbin/provision, который собственно и отвечает за прошивку телефона.Из него видно, что прошивка распаковывается на отдельные файлы командой prov_pipe_unpack, а далее, отдельные разделы прошивки дешифруются командой prov_pipedec. На самом деле, это один и тот же бинарник. Ятобы узнать все его возможности, я его так же закинул и IDA, где и нашёл интересующие нас команды, это: prov_unpackprov_decprov_encprov_pack
Останавливаться подробно на том, как это искалось в IDA я уже не буду, скажу лишь, что для облегчения реверс инжиниринга, на телефон был загружен gdbserver, и эти команды я прогонял в дебаггере.Теперь об этих командах подробнее: prov_unpack — распаковывает прошивку на отдельные файлы, запускается так: prov_unpack gxp1400fw.bin Результат распаковки будет в текущей директории.prov_dec — расшифровывает отдельные файлы прошивки prov_dec nokey gxp1400prog.bin gxp1400prog.bin Первый параметр — ключ прошивки, у заводских прошивок — это nokey, однако могут быть oem выриатны телефонов со своими ключами.Второй параметр — файл который дешифруемТретий параметр — по замыслу производителя это соответствующий образу раздел во флеше телефона, программа сравнивает версию из файла и уже прошитую версию, и если они равны, то ничего не делает. Мы же указываем вторым параметром ещё раз сам шифрованный файл прошивки, тогда всё происходит гладко. На выходе получаем расшифрованный файл gxp1400prog.binprov_enc — шифрует образ назад, но, образ ей нужен в специальном формате.О формате образов подробнее: Образ состоит из заголовка и собственно полезных данных, например файловой системы squashfs.Ниже пример заголовка образа gxp2130prog.binЗаголовок занимает первые 0×5C байт, вот его формат: struct header { DWORD signature; DWORD version; DWORD size_max; DWORD size; WORD image_id; WORD checksum; WORD ts_year; WORD ts_month_day; WORD ts_time; WORD oem_id; DWORD FW_V_Mask; WORD supported_bits1; WORD supported_bits2; WORD supported_bits3; WORD supported_bits4; WORD HW_id; } Назначение не всех полей мне понятно, но это не важно, рассмотрим основные: version — это версия, если мы хотим чтобы наша прошивка прошилась в телефон, её версия должна быть выше текущейsize — размер полезных данных в прошивке, этот параметр использует prov_enc прои шифрованииchecksum — контрольная сумма прошивки, используется при расшифровке прошивки, если не совпадёт, прошивка не прошьётся, об её генерации позже. Остальные поля заголовка, нужно оставлять как и в оригинале, разве что можно поправить дату.Далее заголовок добивается нулями до размера 0×5СПолезные данные идут со смещения 0×200, место между заголовком и полезными данными забивается единицами…Так выглядит расшифрованyая прошивка и в таком виде она пишется во флеш, вместе с заголовком.Утилита prov_enc, работает с другим форматом. На входе у неё доkжен быть файл, где вначале идут полезные данные, а сразу после них (т.е. в конце файла), заголовок, размерам 0×5C. prov_enc читает из заголовка размер полезных данных, шифрует их, а потом шифрует сам заголовок. У заголовка всегда шифруются только первые 32 байта, остальные байты не шифруются. Чтобы зашифрованный файл собрать назад в прошивку утилитой prov_pack, его необходимо перевести в первый формат, т.е. перенести уже шифрованный заголовок в начало файkа, а шифрованное тело прошивки поместить по смещению 0×200.Запускается prov_enc так: prov_enc nokey gxp1400prog.bin gxp1400prog.bin Здесь всё аналогично prov_dec.prov_pack — собирает все шифрованные файлы прошивки в цельную прошивку, готовую для прошивки в телефон prov_pack nokey gxp1400fw.bin gxp1400boot.bin gxp1400recovey.bin gxp1400core.bin gxp1400base.bin gxp1400prog.bin На выходе имеем готовый для прошивки файл gxp1400fw.binС этими утилитами удобнее работать в виртульной машине qemu, чем в самом телефоне.Патчим зелёный светодиод Теперь перейдём собственно к тому, ради чего всё затевалось, отключению зелёного светодиода на BLF клавишах.За GUI в телефоне отвечает процесс gs_gui, лежит он в /app/gui/ и использует гору библиотек из /app/gui/libДелаем grep по слову LED в папке /app/gui и находим библиотеку libFramework.so.1.0.0Сливаем её к себе на комп и грузим в IDA, благо все функции там имеют человеческие названия.Находим функцию с интересным названием turnOnMKPLED, из неё вызывается другая функция writeToFile (LEDCOLOR, int, bool)Ниже её кусок: Как видно, для работы со светодиодами используются файлы /proc/sys/dev/led/*Попробовал записывать данные в эти файлы через echo, и нашёл что за BLF (MKP) клавиши отвечают файлы prog_green и prog_red.Соответственно, чтобы запретить зажигать зелёный светодиод, надо просто запретить писать в файл prog_green. Я это сделал просто, в hex редакторе изменил одну букву в столе green.Теперь пропатченный libFramework.so.1.0.0 надо залить назад в телефон. Создадим для это кастомную прошивку.Директория /app содержится в образе gxp2130prog.bin. Распаковываем прошивку и расшифровываем этот образ. Далее в hex редакторе обрезаем все до смещения 0×200 и получаем squashfs образ.Для работы со squashfs понадобится набор утилит squashfs-tools.Версия 4.0 из дистрибутива Centos не смогла его распаковать, поэтому пришлось собирать из исходников версию 4.2Распаковывем командой
./unsquashfs gxp2130prog.bin
Содержимое будет в директории squashfs-rootДалее меняем там файл libFramework.so.1.0.0 на наш и пакуем назад
./mksquashfs squashfs-root new.bin -comp xz -all-root -noappend -always-use-fragments
Теперь нам надо подготовить заголовок. Для начала возьмём заголовок из оригинального gxp2130prog.bin и скопируем его в конец нашего squashfs образа. Теперь надо поправить в нём версию и размер. Размер — это размер самого squashfs образа, без заголовка. Теперь необходимо посчитать контрольную сумму. Вот код на Си для её генерации (на питоне аналогичный код почему-то работал в 200 раз медленнее, а запускалось это в qemu c эмуляцией arm)
#include
void main (int argc, char *argv[]) { FILE *f; int summ=0; int word; char buff[32]; int i; f = fopen (argv[1], «rb»); if (f) { while (fread (buff,32,1, f) != 0) { for (i=0; i<32;i+=2) { word = buff[i]; word |= buff[i+1]<<8; summ += word; summ &= 0xFFFF; } } printf("%d\n",0x10000-summ); } else printf("Error\n"); } Далее шифруем файл, переставляем заголовок и собираем прошивку, как описывалось в предыдущем разделе. Ну и прошиваем её.Если захотите вернуть оригинальную прошивку, то на телефоне надо ввести от рута команду nvram set force_upgrade=1 И телефон прошьёт любую прошивку. не зависимо от версии.Если интересно, могу ещё написать подробную статью о провижинге телефонов грандстрим (есть нюансы, о которых нигде не писалось) и о закрытом web-api