Робимо кастомний прошивку для телефонів 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 отримуємо підсумковий рядок виду CallengesfTXrhCA2010Password і кладемо її в змінну 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_unpack
prov_dec
prov_enc
prov_pack
докладно Зупинятися на тому, як це искалось в IDA я вже не буду, скажу лише, що для полегшення реверс інжинірингу, на телефон був завантажений gdbserver, і ці команди я проганяв в дебагері.
Тепер про цих командах детальніше:
prov_unpack — розпаковує прошивку на окремі файли, запускається так:
prov_unpack gxp1400fw.bin
Результат розпакування буде в поточній директорії.
prov_dec — розшифровує окремі файли прошивки
prov_dec nokey gxp1400prog.bin gxp1400prog.bin
Перший параметр — ключ прошивки, у заводських прошивок — це nokey, однак можуть бути oem выриатны телефонів зі своїми ключами.
Другий параметр — файл, який дешифруем
Третій параметр — за задумом виробника це відповідний до образу розділ під флеш телефону, програма порівнює версію з файлу і вже прошитую версію, і якщо вони рівні, то нічого не робить. Ми ж вказуємо другим параметром ще раз сам зашифрований файл прошивки, тоді все відбувається гладко. На виході отримуємо розшифрований файл gxp1400prog.bin
prov_enc — шифрує образ назад, але, образ їй потрібен у спеціальному форматі.
Про формат образів детальніше:
Образ складається із заголовка і власне корисних даних, наприклад файлової системи squashfs.
Нижче приклад заголовка образу gxp2130prog.bin

Заголовок займає перші 0x5C байт, ось його формат:

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С
Корисні дані йдуть зі зміщення 0x200, місце між заголовком і корисними даними забивається одиницями…
Так виглядає расшифровануая прошивка і в такому вигляді вона пишеться у флеш, разом із заголовком.
Утиліта prov_enc, працює з іншим форматом. На вході у неї докжен бути файл, де спочатку йдуть корисні дані, а відразу після них (тобто в кінці файлу), заголовок, розмірами 0x5C. prov_enc читає з заголовка розмір корисних даних, шифрує їх, а потім шифрує сам заголовок. У заголовку завжди шифруються тільки перші 32 байта, інші байти не шифруються. Щоб зашифрований файл зібрати назад в прошивку утилітою prov_pack, його необхідно перевести в перший формат, тобто перенести вже шифрований заголовок початок файка, а зашифроване тіло прошивки помістити по зсуву 0x200.
Запускається 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 < stdio.h>

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

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.