Реверс-инжиниринг роутера на примере 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 имел два потайных винтика с обычной крестовой головкой, которые находились под резиновыми ножками. После их удаления корпус легко открывается спаджером.

Изучаем внутренности

Внутри мы видим следующее:

Печатная плата GL.iNet GL-AR750

И с обратной стороны:

Печатная плата 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. Чип довольно популярный, его можно встретить во многих роутерах. Даташит для него был найден здесь. Вообще, архитектура 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 бод, мы увидим:

U-Boot 1.1.4-gbeb0f336 (Oct 25 2017 - 14:12:34)
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:

root@GL-AR750:/# id
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 update
...
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:~# opkg install python3-light
...
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:

iroot@GL-AR750:~# cat /sys/kernel/debug/gpio
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:~# rmmod gpio_button_hotplug
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 вы видите ошибку:

ash: write error: Resource busy

…, значит ресурс занят каким-то драйвером. К сожалению, в 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, вот такой:

Плата на базе 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;

Где какие пины находятся, вы узнаете из даташита чипа. Если даташита вдруг нет, с помощью осциллографа или логического анализатора нетрудно определить, какие пины для чего используются. Часто они расположены таким образом:

Типичное расположение пинов у чипов SPI flash

Однако вы можете помнить, что у какого-нибудь AT45DB161E пины расположены совершенно иначе. И вообще, память может быть в другом корпусе, например SOIC-16. Или же она может не иметь интерфейса SPI, и тогда это совершенно другой разговор. Однако SPI flash в SOIC-8 с расположением пинов, как показано выше, встречается очень часто.

Итак, в предположении, что пины были успешно найдены, выполняем следующую команду:

flashrom -c 'MX25L12835F/MX25L12845E/MX25L12865E' \
  -p 'ft2232_spi:type=2232H,port=A' --read gl-ar750.dump

Часто flashrom сам распознает чип и указывать параметр -c не требуется. Однако в данном случае у программы есть несколько версий касаемо того, что это может быть за чип, поэтому мы должны указать его явно.

Если все было сделано правильно, данные с чипа будут сохранены в файле. Можно заметить, что дамп через FT2232 создается намного быстрее, чем через Bus Pirate или HydraBus.

Распаковываем файловую систему

Распаковать полученный образа нам поможет утилита binwalk, написанная небезызвестным @devttys0. Утилита имеет ряд зависимостей, которые обычно нужно устанавливать отдельно. В Arch Linux нам понадобятся следующие пакеты:

sudo yaourt -S binwalk-git squashfs-tools sasquatch jefferson-git \
  mtd-utils gzip bzip2 tar arj lhasa p7zip cabextract sleuthkit lzop

Пример использования:

$ binwalk gl-ar750.dump

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

Как видите, программа ищет в файле известные сигнатуры и выводит их смещение. Но это еще не все. Говорим:

$ binwalk -e gl-ar750.dump

В результате будут автоматически извлечены и распакованы все найденные данные. В том числе, будут извлечены все файлы из найденных файловых систем. Теперь мы можем прочитать все конфиги или воспользоваться каким-нибудь Radare2, чтобы дизассемблировать исполняемые файлы:

$ r2 -a mips -b 32 _gl-ar750.dump.extracted/squashfs-root/bin/busybox
 -- 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 примет, к примеру, за начало файловой системы. Такой мусор может потребоваться забить нулями:

$ r2 -w some_file.dat
[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 можно построить визуальную «карту» файла. С этой картой можно найти участки, которые, вероятно, сжаты или зашифрованы, повторяющиеся паттерны, и так далее. Если вы не хотите загружать файл на сторонний сервис, можно воспользоваться таким скриптом:

#!/usr/bin/env python3
# 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.

Дополнение: В комментариях Delimitry подсказал еще одну программу, Veles. Это довольно удобный hex-редактор с рядом интересных возможностей, в том числе визуализацией данных. Программа является полностью открытой (лицензия Apache 2.0).

Анализ бинарного файла неизвестной структуры — безусловно, сложная задача, но не нерешаемая. Все определяется исключительно вашей заинтересованностью в этой задаче и желанием инвестировать в нее свое время. Пример такого анализа хорошо описан в серии статей Reversing ESP8266 Firmware на boredpentester.com.

Выводы

Как видите, все оказалось сравнительно несложно. Ковыряние роутеров, всякого IoT и прочего железа может быть весьма затягивающим занятием. Практикуя его, постоянно узнаешь что-то новое.

Отмечу, что не следует пренебрегать поисковыми системами и стеснятся попросить помощи на тематическом форуме или в чате. В частности, можно порекомендовать следующие онлайн-ресурсы:

  • r0 Crew — русскоязычный форум, посвященный реверс-инжинирингу. Также в Telegram’е есть канал и чат (вход через бота);
  • Hardware & Radio — еще один Telegram-чат, про железо, радио, встраиваемые системы, их реверсинг и всякое такое;
  • WikiDevi — вики-сайт с кучей полезной информации про разные железки;
  • Unnamed Reverse Engineering Podcast — тематический подкаст;
  • Еще на тему реверсинга есть StackExchange и сабредит;

А доводилось ли вам исследовать роутеры или иное железо? Если да, то какое, и какие инструменты вы для этого вы использовали?

Метки: , , , .

Понравился пост? Узнайте, как можно поддержать развитие этого блога.

Также подпишитесь на RSS, Facebook, ВКонтакте, Twitter или Telegram.