【问题标题】:How to get WiFi security key(WPA, WPA2, ESS) of scanned networks using NL80211_BSS_CAPABILITY?如何使用 NL80211_BSS_CAPABILITY 获取扫描网络的 WiFi 安全密钥(WPA、WPA2、ESS)?
【发布时间】:2021-04-20 05:44:05
【问题描述】:

我正在使用 netlink 库 nl80211.h 扫描 wifi 网络并成功获取 ssid、mac 地址、状态、频率和信号。我想使用相同的库添加每个网络的安全类型。 我使用 NL80211_BSS_CAPABILITY 作为枚举之一,它给出了一个整数,我将其硬编码到我的代码中以确保安全。这似乎很乏味,并且必须使用这种方法提供大量数据(整数值)。获得 WPA/WPA2 很好,但是当开放网络出现时代码会失败。以下是我迄今为止使用的值。一个更好的逻辑可以让我打开网络(ESS 或 WEP),最好类似于 wpa_supplicant 和 netlink 库。

int keynum;
char *keytype;
keynum = nla_get_u32(bss[NL80211_BSS_CAPABILITY]);

if(keynum==5153 || keynum == 34)
    keytype="NONE";

else if(keynum==5169 || keynum == 1297 || keynum==1073|| keynum == 5393)
    keytype="WPA2";

else if (keynum == 1041)
    keytype="WPA WPA2";

else
    keytype="WPA WPA2";

【问题讨论】:

    标签: wifi embedded-linux netlink wpa-supplicant


    【解决方案1】:

    首先我们来看看NL80211_BSS_CAPABILITY中的数据是什么样的
    为了理解这一点,我将首先提请您注意属性NL80211_BSS_BEACON_IES
    该字段的说明如下:

    /**
    * @NL80211_BSS_BEACON_IES: binary attribute containing the raw
    * information
    */
    

    source
    但是我觉得这样的描述太少了,想要更详细的描述,我们转here
    首先,我们感兴趣的是:

    1. 能力信息(2 字节) 该字段包含用于指示请求或广告可选的子字段的数量 能力。

    以使用wireshark及其Сapabilities Information字段捕获的一个数据包为例(关于如何做到这一点的一篇好文章是here):

    Сapabilities Information: 0x0431
                .... .... .... ...1 = ESS capabilities: Transmitter is an AP
                .... .... .... ..0. = IBSS status: Transmitter belongs to a BSS
                .... ..0. .... 00.. = CFP participation capabilities: No point coordinator at AP (0x00)
                .... .... ...1 .... = Privacy: AP/STA can support WEP
                .... .... ..1. .... = Short Preamble: Allowed
                .... .... .0.. .... = PBCC: Not Allowed
                .... .... 0... .... = Channel Agility: Not in use
                .... ...0 .... .... = Spectrum Management: Not Implemented
                .... .1.. .... .... = Short Slot Time: In use
                .... 0... .... .... = Automatic Power Save Delivery: Not Implemented
                ...0 .... .... .... = Radio Measurement: Not Implemented
                ..0. .... .... .... = DSSS-OFDM: Not Allowed
                .0.. .... .... .... = Delayed Block Ack: Not Implemented
                0... .... .... .... = Immediate Block Ack: Not Implemented
    

    我们可以观察到,其中一个标志(在本例中使用通用名称securityPrivacy: AP/STA can support WEP)。正如所写的,这个标志告诉我们这个网络支持 wep 加密。但是,此标签的其他标志不携带有关网络使用的其他编码算法的信息。因此,后续对这个领域的分析会变成:

    if (bss[NL80211_BSS_CAPABILITY] != NULL) {
        wlan_info.bss_capability = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
      }
      if (!(wlan_info.bss_wpa1_wpa2 | wlan_info.bss_wpa2 | wlan_info.bss_wpa1)) {
        wlan_info.bss_wep = wlan_info.bss_capability & 16;
      }
    

    但这还没有结束。我们仍然需要查找有关其他加密算法的信息。
    为此,我们返回NL80211_BSS_BEACON_IES
    1.我们检查是否为空。

    if (NULL != bss[NL80211_BSS_BEACON_IES] && NULL != nla_data(bss[NL80211_BSS_BEACON_IES]))
    

    2.我们遍历每个标签,读取前两个字节,从上面的文章中可以看出,我们由此找出标签的类型和它包含的数据的长度。

    for (int i = 0;;) {
          bool first = i;
          uint8_t header_type =
              *(uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) + i++);
          uint8_t header_len =
              *(uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) + i++);
          i += header_len;
    

    3.在所有标签中,我们正在寻找 2 个特定标签:# 48 (0x30)# 221 (0xDD)。它们可能看起来像这样:

    第一:

    Tag: RSN Information
                Tag Number: RSN Information (48)
                Tag length: 20
                RSN Version: 1
                Group Cipher Suite: 00:0f:ac (Ieee 802.11) AES (CCM)
                    Group Cipher Suite OUI: 00:0f:ac (Ieee 802.11)
                    Group Cipher Suite type: AES (CCM) (4)
                Pairwise Cipher Suite Count: 1
                Pairwise Cipher Suite List 00:0f:ac (Ieee 802.11) AES (CCM)
                    Pairwise Cipher Suite: 00:0f:ac (Ieee 802.11) AES (CCM)
                        Pairwise Cipher Suite OUI: 00:0f:ac (Ieee 802.11)
                        Pairwise Cipher Suite type: AES (CCM) (4)
                Auth Key Management (AKM) Suite Count: 1
                Auth Key Management (AKM) List 00:0f:ac (Ieee 802.11) PSK
                    Auth Key Management (AKM) Suite: 00:0f:ac (Ieee 802.11) PSK
                        Auth Key Management (AKM) OUI: 00:0f:ac (Ieee 802.11)
                        Auth Key Management (AKM) type: PSK (2)
                RSN Capabilities: 0x0000
    

    在 RSN 信息标签中,我们对 2 个字段(未来 4 个字节)感兴趣:Auth Key Management (AKM) OUI: 00: 0f: ac (Ieee 802.11)Auth Key Management (AKM) type: PSK (2)

    取决于(AKM) type: PSK (2)(AKM) type: WPA (1),它们将分别为WPA2WPA1

    第二:

    Tag: Vendor Specific: Microsoft Corp.: WPA Information Element
                Tag Number: Vendor Specific (221)
                Tag length: 22
                OUI: 00:50:f2 (Microsoft Corp.)
                Vendor Specific OUI Type: 1
                Type: WPA Information Element (0x01)
                WPA Version: 1
                Multicast Cipher Suite: 00:50:f2 (Microsoft Corp.) AES (CCM)
                    Multicast Cipher Suite OUI: 00:50:f2 (Microsoft Corp.)
                    Multicast Cipher Suite type: AES (CCM) (4)
                Unicast Cipher Suite Count: 1
                Unicast Cipher Suite List 00:50:f2 (Microsoft Corp.) AES (CCM)
                    Unicast Cipher Suite: 00:50:f2 (Microsoft Corp.) AES (CCM)
                        Unicast Cipher Suite OUI: 00:50:f2 (Microsoft Corp.)
                        Unicast Cipher Suite type: AES (CCM) (4)
                Auth Key Management (AKM) Suite Count: 1
                Auth Key Management (AKM) List 00:50:f2 (Microsoft Corp.) PSK
                    Auth Key Management (AKM) Suite: 00:50:f2 (Microsoft Corp.) PSK
                        Auth Key Management (AKM) OUI: 00:50:f2 (Microsoft Corp.)
                        Auth Key Management (AKM) type: PSK (2)
    

    这个指向我们WPA1 WPA2。这是一个sn-p,我们如何处理它。

    if (header_type == VENDOR_TAG_ID) {
            long long int oui = 0;
            memcpy(&oui,
                   (uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) -
                               header_len + i),
                   6);
            if (oui == WPA1_WPA2_BEACON) wlan_info.bss_wpa1_wpa2 = 1;
          } else if (header_type == RSN_TAG_ID) {
            int32_t oui = 0;
            memcpy(&oui,
                   (uint8_t *)((nla_data(bss[NL80211_BSS_BEACON_IES])) - 6 + i), 4);
            if ((oui == WPA2_BEACON_1) || (oui == WPA2_BEACON_2))
              wlan_info.bss_wpa2 = 1;
            else if (oui == WPA1_BEACON)
              wlan_info.bss_wpa1 = 1;
          }
    

    这里使用的定义是:

    #define RSN_TAG_ID 0x30
    #define VENDOR_TAG_ID 0xdd
    #define BEACON_DATA_LEN 0x100
    #define BSS_SSID_LEN BEACON_DATA_LEN
    #define WPA1_BEACON 0x1AC0F00
    #define WPA2_BEACON_1 0x2AC0F00
    #define WPA2_BEACON_2 0x4AC0F00
    #define WPA1_WPA2_BEACON 0x101F25000
    

    另一个有趣的点是iw kernel impl
    here 这是一个现成的功能,但我没有研究它是如何工作的。

    void print_ies(unsigned char *ie, int ielen, bool unknown,
               enum print_ie_type ptype)
    {
        struct print_ies_data ie_buffer = {
            .ie = ie,
            .ielen = ielen };
    
        if (ie == NULL || ielen < 0)
            return;
    
        while (ielen >= 2 && ielen - 2 >= ie[1]) {
            if (ie[0] < ARRAY_SIZE(ieprinters) &&
                ieprinters[ie[0]].name &&
                ieprinters[ie[0]].flags & BIT(ptype)) {
                print_ie(&ieprinters[ie[0]],
                     ie[0], ie[1], ie + 2, &ie_buffer);
            } else if (ie[0] == 221 /* vendor */) {
                print_vendor(ie[1], ie + 2, unknown, ptype);
            } else if (ie[0] == 255 /* extension */) {
                print_extension(ie[1], ie + 2, unknown, ptype);
            } else if (unknown) {
                int i;
    
                printf("\tUnknown IE (%d):", ie[0]);
                for (i=0; i<ie[1]; i++)
                    printf(" %.2x", ie[2+i]);
                printf("\n");
            }
            ielen -= ie[1] + 2;
            ie += ie[1] + 2;
        }
    }
    

    最后一点是“group”“attr”的属性。与“组”属性“bss”不同,它们适用于 wiphy structures
    我没有对此进行足够的研究以了解网络管理员如何从中提取有关网络的必要信息,但您可以尝试查看linblnetlink 来源,可能会有关于此的信息。这里还有一个如何使用它们的示例:
    repo
    source file
    安全密钥的枚举值:

    /**
     * NMDeviceWifiCapabilities:
     * @NM_WIFI_DEVICE_CAP_NONE: device has no encryption/authentication capabilities
     * @NM_WIFI_DEVICE_CAP_CIPHER_WEP40: device supports 40/64-bit WEP encryption
     * @NM_WIFI_DEVICE_CAP_CIPHER_WEP104: device supports 104/128-bit WEP encryption
     * @NM_WIFI_DEVICE_CAP_CIPHER_TKIP: device supports TKIP encryption
     * @NM_WIFI_DEVICE_CAP_CIPHER_CCMP: device supports AES/CCMP encryption
     * @NM_WIFI_DEVICE_CAP_WPA: device supports WPA1 authentication
     * @NM_WIFI_DEVICE_CAP_RSN: device supports WPA2/RSN authentication
     * @NM_WIFI_DEVICE_CAP_AP: device supports Access Point mode
     *
     * 802.11 specific device encryption and authentication capabilities.
     **/
    typedef enum {
        NM_WIFI_DEVICE_CAP_NONE          = 0x00000000,
        NM_WIFI_DEVICE_CAP_CIPHER_WEP40  = 0x00000001,
        NM_WIFI_DEVICE_CAP_CIPHER_WEP104 = 0x00000002,
        NM_WIFI_DEVICE_CAP_CIPHER_TKIP   = 0x00000004,
        NM_WIFI_DEVICE_CAP_CIPHER_CCMP   = 0x00000008,
        NM_WIFI_DEVICE_CAP_WPA           = 0x00000010,
        NM_WIFI_DEVICE_CAP_RSN           = 0x00000020,
        NM_WIFI_DEVICE_CAP_AP            = 0x00000040
    } NMDeviceWifiCapabilities;
    

    以及使用方法:
    source file

    if (tb[NL80211_ATTR_CIPHER_SUITES]) {
        int num;
        int i;
        __u32 *ciphers = nla_data (tb[NL80211_ATTR_CIPHER_SUITES]);
    
        num = nla_len (tb[NL80211_ATTR_CIPHER_SUITES]) / sizeof(__u32);
        for (i = 0; i < num; i++) {
            switch (ciphers[i]) {
            case 0x000fac01:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP40;
                break;
            case 0x000fac05:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_WEP104;
                break;
            case 0x000fac02:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_TKIP |
                          NM_WIFI_DEVICE_CAP_WPA;
                break;
            case 0x000fac04:
                info->caps |= NM_WIFI_DEVICE_CAP_CIPHER_CCMP |
                          NM_WIFI_DEVICE_CAP_RSN;
                break;
            default:
                nm_log_err (LOGD_HW | LOGD_WIFI, "Don't know the meaning of NL80211_ATTR_CIPHER_SUITES %#8.8x.", ciphers[i]);
                break;
            }
        }
    }
    

    以及那里使用的调用和回调“链”:

         wifi_utils_init (const char *iface, int ifindex, gboolean check_scan)
                                      ^
                                      ||
                 wifi_nl80211_init (const char *iface, int ifindex)
                                       ^
                                      ||
        static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
    

    WPA 3 编辑:
    我还不能说什么。我的搜索从this page 开始,并通过有链接的页面继续。
    nmcli 可能不支持WPA 3,因为根据存储库判断,上次提交是 9 年前(和@987654364 @ 是从 802.11ax standard 引入的,如果我没记错的话两年前就出来了)。
    如果您有一个使用WPA 3 加密的接入点,可以使用命令nmcli dev wifi 轻松检查。 与 nmcli 不同,iw 存储库中的最后一次提交是相对较新的,因此,它可能已经在处理我们的案例。
    另外,我不能说来自wiresharkWPA 3 加密的数据包的内容,因为我没有必要的接入点,因此我没有测试它。

    【讨论】:

    • 看起来有很多东西可以深入了解 key_mgmt。是否有任何数据集可以映射到可以让我们获得安全类型的值,因为现在 WPA 3 也在开发中?顺便说一句,感谢您提供可怕的细节。 @plzvtl
    • @iambhaskaranand 根据我的假设进行了编辑。
    • 目前 wpa_supplicant 也不支持 WPA 3。
    最近更新 更多