Реверс-инжиниринг роутера на примере GL.iNet GL-AR750
29 октября 2018
Сегодня нас окружает множество так называемых «умных» устройств — телефонов, часов, Wi-Fi роутеров, IP-камер. Даже лампочки и зубные щетки внезапно поумнели. Само по себе это явление не обязательно плохое. Плохо то, что названные устройства часто доступны посторонним людям через интернет, Bluetooth или Zigbee. При этом производители не сильно беспокоятся об их безопасности. Во многих роутерах даже находили намеренно оставленные бэкдоры (пример 1, пример 2). Не удивительно, что мы видим появление целых ботнетов из IoT устройств. Как выяснить, нет ли в конкретном устройстве уязвимостей или бэкдоров? Давайте разберемся на примере роутера GL.iNet GL-AR750.
Не спешите обновляться
В свое время я имел неосторожность обновить прошивку моего GL-AR750 на апстрим-версию OpenWrt. Вообще, я перевел все имеющиеся у меня роутеры на OpenWrt, как раз из соображений безопасности. Хотя официальная прошивка также основана на OpenWrt, она отличалась от апстрим-версии, как минимум — веб-интерфейсом. Откуда нам знать, что в ней нет еще каких-нибудь отличий, например, предустановленного ring0 бэкдора или буткита? Читателям предлагается произвести собственное исследование данного устройства с официальной прошивкой, а затем сообщить в комментариях, насколько результат отличается от полученного мной.
Lesson learned — если вы планируете заниматься исследованием роутеров или иных устройств, либо не обновляйте их, либо делайте резеврный дам flash-памяти. Далее будет рассказано, как его сделать. Даже если вы продолжаете использовать официальную прошивку, в новых версиях она может содержать нежелательные исправления, такие, как, отключение UART. Кроме того, не имея резервного дампа, вы не сможете выяснить, какие байтики и как изменились во flash-памяти между версиями.
Надеюсь, вы найдете описанные далее техники интересными, невзирая на тот факт, что оригинальная прошивка была мной безвозвратно утеряна. Само собой разумеется, эти приемы могут быть применены для любого другого роутера, а также других видов устройств.
Открываем корпус
Конечно, уязвимости и бэкдоры можно искать классическими методами, сканируя порты устройства nmap’ом, подставляя кавычки во все поля веб-интерфейса, и так далее. Но может оказаться куда эффективнее сразу получить доступ к flash-памяти роутера и изучить исполняемый код. Некоторые роутеры могут предоставлять доступ к файловой системе через SSH. Но что, если ОС была пропатчена так, чтобы скрывать от нас часть процессов, файлов и сетевых соединений? В общем, самый надежный способ — это сдампить flash-память, а для этого нам сначала нужно вскрыть корпус.
Это достаточно простой шаг. Однако если вы не часто что-то разбираете, следует иметь ввиду следующее. Ни при каких обстоятельствах не прикладывайте чрезмерную силу. Если вам приходится ее прикладывать, вы скорее всего пропустили потайной винтик. Приложив слишком большую силу, вы рискуете сломать печатную плату устройства. Потайные винтики часто прячутся под наклейками на корпусе, а также приклеиваемыми резиновыми ножками. Помимо винтиков часто используются изгибы самого корпуса. В этом случае используйте специальный инструмент — спаджер (spudger, такая плоская металлическая лопаточка).
Конкретно GL.iNet GL-AR750 имел два потайных винтика с обычной крестовой головкой, которые находились под резиновыми ножками. После их удаления корпус легко открывается спаджером.
Изучаем внутренности
Внутри мы видим следующее:
И с обратной стороны:
Нам повезло — производитель не стал стирать маркировку чипов и заливать плату компаундом. При наличии времени и терпения мы можем полностью восстановить схему платы, используя только KiCad и мультиметр. Впрочем, в данном случае это вряд ли необходимо. Давайте просто вооружимся лупой, выпишем названия основных компонентов и посмотрим, нет ли для них даташитов в открытом доступе.
Основные компоненты следующие:
- Qualcomm Atheros QCA9531 — system on chip, процессор MIPS 24Kc 550 MHz со встроенной поддержкой 2.4 GHz 802.11a/b/g/n, а также до 5 портов 10/100 Мбит Ethernet. Процессор является big endian. Этот SoC довольно популярный, его можно встретить во многих роутерах. Даташит для него был найден здесь. Вообще, архитектура MIPS очень часто используется в роутерах;
- Qualcomm Atheros QCA9887 — WLAN system on chip с поддержкой 5 GHz 802.11a/b/g/n/ac. Мне не удалось найти даташит на него в публичном доступе;
- SpecTek PD723 — 128 Мб DDR2 памяти в корпусе FBGA. По большому счету, это все, что известно о данном чипе. Даташита в открытом доступе мне найти не удалось. Логотип компании на чипе был опознан благодаря этой страничке;
- Genesys Logic GL857L — хаб USB 2.0 high-speed со встроенном ридером SD-карт (SDSC, SDHC, SDXC). Даташит был найден здесь [PDF]. Чипы этого производителя можно найти во многих USB-хабах. Однако в данном роутере, по всей видимости, чип используется исключительно для работы с SD-картами;
- KH25L12835F — 16 Mbyte SPI flash в корпусе SOIC-8. Типичный объем для роутера, типичная распиновка. Даташит публично доступен [PDF];
Интересно, что на плате есть два дополнительных u.FL коннектора, что подразумевает возможность подключения внешних антенн вместо встроенных в плату. Поискав по eBay, я нашел модификацию роутера GL-AR750S. Он такой же, только черного цвета, с двумя дополнительными внешними антенками. В описании к товару указан другой процессор, QCA9563. Цена устройства составляет ~80$ против ~50$ у GL-AR750.
Ищем доступные шины
Шелкография GND, TX и RX рядом с пинами намекают нам на то, что у роутера разведен UART. Находящиеся рядом пины GND, SDA, SCL и 3V3 намекают на шину I2C. На фото также можно заметить часть платы, где пины не были впаяны и не имеют подписи. Выяснилось, что четыре из них идут к контактам Ethernet-разъема, два оказались 5V и GND. Еще два, насколько я смог выяснить, ни к чему не ведут. Вероятно, они остались на плате по историческим причинам.
Если подключиться к UART любым USB-UART конвертером и выставить скорость в 115200 бод, мы увидим:
DRAM: 128 MB
Nor Flash: 16 MB, sector count = 256
ath_spi_nand_ecc: Couldn't enable internal ECC
Protect off 9F040000 ... 9F04FFFF
Un-Protecting sectors 4..4 in bank 1
Un-Protected 1 sectors
Erasing Flash...Erasing flash...
First 0x4 last 0x4 sector size 0x10000
Erased 1 sectors
Writing to Flash... write addr: 9f040000
done
Protecting sectors 4..4 in bank 1
Protected 1 sectors
Warning: Bootlimit (3) exceeded. Using altbootcmd.
Un-Protect Flash Bank # 1
Hit any key to stop autoboot: -99
Found ART,Checking calibration status...
Device have calibrated,Checking device test status...
Device have tested,Checking MAC address...
Device have MAC address,Checking device flash status...
Device only have nor flash,Booting standard firmware from nor flash...
Booting image at: 0x9f060000
... далее следует долгий вывод dmesg ...
Таким образом, можно узнать версию бутлоадера, объем доступной памяти, и собрать другую полезную информацию. Более того, нажав Enter, мы получаем шелл с правами root:
uid=0(root) gid=0(root)
root@GL-AR750:/# uname -a
Linux GL-AR750 4.9.111 #0 Mon Jul 30 16:25:17 2018 mips GNU/Linux
Иногда шелл доступен, но защищен паролем. Тогда его можно попытаться подобрать или извлечь, возможно, в виде хэша, из flash-памяти (см далее).
К шине I2C можно подключиться при помощи HydraBus или Bus Pirate и просканировать адреса. В данном случае на шине никто не откликнется. Она предусмотрена исключительно для подключения внешних устройств. Например, можно подключить датчик давления и температуры BMP180 (даташит [PDF]):
...
root@GL-AR750:/# opkg install kmod-i2c-gpio-custom \
> kmod-i2c-core i2c-tools
...
root@GL-AR750:/# i2cdetect -y -a 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77 -- -- -- -- -- -- -- --
Для общения с устройством в OpenWrt можно воспользоваться командами i2cget и i2cset. Даташит BMP180 подробно описывает алгоритм перевода значения регистров в градусы Цельсия и миллиметры ртутного столба, а значит не составит труда реализовать его на Python:
...
root@GL-AR750:~# ./read-bmp180.py
t = 25.3 C, p = 753.6 mmHg
t = 25.4 C, p = 753.6 mmHg
t = 25.4 C, p = 753.9 mmHg
Заинтересованные читатели могут скачать скрипт read-bmp180.py по этой ссылке.
Также из системы мы можем получить доступ к GPIO, как ранее было описано в посте о работе с GPIO-пинами из Raspberry Pi:
gpiochip0: GPIOs 0-31, parent: platform/ath79-gpio, ath79-gpio:
gpio-0 ( |sw1 ) in hi
gpio-2 ( |USB power ) out hi
gpio-3 ( |reset ) in hi
gpio-12 ( |gl-ar750:white:power) out lo
gpio-13 ( |gl-ar750:white:wlan5) out lo
gpio-14 ( |gl-ar750:white:wlan2) out lo
gpio-16 ( |scl ) in hi
gpio-17 ( |sda ) in hi
gpiochip1: GPIOs 494-511, parent: platform/qca953x_wmac, ath9k-phy1:
gpio-495 ( |ath9k-phy1 ) in lo
Пример считывания переключателя, расположенного на роутере сбоку:
root@GL-AR750:~# echo 0 > /sys/class/gpio/export
root@GL-AR750:~# cat /sys/class/gpio/gpio0/value
1
root@GL-AR750:~# cat /sys/class/gpio/gpio0/value
0
Если при попытке использовать GPIO вы видите ошибку:
…, значит ресурс занят каким-то драйвером. К сожалению, в lsof нельзя увидеть, каким. Можно только посмотреть через lsmod, какие драйверы сейчас загружены, после чего погрепать их исходники, или попытаться выгрузить пару драйверов наугад. В данном случае я угадал, что переключатель был занят драйвером gpio_button_hotplug.
Вообще, наличие GPIO, шины I2C, интерпретатора Python, выхода в интернет и связи с другими устройствами по Wi-Fi открывают много возможностей. Тем не менее, давайте вернемся к исследованию роутера.
Очевидно, у него также есть USB шина и по крайней мере две SPI шины — одна для работы с SD-картами, вторая для доступа к SPI flash. Эти и другие шины можно поснифать при помощи логического анализатора — вдруг там передается что-то интересное.
На некоторых роутерах также может быть разведен JTAG. В общем случае пины могут быть не подписаны, или шина может быть вовсе не разведена. Тогда может потребоваться припаяться напрямую к пинам соответствующего чипа. Бывает, что какой-нибудь UART даже разведен и подписан, но отключен. В этом случае имеет смысл проверить, не включается ли он в настройках устройства. Если пины есть, но не очень понятно, что по ним передается, можно потыкать в них щупом осциллографа.
Дампим SPI flash
Ранее в этом блоге уже рассказывалось, как сдампить SPI flash при помощи утилиты flashrom и HydraBus или Bus Pirate. В этот раз для разнообразия было решено воспользоваться платой на базе чипа FT2232HL, вот такой:
Плату можно приобрести на eBay за ~15$. Как видно, мне пришлось ее слегка подхачить, поскольку несколько пинов не были ни к чему подключены, и маркировка рядом с ними не соответствовала действительности.
FT2232 является «старшим братом» популярного USB-UART конвертера FT232. В отличие от него, FT2232HL имеет сразу два USB-UART, 16 пинов для режима bitbang вместо 8, а также имеет механизм под названием MPSSE. Последний позволяет реализовывать последовательные протоколы. Программа на компьютере может сходить по USB к какому-нибудь чипу, скажем, по SPI, используя FT2232 в качестве «мостика». Таким образом, flashrom может использовать чип для чтения SPI flash. Также FT2232 часто используется для конфигурации FPGA, в частности, в плате iCEstick, он может быть использован avrdude для программирования микроконтроллеров AVR, OpenOCD для программирования STM32, и так далее.
Чтобы сдампить чип, его нужно выпаять из роутера при помощи паяльного фена. Перед выпаиванием, пометьте где-нибудь, как был расположен чип, потому что вам его потом впаивать обратно. Если все ясно из шелкографии — прекрасно. Иначе либо зарисуйте, либо сделайте фотографию. Чтобы случайно не сдуть феном компоненты, которые могут находиться рядом с чипом, обдув следует поставить на минимум, а компоненты заклеить каптоном (термоскотчем). Впаиваем чип в переходник из SOIC-8 в DIP, который можно вытравить или купить на eBay, и подключаем к FT2232 таким образом:
- VCC и GND — понятно, питание. Обычно это 3.3 В, но лучше свериться с даташитом;
- CLK — к ADBUS0, пин 16;
- MOSI (он же DI или SI) — к ADBUS1, пин 17;
- MISO (он же DO или SO) — к ADBUS2, пин 18;
- /CS — к ADBUS3, пин 19;
- /WP и /RESET — оба к VCC;
Где какие пины находятся, вы узнаете из даташита чипа. Если даташита вдруг нет, с помощью осциллографа или логического анализатора нетрудно определить, какие пины для чего используются. Часто они расположены таким образом:
Однако вы можете помнить, что у какого-нибудь AT45DB161E пины расположены совершенно иначе. И вообще, память может быть в другом корпусе, например SOIC-16. Или же она может не иметь интерфейса SPI, и тогда это совершенно другой разговор. Однако SPI flash в SOIC-8 с расположением пинов, как показано выше, встречается очень часто.
Итак, в предположении, что пины были успешно найдены, выполняем следующую команду:
-p 'ft2232_spi:type=2232H,port=A' --read gl-ar750.dump
Часто flashrom сам распознает чип и указывать параметр -c
не требуется. Однако в данном случае у программы есть несколько версий касаемо того, что это может быть за чип, поэтому мы должны указать его явно.
Если все было сделано правильно, данные с чипа будут сохранены в файле. Можно заметить, что дамп через FT2232 создается намного быстрее, чем через Bus Pirate или HydraBus.
Распаковываем файловую систему
Распаковать полученный образа нам поможет утилита binwalk, написанная небезызвестным @devttys0. Утилита имеет ряд зависимостей, которые обычно нужно устанавливать отдельно. В Arch Linux нам понадобятся следующие пакеты:
mtd-utils gzip bzip2 tar arj lhasa p7zip cabextract sleuthkit lzop
Пример использования:
DECIMAL HEX DESCRIPTION
--------------------------------------------------------------------
166352 0x289D0 U-Boot version string, "U-Boot 1.1.4-gbeb0f336 ...
166779 0x28B7B HTML document header
167746 0x28F42 HTML document footer
168631 0x292B7 HTML document header
169597 0x2967D HTML document footer
169699 0x296E3 HTML document header
171455 0x29DBF HTML document footer
171555 0x29E23 HTML document header
172238 0x2A0CE HTML document footer
172339 0x2A133 HTML document header
173214 0x2A49E HTML document footer
173327 0x2A50F HTML document header
173713 0x2A691 HTML document footer
173728 0x2A6A0 CRC32 polynomial table, big endian
393216 0x60000 uImage header, header size: 64 bytes, header CR...
393280 0x60040 LZMA compressed data, properties: 0x6D, diction...
1769472 0x1B0000 Squashfs filesystem, little endian, version 4.0...
4521984 0x450000 JFFS2 filesystem, big endian
Как видите, программа ищет в файле известные сигнатуры и выводит их смещение. Но это еще не все. Говорим:
В результате будут автоматически извлечены и распакованы все найденные данные. В том числе, будут извлечены все файлы из найденных файловых систем. Теперь мы можем прочитать все конфиги или воспользоваться каким-нибудь Radare2, чтобы дизассемблировать исполняемые файлы:
-- Sharing your latest session to Facebook ...
[0x00403940]> aa
[x] Analyze all flags starting with sym. and entry0 (aa)
[0x00403940]> pdf
;-- _ftext:
/ (fcn) entry0 8
| entry0 ();
| 0x00403940 04110004 bal fcn.00403954
\ 0x00403944 0000f025 move fp, zero
[0x00403940]> s fcn.00403954
[0x00403954]> pd 8
/ (fcn) fcn.00403954 232
| fcn.00403954 (int arg2);
| ; var int local_1ch @ sp+0x1c
| ; var int local_20h @ sp+0x20
| ; var int local_24h @ sp+0x24
| ; var int local_28h @ sp+0x28
| ; var int local_2ch @ sp+0x2c
| ; arg int arg2 @ a1
| ; CALL XREF from entry0 (0x403940)
| 0x00403954 8ffc0000 lw gp, (ra)
| 0x00403958 03fce023 subu gp, ra, gp
| ;-- imp.opendir:
| 0x0040395c 03a02025 move a0, sp
| 0x00403960 8fe50008 lw a1, 8(ra)
| 0x00403964 00bc2821 addu a1, a1, gp ; arg2
| 0x00403968 8ff90004 lw t9, 4(ra)
| ;-- imp.ctime:
| 0x0040396c 033cc821 addu t9, t9, gp
| 0x00403970 2401fff8 addiu at, zero, -8
...
К счастью для нас, в мире встраиваемых систем какие-то антиотладочные приемы в программах встречаются редко.
Впрочем, часто все бывает не так просто. Например, после обновления устройства во flash-памяти может остаться какой-то мусор, который binwalk примет, к примеру, за начало файловой системы. Такой мусор может потребоваться забить нулями:
[0x00000000]> s 0x12345
[0x00012345]> px
... какой-то hex dump ...
[0x00012345]> w0 16
[0x00012345]> px
... проверяем, что все правильно ...
[0x00012345]> q
Часто производители роутеров как-то модифицируют существующие файловые системы. Например, добавляют в них способ сжатия, не поддерживаемый в апстриме. В этом случае вам могут помочь sasquatch и firmware-mod-kit (см unsquashfs_all.sh). Многие производители, включая D-Link, MikroTik, и других, выкладывают свои модификации в соответствии с требованием лицензии GPL, хотя и не слишком это афишируют. Если вы видите, что в роутере используется модифицированная SquashFS, и беглое гугление не находит ее модификации, просто напишите в техподдержку! Попросите выслать интересующие вас исходники с целью ревью и во благо opensource движения. Если будут упрямится, поясните, что GPL обязывает это сделать. Это, конечно, займет некоторое время, но обычно бизнес предпочитает не рисковать быть затянутым в судебные разбирательства, и в итоге высылает этот десяток измененных файлов.
А если не распаковывается?
В общем случае, если перед вами находится бинарный файл неизвестного формата, помните, что такие утилиты, как hexdump и strings — ваши друзья. Если вы можете модифицировать файл (менять настройки роутера, создавать на нем файлы, ставить обновления), попробуйте сделать несколько модификацией и посмотрите, какие байтики при этом изменяются. В Radare2 для этого есть утилита radiff2, а у binwalk есть ключи --hexdump
и --red
. Если вы хотите поискать конкретные сигнатуры в файле, воспользуйтесь rafind2 из все того же Radare2.
При помощи сервиса binvis.io можно построить визуальную «карту» файла. С этой картой можно найти участки, которые, вероятно, сжаты или зашифрованы, повторяющиеся паттерны, и так далее. Если вы не хотите загружать файл на сторонний сервис, можно воспользоваться таким скриптом:
# vim: set ai et ts=4 sw=4:
import os
import sys
import subprocess
import tempfile
def run(cmd):
code = subprocess.call(cmd, shell=True)
if code != 0:
print("Command `%s` returned non-zero status: %d" %
(cmd, code))
sys.exit(1)
if len(sys.argv) < 2:
print("Usage: " + sys.argv[0] + " <infile>")
sys.exit(1)
infile = sys.argv[1]
outfile = tempfile.gettempdir() + "/temp.pgm";
fsize = os.path.getsize(infile)
run('(echo "P5 512 %d 255"; cat %s) > %s' %
(fsize / 512, infile, outfile))
run('gimp %s' % (outfile))
Он позволяет просмотреть заданный бинарный файл в Gimp. У меня в системе скрипт называется showbin.
Анализ бинарного файла неизвестной структуры — безусловно, сложная задача, но не нерешаемая. Все определяется исключительно вашим интересом к этой задаче и желанием инвестировать в нее свое время. Пример такого анализа хорошо описан в серии статей Reversing ESP8266 Firmware.
Заключение
Ссылки по теме:
- r0 Crew — русскоязычный форум, посвященный реверс-инжинирингу. Также в Telegram’е есть канал и чат (вход через бота);
- Hardware & Radio — еще один Telegram-чат, про железо, радио, встраиваемые системы и всякое такое;
- Unnamed Reverse Engineering Podcast — тематический подкаст;
- Еще на тему реверсинга есть StackExchange и сабредит;
Как видите, все оказалось сравнительно несложно. Ковыряние роутеров, всякого IoT и прочего железа может быть весьма затягивающим занятием. Практикуя его, постоянно узнаешь что-то новое.
Метки: Безопасность, Отладка, Реверсинг, Электроника.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.