Взлом умных зданий. Вторгаемся в сеть, управляем узлами, DoS’им и проникаем в тайники
Эксплойт
В прошлой статье мы рассказали тебе про устройство BMS и хорошенько распалили твое воображение будущими хакерскими экспериментами. И вот их час пробил! Готовься, дружище!
Устройство сети
Перед тем как наАхчать охоту на стадо газелей, любой порядочный лев должен определиться — а сколько же там особей? Хватит ли их, чтобы накормить целый прайд?
Как видишь:

Число газелей узлов в сети KNX может достигать 65 535. Поверь, это много, тем более что под узлом может подразумеваться как простой выключатель, так и полноценный MCU/PLC, который «рулит комфортом» в целом номере отеля.
Как видно из рисунка, в стаде у нас царит шинная топология. На одной шине (sub-line) может находиться до 255 узлов, на одной линии (mainline) может быть до 15 шин (sub-line). Одна зона (area) включает в себя до 15 линий (mainline). Всего может быть 15 зон (area). Такое разделение позволяет неплохо разбивать как на логические (управляем светом), так и на физические группы (система автоматики отвечает за конкретный этаж). Напрашивается знакомая всем фраза «разделяй и властвуй». Ведь все это многообразие (в этот момент автор попросил включить «злобный смех за кадром». — Прим. ред.) можно сломать буквально парой команд.
Мир KNX расширяется
Управление спортивным, наружным и внутренним освещением на стадионе «Лужники»теперь реализовано с помощью KNX. Он же будет применяться в строящемся небоскребе «Лахта центр» в Санкт-Петербурге.

Для начала посмотрим, что собой представляет формат сообщений/телеграмм по шине KNX-TP. Вот так выглядит общий формат сообщения в KNX.

Теперь пройдусь по каждому полю и добавлю пару комментариев.
## Control byte/Control Field

Отвечает за то, будет ли данное сообщение системным или нет. Если обычное, то будет указан его приоритет.
## Source address

Вот так оно выглядит, детальные комментарии тут, думаю, излишни.
## Receiver address

Вот так уже выглядит формат адреса получателя. Та или иная реализация зависит от того, что решил интегратор, когда устанавливал систему. И как видим, 16 бит определяют/подтверждают то, что емкость сети равна 2^16 :).
Кстати, очень хочется сказать «спасибо» документации — тому, что она есть и что ее много. Наверное, ты спросишь, почему слово спасибо я заключил в кавычки и почему заострил на нем внимание? А посмотри-ка на это изображение:

В одном месте документация нам говорит, что receiver address следует трактовать как групповой, если некий бит DAF = 1, а парой страниц ниже идет уже обратная информация — что receiver address нужно трактовать как групповой, когда DAF = 0.
А вот дальше поля уже пойдут интереснее и трактоваться они будут в зависимости от того, что записано в предшествующих битиках.
## NPCI

Этот байт отвечает за длину данных, передаваемых по KNX-TP. Увы, не так много — максимум мы можем передать до 15 байт полезной нагрузки. Поле Routing counter отвечает за количество «прыжков/переходов» через шлюзы и gateway. Тем самым реализована борьба против зацикливания сообщений и broadcast-штормов. Идея хорошая и здравая. Но иногда можно не обращать внимания на это поле, смело ставить значение в 0, и все будет работать как надо :). Об этом чуть позже. Ну и старший бит, о котором уже немного упоминали, — бит DAF, отвечающий за то, как трактовать поле receiver address — как групповой адрес или как индивидуальный.
## TPCI/APCI

Поле TPCI проясняет вопрос, данные ли это или служебная информация. Sequence number в дополнение к TCPI, если у нас диалог между узлами не ограничивается одной телеграммой, а надо несколько телеграмм. Угадай, из какого количества телеграмм может состоять «диалог» между узлами? Если ты подумал, что максимум может быть 16 фреймов, то нет. Их может быть сколько угодно, просто когда значение sequence number дойдет до 15, следующее значение будет равно 0 и все пойдет сначала, инкрементируясь до 15.
И на десерт — поле APCI. Вот оно-то как раз зачастую и определяет смысловую нагрузку сообщений. Разумеется, существуют определенные коды для того или иного сообщения:

Как можно заметить, меня сразу заинтересовали следующие команды: MemoryRead, MemoryWrite, UserMessage. Еще одна забавная команда Escape расширяет APCI кода на еще некоторое количество дополнительных команд.
Вот так, шаг за шагом и поле за полем, разобрали фрейм KNX, который передается по KNX-TP (для тех, кто забыл, что это такое, — всего лишь пара проводов, которые подходят к узлу, его запитывают и служат для передачи данных). Немного терпения, дорогой читатель, ибо хочу еще несколько слов сказать о том, во что оборачивается телеграмма KNX-TP, когда она попадает через шлюз в Ethernet.
Когда KNX-TP попадает в Ethernet, эта телеграмма передается по UDP с помощью cEMI message. Вот как выглядит фрейм KNX в Ethernet-сети:

А вот как выглядит cEMI message:

Почему тут поставил восклицательный знак и обвел одно поле? Помнишь, я говорил, что иногда можно не обращать внимания на routing counter в поле NPCI? Это как раз тот случай: у нас вклинилось еще одно поле control field 2, которое и определяет количество hop’ов, и у нас идет групповое или индивидуальное сообщение. Но это применимо, только если ты общаешься с KNX-сетью с помощью IP-роутера.
Вся информация предоставлена исключительно в ознакомительных целях. Ни канал, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи.
Что ломаем?
В нашей компании мы решили ломать в первую очередь IP-роутеры. Надеюсь, следующая картинка пояснит наш выбор.

На этой картинке представлен типовой кусок KNX-сети. Имеются различные датчики (sensors) и актуаторы (actuators, исполнительные механизмы). Все это сидит на шине, которая через Line Couple и/или IP-роутер может объединяться в mainline, которая, в свою очередь, подключена к Ethernet. К Ethernet уже могут быть подключены различные инженерные системы, завязка на корпоративную сеть, ну и далее, надеюсь, читатель, ты понимаешь, к чему я клоню. Причем изначально выбираю very hard level, когда рассматриваю сценарий «заселился в отель, расковырял выключатель и подключился к KNX-TP».
Делаем тестовый стенд
Чтобы сделать проект на KNX, надо потратить пару тысяч долларов для покупки софта ETS5. Далее, приехав на объект, перед тем как конфигурировать узел, нужно включить режим программирования этого узла.

Для этого надо нажать маленькую кнопочку на самой железке — загорится лампочка, означающая, что узел можно конфигурировать. Да-да, ты не ошибся: нажать кнопку на железке. А для этого может понадобиться сбегать на чердак соседнего здания, где железка установлена :). Ну и после этой небольшой разминки, как истинный интегратор, сесть за ноут, запустить ETS и произвести конфигурацию узла, чтобы у нас полноценно заработала BMS. В нашем же случае мы немного «упростили задачу» и собрали маленькую сеточку у себя в офисе.

Итак, у нас имеется IP-роутер Gira, который подключен через Ethernet к ETS5. Smart button — кнопочка, то есть sensor, и dimmer, то есть actuator. Ну и еще другой IP-роутер, который используется для сценария «подключились к выключателю, хакаем BMS».
Вообще говоря, когда начинаешь делать research того, что еще мало изучено, у тебя может не оказаться необходимого инструмента. Так случилось и с нами. Простой пример — у нас не было трансивера KNX-TP, чтобы подключиться напрямую в линию KNX-TP, для этого использовали IP-роутер. А как смотреть трафик в Ethernet? Слышу-слышу нестройный хор голосов, скандирующих: Wireshark!
Vox populi не обманывает, но на тот момент Wireshark не знал, что такое KNXnet/IP, KNX-TP, cEMI message. Он просто показывал кучу бинарщины, которая передается в UDP. И выглядело это как-то так:

Поэтому была написана простенькая утилитка, которая всю эту бинарщину разбирала по полям и выдавала комментарии/пояснения, помогающие понять data flow. Теперь это стало выглядеть приличнее:

Следующая вещь, которую тоже пришлось писать, — поиск узлов на линии. Ибо та же ETS5, что стоит кучу баксов, даже на простом сегменте из трех-пяти устройств не всегда могла отобразить все пять устройств, которые есть на линии. Глюк, видимо, связан с тем, что, когда в ETS5 нажимаешь кнопку «сканирование линии», она тупо шлет 255 запросов с наименьшим приоритетом по адресам x.y.0 — x.y.255 и потом пытается поймать ответы от устройств. Поэтому в своей утилите при поиске устройств я поднял приоритет запросов на более высокий и пошел по принципу: «пинганули устройство, подождали чуть-чуть ответ». Если ответа нет, значит, нет и устройства с этим адресом на линии. Да, получается чуть дольше, но зато гарантированно находит все устройства на линии.
Результат неплохой. Отсканировав линию, можно понять, какие устройства на ней есть. Помимо этого, устройства присылают так называемую mask version, состоящую из 2 байт:

И уже по этой mask version можно примерно понять, насколько интересно нам то или иное устройство, и спрогнозировать, по какому адресу находится IP-роутер, является ли узел «простой кнопкой», или есть подозрение, что это более сложный узел (соответственно, имеющий большую вычислительную мощность).
DoS-атака
Как только мы поняли, как происходит общение между узлами в сети KNX, мы тут же захотели что-нибудь задосить :). Первым делом мы атаковали один узел, но оказалось, что можно положить и весь сегмент целиком. Вроде бы не так страшно, но представь себе владельца отеля, к которому выстроилась очередь из жильцов, жалующихся, что нет света… ну или ситуацию, когда пора включать игровое освещение на стадионе, а оно не включается.
Помимо этого, в KNX нет механизма защиты от дублирования сообщения. То есть если ты перехватил KNX-телеграмму (ну например, так получилось, что KNX «бегает» через публичный Wi-Fi в гостинице) и ее повторно отправил, то можешь побаловаться и повключать/выключать свет в соседнем номере :). Нет, я не шучу — Jesus Molina в своей работе Learn how to control every room at a luxury hotel remotely: the dangers of in secure home automation deployment описывает этот сценарий со своим собственным участием.
Кстати, помнишь, как в первой части статьи я обращал твое внимание на решение по контролю доступа от ABB для гостиниц? Что они предлагают:

Здесь мы видим, что система контроля доступа завязана на шину KNX. И у меня возникает вопрос: сможем ли мы открыть дверь в соседний номер, отправив нужную команду по KNX-TP? Конкретно эта проблема находится за пределами сегодняшнего исследования, но скорее всего, сможем!
Итак, свет выключили, светом поморгали, двери пооткрывали — пора двигаться дальше, в сторону похищения корпоративных секретов. На пути к этой темной цели может оказаться (и наверняка окажется) тот или иной роутер/шлюз, который будет обеспечивать сегментирование сети. Кроме этого, предположим, что интегратор немного разбирается в вопросах безопасности и сделал сегментирование сети, настроил, какие KNX-сообщения могут проходить сквозь шлюз, а какие будут отбрасываться, чтобы не засорять лишними сообщениями mainline.
Тухло, не так ли? Вовсе нет :). Расходиться по домам еще рано.
Помнишь, когда я говорил про APCI-команды, меня там заинтересовали команды MemoryRead, MemoryWrite, UserMessage? Так вот, зная, как строятся сообщения в KNX-сети, я решил расширить свою утилитку командой «прочитай-ка память». И, наблюдая попутно за data flow в KNX-сети, заметил, что перед тем, как общаться с тем или иным узлом, применяется еще авторизация. Да и документация гласила: для повышения безопасности в KNX стоит использовать BCU-ключ (другое название key authorization). Сказано — сделано, фэншуй соблюден («как начинать общаться с узлом»), и… я получаю в ответ кусок бинарных данных. Немного доработав утилиту, я получил полный дамп application memory в пределах от 0x0 до 0xFFFF.

На картинке выше представлен дамп памяти одного из IP-роутеров. Разглядывая всю эту бинарщину, я обнаружил, что мне удалось прочитать конфиг роутера, который содержит его IP-адрес, маску подсети, шлюз, friendlyname. Помимо этого, можно понять, используется ли «стандартный» IP:port (224.0.23.12:3671) для broadcast или какой-то кастомный. И самое интересное — можно найти «заветные» байты (на картинке выше обведены в красный квадратик), которые отвечают за настройки «шлюзования» телеграмм, — то есть можно ли им дальше идти в Ethernet, или роутер должен их отбросить.
О! Уже полдела сделано. Срочно дописываем в нашу утилиту команду «писать память» :). Написали, запустили, получили ответ «ОK» и с замиранием сердца тестируем. Барабанная дробь… нам удалось выбраться в Ethernet и другие сегменты KNX-сети. Просто записав несколько байтов по нужному адресу! Даже не потребовалось бежать на чердак включать режим программирования (который нужен во время конфигурирования сети). Все это я тестировал с роутером от ABB.
Окрыленный успехом, начинаю делать все то же (сначала читать память) с IP-роутером от Gira. И на первом же чтении получаю ошибку и отсутствие какого-либо дампа. Я в непонятках. Как так… с ABB все прошло на ура, тот же скрипт, а Gira молчит как партизан. Смотрю дамп трафика, сравниваю, проверяю — все аналогично с ABB, но результата нет. Начинаю сравнивать по шагам, то есть по пакетам. И вижу, что maskversion приходит, а вот когда отправляю запрос с ключом авторизации, Gira «ругается, дуется и обижается» и заканчивает диалог. Непонятно — мы же читаем память, а это уже достаточно критичная операция.
Почему Gira не «проглатывает» ключик авторизации? Со стороны ETS5 можно спокойно конфигурировать этот роутер, а со стороны KNX-TP затык. Что за ерунда, что не так? Решаю переписать скрипт, убрать авторизацию. Свожу скрипт к диалогу «дай кусок памяти по такому-то адресу» вместо всяких долгих приветствий и реверансов. И… при таком раскладе Gira мне отвечает на запрос и отдает кусок памяти. Аналогично упрощаю утилиту по записи в память. Результат положительный. Записано с первого раза без каких-либо проблем.
Возможно, я чего-то не понимаю в этом мире. Ты только вдумайся: мы можем читать и писать в память IP-роутера без включения режима программирования и без ключа авторизации!
С третьим роутером, который у нас был (а это Siemens), аналогичная ситуация — чтение и запись проходят без проблем. В результате мы можем выйти на другие mainline и area и там устроить все те же самые безобразия (управлять и досить).
Теперь еще небольшая вкусняшка. Помнишь команду UserMessage? И поле length, которое говорит, что размер макета KNX-TP может быть не более 15 байт? Помня о том, как вендоры соблюдают стандарты, решил попробовать отправить с помощью UserMessage больше чем 15 байт полезной нагрузки. В общем, выяснилось, что некоторые IP-роутеры могут пропустить в одной телеграмме значительно больше бинарных данных. Количественная характеристика равна 69 байтам. То есть мы можем по KNX-TP отправить сообщение с payload, равным 69 (без учета source or destination address и прочих служебных полей) байт, и IP-роутер его прекрасно ретранслирует в Ethernet. А если пошлем больше, то у Gira, по ходу, будет намечаться переполнение буфера, так как, помимо этих 69 байт, он отправит в Ethernet еще пару десятков байт в этом сообщении.
Тут хочу сделать одно небольшое отступление. В ходе этого исследования попадались роутеры, чувствительные к «правильному» Ethernet-фрейму, который равен 60 байт. Ибо некоторые пакеты получались равными 59 байт, и роутер их не понимал. Не хватало такой штуки, как padding. (О том, что такое padding, можно прочитать, к примеру, тут.)
Как защититься и интересен ли кому-нибудь багрепорт?
Теперь пару слов про BCU-ключ (key authorization). В таком замечательном документе, как Serial Data Transmission and KNX Protocol by KNX Association, говорится: These services allow to write keys for the various access levels to bus devices with memory access protection (16 levels are possible, 0 is the highest level and 15 is the lowest).
О! Класс. Можно поставить ключик защиты и усложнить жизнь плохим парням, которые захотят поломать BMS. Сказано — сделано. Написан очередной скрипт, который позволяет установить ключ авторизации. Хочу заметить, что штатные средства ETS5 этого вообще не позволяют. Даже упоминания нет.
Решил проверить это на IP-роутере от ABB. Поставил новый ключ авторизации, роутер рапортует, что ключ сменился. Для чистоты эксперимента перегружаю по питанию. И… он позволяет прочитать и записать память с дефолтным ключом. Повторно ставлю новый ключ авторизации, получаю подтверждение, что ключ установлен. Без какой-либо перезагрузки пробую прочитать память со старым ключом — снова удается.
Пишу в представительство ABB (как русское, так и европейское), что есть такой роутер, есть возможность со стороны TP поменять полностью весь конфиг, и данный роутер не позволяет установить BCU-ключ. На что получаю ответ от российского представительства: мол, «это старая железка, скоро выйдет новая версия, используйте ее». Европейское представительство дало более развернутый ответ:
«Спасибо за интерес к нашей продукции. Мы понимаем вашу обеспокоенность, но не волнуйтесь. Так как стандарт не обязывает использование BCU-ключа, то мы посчитали, что реализовывать данную функциональность будет лишним. Кроме того, пользователь может посчитать это достаточной мерой безопасности и будет введен в заблуждение. Да и вообще — используйте безопасные IP-сети».
Примерно таковы по смыслу были ответы.
Как промежуточное резюме — на данный момент имеется возможность, подключившись в любом месте к KNX-сети, устроить DoS, управлять любыми устройствами, менять конфигурацию любых узлов с помощью команды WriteMemory. При этом не всегда требуется BCU-ключ, нет необходимости включать «режим программирования», да и IP-роутеры, призванные служить для разграничения трафика, как показывает практика, не будут сильной помехой.
«You need to go deeper»
Во время исследования я натыкался на разные документации к роутерам, да и KNX-сети в целом. И не покидало меня чувство, что есть еще что-то «вкусное да интересное». Чувство это подкреплялось тем, что встречались datasheet, которые говорили: «мол, не бойтесь окирпичить устройство, если что — зальете прошивку через KNX-TP и его восстановите». Да еще на столе лежит IP-шлюз Jung, который не работает как надо. Пишу в саппорт Jung с вопросом: есть IP-шлюз, в ETS5 видится, конфигурится, но вот в Ethernet ничего не отправляется. Приходит ответ: «А… Так это известная проблема, вот вам user guide, вот вам софтина, вот вам прошивка, обновите, и все будет пучком».
Интересно… Решил выполнить их инструкции — вдруг заработает? Да дамп обновления сохраню на всякий случай. Обновил прошивку Jung — работает как следует. Смотрю попутно дамп обновления прошивки, нахожу еще интересные APCI-коды: A_UserMemory_Read, A_UserMemory_Write. Заодно подворачивается ссылка на KNXmap товарища Niklaus’a, где встречаются эти же самые коды. Начинают закрадываться подозрения. Допиливаю утилиту до возможности read firmware. Запускаю команду «читать прошивку» для Jung, подключившись к нему со стороны KNX-TP. Получаю кусок бинарных данных. Открываю прошивку, которую только что залили на этот шлюз. Бинго: оказывается, read firmware мне вернула не просто кусок бинаря, а часть прошивки, которая крутится на железке. А раз можно читать прошивку, значит, таким же способом можно ее и записать.
Погодите секундочку… я только что сказал «записать прошивку»? Да, все верно. Найден механизм, как можно, подключившись к KNX-шине в произвольном месте, найти узел, который смотрит в Ethernet, и обновить его прошивку. Можно обновить на кастомную прошивку, чтобы простой шлюз превратить во что-то более серьезное, то есть пробросить подключение до технологической сети. Да, скорость будет не особо высокая для доступа к этой технологической сети, но не забываем, что это мы сделали из публичного места. Усыплять охрану, спиливать замки и лазить по пожарным лестницам не надо!
Как и всякий порядочный вайтхет, я написал в представительство Jung и доложил: «Ребята, можно обновить прошивку со стороны KNX-TP, ключ авторизации не нужен, режим программирования не нужен, могу менять настройки — IP, маску, gateway». Ответа никакого нет.
Десерт для терпеливых, или выходим за пределы лаборатории
Скептики скажут: вы тестили KNX-сеть у себя в офисе, в лабораторных, так сказать, условиях и наверняка что-нибудь подстроили и упростили. Чтобы исключить подобные намеки, нам удалось договориться с одними товарищами протестировать созданный тулкит на живом объекте, в реальных условиях. Я приехал к ним на объект, подключился к выключателю. Послушал трафик, просканировал узлы. И начал им рассказывать, какой примерно data flow, какие и сколько узлов у них установлены. Выдал их serial/hardware number, сказал, какие приблизительно конфигурации залиты по узлам. Некоторое удивление читалось на лицах :).
Под капотом у роутеров
В качестве дополнительной информации расскажу тебе о том, что внутри у роутеров, которые мы закупили для исследования.
ABB

На картинке выше показано, как выглядит снаружи и изнутри данный роутер. Сердце и мозг тут два ATmega128. Структурная схема примерно следующая:

Одна ATmega связана с трансивером шиной KNX. Другая ATmega отвечает за связь по Ethernet. Общение происходит через SRAM.
Было бы странным не сказать пару слов о том, как можно обновить прошивку на данном роутере. Для этого надо выполнить несколько «очень сложных» шагов:
- подключиться Ethernet’ом к данному KNX-роутеру;
- запустить фирменную утилиту ABB i-bus IP Firmware Tool;
- выбрать нужную железку и нажать кнопочку «Обновить прошивку».
Всё. Совсем всё: никакой проверки на целостность, подлинность и вообще чего-либо нет.
Gira

В этом IP-роутере фарша уже побольше:

Есть два Ethernet + SD-карта для сохранения логов. Кроме того, используется ARM. И прошивка уже выглядит не просто как бинарь, а как маленький полноценный Linux :). У которого ко всему прочему есть SSH и GDBserver. Обновляется прошивка так же, как в ABB: подключились и залили прошивку. Правда, тут уже есть «проверка». Gira позволяет залить только последнюю версию прошивки. Но в документации можно встретить вот такую «вкусняшку»:

Значит, у предыдущего поколения роутеров можно обновить прошивку через twisted pair, то есть через KNX-TP.
Siemens

Как видно, имеется вполне приличная вычислительная мощь для такой железки.
Ну и естественно, пара слов о том, как обновить прошивку. Тут все сделано похитрее. Чтобы перевести этот роутер в режим обновления прошивки, понадобятся танцы с бубном: выключить/включить устройство, зажать сервисную кнопку, ждать, созерцая огоньки, пока световая индикация не скажет о том, что устройство перешло в режим ожидания новой прошивки. Далее запускаем фирменную утилиту от Siemens по обновлению прошивки и обновляемся.
Проверка происходит на канальном уровне.
Хост отправил 1048 байт. Роутер шлет обратно эти же байты. Хост смотрит, если пришли те же самые байты — значит, процесс обновления прошивки идет хорошо.
Для внедрения в сеть первоначально использовался IP-роутер от ABB. Нам было этого мало, мы его разобрали, стали использовать бинарный трансивер в связке с Teensy. А сейчас мы разрабатываем полностью свою железку, которая позволит подключиться к KNX-сети и найти узлы, у которых можно обновить прошивку со стороны KNX-TP, выдать список узлов, не поддерживающих BCU-ключ, а те, что поддерживают, — изменить с дефолтного на произвольный.
Заключение
Что в итоге мы имеем в сухом остатке? Подключившись в любом месте к KNX-шине, можно устроить DoS-атаку как на конкретное устройство, так и на шину целиком. Можно управлять любым оборудованием, которое подключено по KNX. Можно беспрепятственно проникать в другие сегменты сети. Читать и изменять конфигурации узлов. И как «вишенка на торте» — возможность прочитать и записать прошивку узлов. Причем в ряде случаев эти узлы могут быть подключены к Ethernet.