Категория > Новости > Утиные истории. Делаем свой аналог Rubber Ducky с беспроводной связью - «Новости»

Утиные истории. Делаем свой аналог Rubber Ducky с беспроводной связью - «Новости»


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

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



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



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


USB HID



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



Утиные истории. Делаем свой аналог Rubber Ducky с беспроводной связью - «Новости»
INFO

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



const struct usb_device_descriptor dev_descr = { 
// Дескриптор устройства
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x0483, // VID
.idProduct = 0x5710, // PID
.bcdDevice = 0x0200,
.iManufacturer = 1, // Номера строк в usb_strings[],
.iProduct = 2, // начиная с первой (!), а не
.iSerialNumber = 3, // с нулевой, как можно было бы ожидать
.bNumConfigurations = 1,
};

static const uint8_t hid_report_descriptor[] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x02, /* USAGE (Mouse) */
0xa1, 0x01, /* COLLECTION (Application) */
0x09, 0x01, /* USAGE (Pointer) */
0xa1, 0x00, /* COLLECTION (Physical) */
0x05, 0x09, /* USAGE_PAGE (Button) */
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x95, 0x03, /* REPORT_COUNT (3) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x05, /* REPORT_SIZE (5) */
0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x30, /* USAGE (X) */
0x09, 0x31, /* USAGE (Y) */
0x09, 0x38, /* USAGE (Wheel) */
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x03, /* REPORT_COUNT (3) */
0x81, 0x06, /* INPUT (Data,Var,Rel) */
0xc0, /* END_COLLECTION */
0x09, 0x3c, /* USAGE (Motion Wakeup) */
0x05, 0xff, /* USAGE_PAGE (Vendor Defined Page 1) */
0x09, 0x01, /* USAGE (Vendor Usage 1) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x95, 0x02, /* REPORT_COUNT (2) */
0xb1, 0x22, /* FEATURE (Data,Var,Abs,NPrf) */
0x75, 0x06, /* REPORT_SIZE (6) */
0x95, 0x01, /* REPORT_COUNT (1) */
0xb1, 0x01, /* FEATURE (Cnst,Ary,Abs) */
0xc0 /* END_COLLECTION */
};

static const struct {
struct usb_hid_descriptor hid_descriptor;
struct {
uint8_t bReportDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed)) hid_report;
} __attribute__((packed)) hid_function = {
.hid_descriptor = {
.bLength = sizeof(hid_function),
.bDescriptorType = USB_DT_HID,
.bcdHID = 0x0100,
.bCountryCode = 0,
.bNumDescriptors = 1,
},
.hid_report = {
.bReportDescriptorType = USB_DT_REPORT,
.wDescriptorLength = sizeof(hid_report_descriptor),
}
};


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



const struct usb_endpoint_descriptor hid_endpoint = {
// Дескриптор конечной точки
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81, // Адрес конечной точки IN
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 4, // Максимальная длина пакета
.bInterval = 0x02, // Р?нтервал РѕРїСЂРѕСЃР° РІ миллисекундах
};

const struct usb_interface_descriptor hid_iface = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 1, /* boot */
.bInterfaceProtocol = 2, /* mouse */
.iInterface = 0,
.endpoint = &hid_endpoint,
.extra = &hid_function,
.extralen = sizeof(hid_function),
};


В дескрипторе конечной точки нас интересуют ее адрес .bEndpointAddress = 0x81, максимальная длина пакета .wMaxPacketSize = 4 и интервал опроса .bInterval = 0x02,. Адрес конечной точки для нашей цели РЅРµ имеет принципиального значения, его РјРѕР¶РЅРѕ РЅРµ трогать. Что Р¶Рµ касается максимального размера пакета, то РѕРЅ обязательно должен соответствовать структуре отчета, описанной РІ hid_report_descriptor[]. В данном случае это четыре байта.



const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = &hid_iface,
}
};

const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0xC0,
.bMaxPower = 0x32,
.interface = ifaces,
};

static const char *usb_strings[] = {
// Строки, отображаемые в описании устройства
"Black Sphere Technologies",
"HID Demo",
"DEMO",
};


Завершают определения строки usb_strings[], которые ты тоже можешь прописать РїРѕ своему РІРєСѓСЃСѓ (Рё чувству СЋРјРѕСЂР°).



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



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



...
0x05, 0x01,
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null)
0x19, 0x00, // Usage Minimum (0x00)
0x29, 0x65, // Usage Maximum (0x65)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x75, 0x08, // Report Size (8)
0x95, 0x06, // Report Count (6)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null)
0xC0, // End Collection
…


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


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



С устройствами ввода и их дескрипторами мы разобрались. Теперь возникает следующий вопрос: можно ли объединить в одном устройстве и клавиатуру, и мышь? Тут нам на помощь приходит мануал по созданию составных устройств. Достаточно в дескрипторы отчетов для мыши и клавиатуры добавить поле report id, Рё РёС… РјРѕР¶РЅРѕ будет объединить. Теперь ответы нашей периферии станут длиннее РЅР° РѕРґРёРЅ байт, РЅРѕ С…РѕСЃС‚, читая его значение, будет знать, РѕС‚ какого устройства отчет.



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



...
0x05, 0x01,
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID
0x05, 0x07, // Usage Page (Kbrd/Keypad)
0x19, 0xE0, // Usage Minimum (0xE0)
0x29, 0xE7, // Usage Maximum (0xE7)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null)
0x19, 0x00, // Usage Minimum (0x00)
0x29, 0x65, // Usage Maximum (0x65)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x75, 0x08, // Report Size (8)
0x95, 0x06, // Report Count (6)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null)
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x85, 0x02, // Report ID
0x05, 0x09, // Usage Page (Buttons)
0x19, 0x01, // Usage Minimum (01)
0x29, 0x03, // Usage Maximum (03)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (0)
0x95, 0x03, // Report Count (3)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x95, 0x01, // Report Count (1)
0x75, 0x05, // Report Size (5)
0x81, 0x01, // Input (Constant) ;5 bit padding
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
0x95, 0x02, // Report Count (2)
0x81, 0x06, // Input (Data, Variable, Relative)
0xC0, 0xC0, // End Collection,End Collection
…


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



Клавиатура
1 REPORT ID = 1
2 MOD_KEYS
3 RESERVED
4 KEY1
5 KEY2
6 KEY3
7 KEY4
8 KEY5
9 KEY6

Мышь
1 REPORT ID = 2
2 KEYS
3 X
4 Y


Осталось только инициализировать интерфейс. Тут в примере можно ничего не менять, на старте драйвер вызывает функцию hid_set_config, регистрирующую конечную точку 0x81, которую РІ дальнейшем будет опрашивать наш С…РѕСЃС‚. Р’ ответ РѕРЅ получит указанные выше отчеты. Что Р¶Рµ касается функции hid_control_request, то она служит просто заглушкой и в данном случае ни на что не влияет.


Эмулируем клавиатуру



Теперь разберемся с имитацией нажатия клавиши. Для примера возьмем клавишу a СЃ РєРѕРґРѕРј 0x04. Важно обратить внимание, что РєРѕРґС‹ клавиш, выдаваемые клавиатурой, — это РІРѕРІСЃРµ РЅРµ ASCII, Рё Рѕ раскладке клавиатура тоже ничего РЅРµ знает, это РІСЃРµ РїСЂРѕРёСЃС…РѕРґРёС‚ уровнем выше. Так как Р¶Рµ выглядит нажатие клавиши Р°? Это два последовательных отчета — первый о нажатии клавиши, а второй о ее отпускании (если забыть про то, что клавишу надо отпустить, выйдет конфуз).



uint8_t pres_a[] = {1, 0, 0, 0x04, 0, 0, 0, 0, 0};
uint8_t rel_a[] = {1, 0, 0, 0, 0, 0, 0, 0, 0};

usbd_ep_write_packet(usbd_dev, 0x81, pres_a, 9);
usbd_ep_write_packet(usbd_dev, 0x81, rel_a, 9);


Перейти обратно к новости