Rubber Ducky своими руками

BadUSB Rubber Ducky своими руками

Есть такой вектор атак, как BadUSB, — его суть заключается в эмуляции работы клавиатуры и выполнении операций на компьютере под видом обычного ввода от пользователя. Клавиатура обычно не вызывает подозрений у ОС и антивируса, поэтому такие атаки сложно отследить. Сегодня мы посмотрим, как создать свой девайс этого класса — в корпусе флешки и с беспроводной связью.

Еще по теме: BadUSB своими руками

Как ты понимаешь, за годы существования проблемы способов реализации придумано уже достаточно много. Это может быть как классический, хорошо всем известный Rubber Ducky, так и весьма экзотический вариант с перепрошивкой флешки с подходящим контроллером. Также народ придумал некоторое количество реализаций на Arduino и совместимом Digispark.

Кроме того, однозначно стоит упомянуть и о Pill Duck, так как своей концепцией именно этот проект наиболее близок к тому, что я покажу в статье. У Pill Duck есть хорошее и подробное описание, так что всячески рекомендую тебе ознакомиться с ним, если ты настроен в деталях разобраться в проблеме.

Сразу скажу, что я не ставил перед собой цель превзойти упомянутые устройства. Скорее это мой личный эксперимент на тему дистанционного пульта управления для компьютера, так что оценивать его стоит в первую очередь именно с такой точки зрения.

USB HID

USB (Universal Serial Bus), как ясно из названия, представляет собой универсальную последовательную шину, которая де-факто является стандартом в настоящее время (вернее, даже целым семейством стандартов). Она практически полностью заменила собой RS-232, LPT, PS/2 и используется преимущественно для связи ПК с периферийными устройствами.

Следует заметить, что рабочие места для наиболее ответственных задач до сих пор оснащаются средствами ввода с интерфейсами PS/2. Это как раз связано с проблемой обеспечения безопасности подобных систем. Так что отправляться на штурм какой-нибудь условной АЭС со своей Rubber Ducky на USB — занятие не только глупое, но и заранее обреченное на провал.

Однако из основных достоинств протокола USB вытекают и его недостатки. В первую очередь это сложная процедура обмена информацией между девайсами, особенно в начальный момент. Причина проблемы заключается в использованной концепции Plug’n’play, которая подразумевает, что периферия при подключении сразу же инициализируется. Ведомое устройство передает хосту информацию о себе, что позволяет системе подгрузить нужный драйвер и приступить к работе.

С точки зрения конечного пользователя, безусловно, это очень круто, однако как раз из-за универсальности спецификации USB составляют несколько многостраничных томов. К счастью, наша задача — эмуляция клавиатуры и мыши — достаточно простая и распространенная, что несколько облегчает жизнь.

Итак, интересующие нас устройства относятся к классу HID (Human Interface Device), и если мы сообщим хосту, что его новая периферия — это стандартная клавиатура, то установка специальных драйверов не потребуется и будут использованы стандартные. В интернете есть неплохие статьи о кастомном HID-устройстве, но это не совсем наш случай.

Тебе нужно запомнить следующее: обмен данными в протоколе USB всегда инициируется хостом и происходит пакетами. Их размер описан в дескрипторах девайса, которые хост обязательно запрашивает во время инициализации.

Прошивка МК

Самый простой на сегодня способ собрать собственное устройство с USB — взять подходящий микроконтроллер и написать для него нужную прошивку. Теоретически нам подойдет едва ли не любой МК, ведь USB тоже можно эмулировать средствами GPIO и нужными библиотеками (эмулировать USB для эмуляции HID и «пользовательского ввода» — в этом определенно есть что-то безумно заманчивое). Однако разумнее, конечно же, выбрать микроконтроллер с необходимой нам периферией.

Наиболее известная в мире плата Arduino с такой функциональностью — Leonardo на ATmega32u4. Этот МК уже содержит в своем составе аппаратный блок USB, а Arduino IDE предлагает на выбор несколько скетчей и библиотек (для мыши и клавиатуры). Также подойдет и более мощная версия на ARM — Arduino Due. Но лично мне ближе микроконтроллеры STM32, тем более что некоторый опыт работы с ними уже имеется. Поэтому в основу проекта лег STM32F103C8T6. Очень удобно, что эта микросхема доступна в составе отладочной платы Blue Pill, которая облегчает прототипирование устройства.

Дескрипторы

Для старта возьмем за основу один из примеров libopencm3, в котором эмулируется движение мыши. Наибольший интерес для нас представляет именно дескриптор, вот как он выглядит:

Добрая половина этих параметров стандартна для многих совместимых устройств, так что можешь даже не забивать ими голову. Нас же здесь больше всего интересуют параметры PID (Product ID) и VID (Vendor ID). Изменив их, можно притвориться практически любым устройством любого производителя (правда, есть сомнения в правовом статусе такого притворства, так что подумай дважды).

В дескрипторе конечной точки нас интересуют:

  • ее адрес .bEndpointAddress = 0x81;
  • максимальная длина пакета .wMaxPacketSize = 4;
  • интервал опроса .bInterval = 0x02.

Адрес конечной точки для нашей цели не имеет принципиального значения, его можно не трогать. Что же касается максимального размера пакета, то он обязательно должен соответствовать структуре отчета, описанной в hid_report_descriptor[]. В данном случае это четыре байта.

Завершают определения строки usb_strings[], которые ты тоже можешь прописать по своему вкусу (и чувству юмора).

Рассмотрим теперь подробнее дескриптор отчета. Ответ стандартной мыши на запрос от хоста состоит из четырех байт. Первый передает состояние кнопок (младшие три бита — правая, левая и средняя кнопки, старшие пять бит не задействованы). А оставшиеся три байта отвечают за перемещение по осям X, Y и вращение колесика. Эти байты представляют собой целое число со знаком (диапазон от –127 до 127). Его значения при этом соответствуют единичному относительному перемещению указателя.

Хорошо, с мышью немного разобрались, а что насчет клавиатуры? На самом деле почти все аналогично. Однако теперь отчет длиннее и состоит из восьми байт. Биты первого байта отвечают за клавиши-модификаторы: RIGHT_GUI, RIGHT_ALT, RIGHT_SHIFT, RIGHT_CTRL, LEFT_GUI, LEFT_ALT, LEFT_SHIFT, LEFT_CTRL. Следующий байт зарезервирован для совместимости, в принципе его можно выкинуть. Дальше идут шесть байт, каждый из которых отвечает одной нажатой клавише: такой мультитач на шесть касаний, не считая модификаторов. Дескриптор клавиатуры выглядит следующим образом:

Для упрощения работы с дескрипторами USB есть хороший сайт, который позволяет анализировать и редактировать дескрипторы. Кроме того, существует официально рекомендуемое приложение USB HID Descriptor tool. Оно доступно только в версии для Windows, но и в Wine тоже заведется.

Составное устройство

С устройствами ввода и их дескрипторами мы разобрались. Теперь возникает следующий вопрос: можно ли объединить в одном устройстве и клавиатуру, и мышь? Тут нам на помощь приходит мануал по созданию составных устройств. Достаточно в дескрипторы отчетов для мыши и клавиатуры добавить поле report id, и их можно будет объединить. Теперь ответы нашей периферии станут длиннее на один байт, но хост, читая его значение, будет знать, от какого устройства отчет.

В итоге наш финальный HID-дескриптор выглядит так:

Главное — не забыть поправить максимальную длину отчета устройства, она теперь равна девяти. Сами отчеты окажутся следующими:

Осталось только инициализировать интерфейс. Тут в примере можно ничего не менять, на старте драйвер вызывает функцию hid_set_config, регистрирующую конечную точку 0x81, которую в дальнейшем будет опрашивать наш хост. В ответ он получит указанные выше отчеты. Что же касается функции hid_control_request, то она служит просто заглушкой и в данном случае ни на что не влияет.

Эмуляция клавиатуры

Теперь разберемся с имитацией нажатия клавиши. Для примера возьмем клавишу a с кодом 0x04. Важно обратить внимание, что коды клавиш, выдаваемые клавиатурой, — это вовсе не ASCII, и о раскладке клавиатура тоже ничего не знает, это все происходит уровнем выше. Так как же выглядит нажатие клавиши а? Это два последовательных отчета — первый о нажатии клавиши, а второй о ее отпускании (если забыть про то, что клавишу надо отпустить, выйдет конфуз).

Единственное, о чем стоит опять же помнить: все транзакции инициируются хостом и в случае чего могут быть отложены. Поэтому всегда полезно убедиться, что отчет ушел. Сделать это можно, анализируя значение, возвращаемое usbd_ep_write_packet. Осталось добавить функцию перевода ASCII в keykode, в этом нет ничего сложного. Более того, есть достаточно примеров готовой реализации. Мне понравилась библиотека keycodes Эдуарда Емельянова. Ее я и использовал с минимальными правками.

Теперь, написав две несложные функции, мы получаем возможность набирать строки и прожимать горячие клавиши.

Проверим наш код простым примером:

И вот мы уже можем взаимодействовать с консолью, имитируя пользователя за компьютером. Главное здесь — правильно подобрать задержку, иначе фокус не удастся.

Эмуляция мыши

С мышью будет, с одной стороны, проще — там отчет короче, а с другой стороны, сложнее. Дело в том, что X и Y — это относительные координаты, по сути единичный шаг перемещения (причем максимальная длина в стандартном случае 127 по каждой оси). Если посниффать трафик с обычной мыши, то можно увидеть, что при перемещении она выдает числа в X и Y, пропорциональные скорости движения, а в случае простоя шлет нули. Вот как мы поступим.

Во-первых, напишем функцию для перемещения в точку с относительными координатами, при этом траектория нам не принципиальна, а скорость пусть будет постоянной.

Таким образом, курсор будет двигаться по диагонали, а затем по вертикали или горизонтали, пока не достигнет заданной точки, добавлять сюда алгоритм Брезенхема я посчитал избыточным. Если очень хочется попасть в заданную точку экрана, то это можно сделать с помощью небольшого хака: сначала переходим в условный ноль (левый верхний угол), задавая перемещение заведомо больше разрешения экрана, а уже оттуда двигаемся к нужной точке.

При желании к этой проблеме можно подойти и с другой стороны, реализовав вместе с мышью тачскрин, который выдает абсолютные координаты.

Подведем промежуточный итог: мы научились вводить текст, жать клавиши-модификаторы и двигать курсор, но все-таки чего-то не хватает.

Добавление радиоуправления

Во многих реализациях BadUSB есть один очевидный минус, а именно: они начинают работать автоматически после включения или через заданный промежуток времени. Иногда это удобно, иногда не очень. Куда эффективнее контролировать работу устройства издалека, тогда можно выждать подходящий момент. Такие конструкции тоже известны, и некоторое время назад в журнале даже была статья об утке с Wi-Fi.

Но использовать в своем устройстве ESP12E мне не хотелось по многим причинам. В первую очередь из-за размера, который не укладывался в габариты обычной флешки. А вот NRF24L01 на роль такого радиомодуля подошел прекрасно: достаточная скорость передачи, скромное энергопотребление и, главное, миниатюрный размер.

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

Если коротко, корень проблемы кроется в том, что на просторах китайских онлайновых площадок есть примерно с десяток клонов чипа NRF24L01, причем все они немного разные (и это если сразу исключить откровенный брак). У меня, например, завелся только вариант с переменной длиной пакета, и то не с первого раза. В этом деле мне помог расширенный мануал (PDF).

Собственно, бороться с болячками некачественных клонов лучше всего полной инициализацией, когда явно прописываются значения во всех регистрах, что позволяет исключить влияние некорректных установок по умолчанию. Также есть интересная деталь, о которой упоминают далеко не в каждом руководстве, а если и упоминают, то обычно вскользь. Это команда ACTIVATE(0x50) с параметром 0х73 следом, ее описание есть лишь во второй версии даташита NRF24l01(PDF). Без нее запись в регистры FEATURE и DYNPD не происходит и, соответственно, ничего не заводится. Чтобы до этого докопаться, пришлось перелопатить изрядное количество мануалов и послушать шину SPI анализатором (кстати, в программе Sigrock есть удобный декодер протокола NRF24L01).

В итоге инициализация получилась такой.

После успешной инициализации все работает как часы: и отправка, и прием данных тривиальны. Мы опускаем линию CE интерфейса SPI, переводим модуль в режим передачи, обнуляя младший бит в CONFIG, и записываем передаваемую строку вслед за командой WR_TX_PLOAD. После чего остается несколько раз поднять линию CE на 25 мкс, до тех пор пока буфер для передачи не опустеет.

Прием происходит следующим образом: мы переводим модуль в режим передачи, поднимаем линию CE и ждем низкий уровень на выводе IRQ (EXTI0). После чего проверяем, есть ли принятый пакет, в статусном регистре, выясняем длину пакета и считываем данные с помощью команды RD_PX_PLOAD. В конце остается только не забыть сбросить прерывание.

Разумеется, прием можно выполнить и без прерывания. Надо просто в цикле ждать установку бита RD_RX в статусном регистре. Но с прерыванием, на мой взгляд, удобнее и быстрее. Что же касается адресов устройств, то менять местами адреса RX и TX необязательно, так как передатчик слушает адрес, заданный в TX в канале P0. Это необходимо для приема сигнала ASK. Как бонус получается, что устройства с одинаковыми адресными настройками могут общаться между собой в обе стороны.

Протокол обмена

NRF24L01 не предоставляют никакого высокоуровневого протокола для общения между устройствами. Мы поступим предельно просто: команды будем отсылать строкой текста, в котором приемник попытается найти инструкции с помощью функции strstr(). Если подходящие лексемы не обнаружились, то сразу же передаем принятую строку на эмулятор клавиатуры. Последнее — задел на повышение функциональности в будущем, так как пульт способен принимать команды по UART, что расширяет возможности применения девайса.

Ниже представлены соответствующие функции приема и отправки команд.

Шестнадцатеричные коды здесь — это коды клавиш, считанные по прерыванию с контроллера клавиатуры пульта. В качестве контроллера использована микросхема PCF8574 (расширитель портов ввода-вывода по I2C).

Реализация в железе

Вот так выглядит схема устройства. Слева изображен пульт, справа эмулятор.

Rubber Ducky своими руками

Сначала все было выполнено на макетках. Во время предварительной сборки надо обратить внимание на несколько моментов. Прежде всего, на модуль NRF24 обязательно следует напаять конденсатор по питанию. Учитывая, что он у нас, скорее всего, висит на проводах, 100 мкФ будет вполне достаточно. Во-первых, это позволит исключить проблему питания, если что-то пойдет не так. Во-вторых, подавая питание сразу с двух источников (с двух сторон встроенного стабилизатора), можно убить схему питания в Blue Pill. Вроде мелочь, а неприятно. Поэтому, когда используется питание от USB, всегда отключай дополнительный источник.

Вставлять самодельное устройство в USB-порт компьютера может быть чревато крупным разочарованием и выходом контроллера USB из строя. Поэтому, если попробовать очень хочется, а уверенности в прямоте своих рук нет, можно воспользоваться внешним USB-хабом (впрочем, и это не дает стопроцентных гарантий).

В этот раз в качестве контроллера клавиатуры пульта я не стал использовать сдвиговый регистр, как в телефоне или MP3-плеере. Расширитель портов ввода-вывода PCF8574 для такой задачи подходит гораздо лучше, чем сдвиговый регистр. Главное преимущество — наличие сигнала прерывания, что сильно упрощает работу с клавиатурой со стороны микроконтроллера. Кроме того, I2C — это две линии, а интерфейс регистра составляет минимум три. Да и стоит микросхема не сильно дороже — всего 15 рублей в рознице.

А вот и готовый макет. Не могу сказать, что все заработало сразу: пришлось поковыряться, побить в бубен и покурить мануалы. Но в итоге все проблемы удалось решить.

Rubber Ducky своими руками

Как ты понимаешь, в таком виде это все жутко непрактично, поэтому устройство надо оформить достойнее. Тут мне на глаза попалась флешка, и родилась вполне ожидаемая идея упаковать все в готовый и хорошо узнаваемый корпус. Размер платы флешки 14 на 34 мм, особо не разгуляешься, но с применением двухстороннего монтажа втиснуться оказалось легко.

Тут я впервые изготавливал двухстороннюю плату, и в целом это оказалось не так сложно, как я представлял. (Так, наверное, можно докатиться и до металлизации отверстий.) И честно говоря, получилось даже лучше, чем я ожидал. Для сравнения снимок рядом с оригинальной флешкой.

Rubber Ducky своими руками

Теперь можно поместить в корпус — плата встала как родная.

Rubber Ducky своими руками

Правда, пришлось сверху напаять провод для подключения светодиода, я совсем забыл про него, когда разводил плату. Ну да плат без ошибок не бывает. Осталось прикрепить крышку на место.

Rubber Ducky своими руками

Что касается пульта, то при переходе от макета к финальной версии я решил оптимизировать питание. Дело в том, что для устойчивой работы передающей части необходимо 3,3 В. Конечно, напряжение можно опустить до 3 В, и тогда схему допустимо запитать и от двух батареек АА. Но так не удастся выжать из батареек весь заряд, ведь их конечное напряжение составляет что-то около 1 В (или примерно 2 В для двух последовательно подключенных источников). А это явно недостаточно.

Если взять аккумуляторы Ni-MH, то это будет уже 2,4 В в заряженном состоянии, что тоже маловато. Решением проблемы оказалось применение step-up-преобразователя на ME2108A(PDF). Обвеса требуется минимум, а эффективность микросхемы достигает 85%. Это позволяет питать схему от двух и даже одного аккумулятора.

Rubber Ducky своими руками Rubber Ducky своими руками

Я собрал пульт, поправил несколько ошибок (забыл подтягивающие резисторы для PCF8574), и все заработало. Потом померил ток потребления от одного аккумулятора — целых 250 мА! Подобное ни в какие ворота не лезет, так что исправим это и озаботимся вопросом энергосбережения в нашем устройстве.

Энергосбережение

Держать микроконтроллер включенным все время нет никакой необходимости, он нужен лишь в момент нажатия кнопки. Помнишь, выше я писал про сигнал прерывания от контроллера клавиатуры? Тут он очень кстати. Поэтому будем ждать нажатия кнопки, будить нашу схему, посылать данные в эфир и снова засыпать. Кроме того, перевод NRF24L01 в режим stand by вместо постоянного приема позволит дополнительно сократить потребление. Финальный штрих — погасить светодиод, он тоже потребляет несколько миллиампер.

Главное здесь — не забыть, что при пробуждении микроконтроллера блок RCC тактируется от внутреннего генератора 8 МГц напрямую. Это сбивает все тайминги интерфейсов, поэтому нужно предусмотреть функцию перенастройки тактирования.

Применение этих нехитрых трюков позволило снизить потребление более чем в 500 раз! Финальное значение удалось измерить на уровне около 0,5 мА, что можно считать очень хорошим результатом.

Сценарии использования

Теперь перейдем к вариантам применения нашего комплекта. Самое первое, но не самое очевидное применение — это пульт управления. Как пользователю Arch Linux, мне очень нравится MPlayer, управление которым полностью осуществляется горячими клавишами.

Подружить его с новым устройством очень просто. Отправка с пульта строки PK2 A B приводит к эмуляции нажатия клавиши с кодом A и модификатором B. Этими двумя значениями можно описать любую клавишу и практически любое сочетание клавиш из числа используемых.

Окей, а как насчет чего-нибудь повеселее?

Все описанное ниже представлено исключительно в ознакомительных целях и не является руководством к действию. Также следует помнить, что совершение неправомерных действий влечет за собой правовые последствия.

На самом деле дальше все зависит от твоего воображения. С таким устройством можно разыграть незадачливого пользователя, прожимая горячие клавиши в самый неподходящий момент (например, комбинация Alt + F4 в Windows раздражает жертву особенно быстро).

«Глючная» мышь

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

Качество генератора псевдослучайных чисел тут несущественно. Однако, чтобы все было совсем красиво, мы можем инициализировать генератор случайным числом из АЦП, об этом целая статья.

Работает неплохо, для инициализации ГПСЧ как раз хватит. Давим на кнопку, курсор уезжает в произвольном направлении, и, если пользователь в этот момент делает что-то ответственное мышью, он будет несколько удивлен.

Баловство с текстом

Точно так же по нажатию клавиши в текстовом документе можно отрисовать какой-нибудь ASCII-арт. Например, вот такую птичку (я называю ее «трясогузкой», не спрашивай почему).

Чтобы добавить ее в код, понадобится много кавычек, переносов строк и экранирования символов. Расставлять все это вручную утомительно, поэтому можно воспользоваться скриптом и перекодировать текстовую картинку в массив. Следующий скрипт принимает два аргумента: имя файла с картинкой и имя выходного массива.

Выполнение команды

Теперь давай сделаем что-нибудь посерьезнее. Чтобы добраться до возможности исполнять команды в Windows, нужно ввести Super + R, затем набрать cmd и Enter. Главное — угадать с задержками, потому что если вводить команду, пока окно консоли не открыто, то она улетит в пустоту. Впрочем, найти подобную информацию для Windows в интернете не составит труда.

Что же касается Linux, то, как ты понимаешь, тут уже возможны варианты. Конечно, почти всегда можно рассчитывать на Ctrl + F2, но тогда придется наверняка авторизоваться в системе, а это уже само по себе задача. Поэтому примем для простоты, что мы уже знаем хоткей для вызова эмулятора терминала. Например, Ctrl + Alt + T. Тогда мы можем набрать какую-нибудь однострочную команду или вовсе написать небольшой скрипт.

Однако и такой подход неудобен и нерационален, поскольку требует много лишних команд.

Бэкдор

Есть вариант гораздо эффективнее и изящнее — связка потокового сжатия и кодирования в Base64. Возьмем небольшой скрипт, который собирает информацию о системе и открывает бэкдор.

Сжимаем его при помощи gzip на лету, перекодируя результат в Base64 с помощью cat script.sh|gzip -9|base64. После небольшой обработки получаем вот такой массив в прошивке микроконтроллера.

Осталось только выполнить обратную процедуру, благо Base64 и gzip у нас стандартные утилиты. Набираем echo BASE64 |base64 -d|gzip -d>payload.sh;chmod +x payload.sh;./payload.sh\n или, если смотреть со стороны прошивки:

А в конце добавляем нашу птицу, куда же без нее. Таким образом, кстати, очень удобно хранить и выводить в терминале ASCII-графику, тут и экономия места, и ускорение набора налицо. Да и создавать такие массивы тоже проще скриптом.

Листинг еще короче предыдущего, а экономия места в разы. Как тебе уже наверняка понятно, в качестве нагрузки скрипты использовать особенно удобно. Причем это необязательно должен быть shell. Python выглядит даже более привлекательно. Здесь определенно есть где развернуться и над чем поэкспериментировать на досуге. Думаю, теперь мне точно удалось тебя заинтересовать достаточно сильно, так что дальше ты и сам разберешься.

Как всегда, исходники проекта доступны на GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *