CVE-2023-24871 - 简介和漏洞描述

admin 2024年6月25日14:18:00评论6 views字数 9241阅读30分48秒阅读模式

CVE-2023-24871 - 简介和漏洞描述

[1.0] 术语

让我们从一些术语开始:

  • BLE = 低功耗蓝牙

  • HCI / 主机 / 控制器 - HCI 代表主机-控制器接口,它是主机(在本例中为 Windows 上的蓝牙组件)和控制器(带有固件的蓝牙设备)通信的方式。

  • 适配器 = 带有固件的物理蓝牙设备。

  • 本地设备/控制器/适配器 = 存在漏洞且属于攻击者所针对的受害者的设备/控制器/适配器。

  • 远程设备/控制器/适配器 = 攻击者为了触发漏洞而控制的设备/控制器/适配器。

  • PDU = 协议数据单元,是作为数据包的一部分传输的一些数据。您可以将 PDU 视为蓝牙数据包的“内容”部分,其特定格式与数据包类型相对应。

[2.0] 有用的链接

蓝牙核心规范 v5.2(PDF) ——您可能一辈子也读不完它,但它是所有蓝牙事物的真相来源。

https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=478726

低功耗蓝牙(维基百科) ——提供了该技术的良好概述,尽管我们只需要了解其中的一小部分。

https://en.wikipedia.org/wiki/Bluetooth_Low_Energy

蓝牙低功耗广告(Medium 文章) ——一篇详细介绍 BLE 广告的博客文章。我在下面提供了摘要,但如果您感兴趣,本文会提供一些额外的背景信息。

https://medium.com/networkers-fiit-stu/introduction-to-bluetooth-low-energy-advertisements-fde7a409673

Microsoft Swift Pair -Swift Pair 是一个相当模糊的 Windows 功能(至少我之前并不知道),但在后来的第一个漏洞背景下却很重要。

https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/bluetooth-swift-pair

[3.0] 简介

在深入研究漏洞之前,我们先介绍一下 BLE 和广告。如果你熟悉它的工作原理,请直接跳到漏洞描述

https://ynwarcs.github.io/y-cve-2023-24871-intro-descr#40-vulnerability

低功耗蓝牙是蓝牙技术的一个子集,旨在用于不需要在短时间内传输大量数据的设备。BLE 协议不同于传统蓝牙,不能互换使用,但设备可以同时支持这两种协议。虽然传统蓝牙用于需要大量传输数据的东西,例如流媒体音乐或传输文件,但 BLE 适用于不需要大量电量的轻量级东西,例如从智能手表传输实时心率数据或通过信标发送信号。

兼容 BLE 的设备以多种不同的方式传输数据,但它们最基本的功能之一是广告和扫描。设备可以使用广告来广播数据以用于多种不同的目的,但一般目的是让以这种方式发送的数据包含有关设备的信息,主动扫描设备可以使用这些信息来识别和分类广播设备。发送的信息通常涉及设备的名称、制造商的 ID、设备的类型和功能,以及告诉接收器设备是否可以连接的某些指标等。简而言之,我们可以将广告想象成设备向正在监听介绍的其他设备介绍自己。

广告信息以包含广告 PDU 的 BLE 数据包的形式发送。广告的生命周期由以下步骤定义:

  • 想要通告某些信息的主机会向控制器发送一组特定的 HCI 命令。这些命令用于设置通告参数以及启用或禁用通告。对于外围设备,可能没有传统的主机-控制器接口,并且通告参数可能硬编码在固件中。

  • 想要监听广告的主机会向控制器发送特定的 HCI 命令,指示应启用扫描。无论主机是否想要接收广告,控制器通常都会被动地监听广告,此命令只是通知控制器将收到的广告转发给主机。

  • 当主机启用广告时,控制器开始以 BLE 数据包的形式定期广播广告,其中包含广告数据的特定 PDU。

  • 扫描广告的控制器接收数据包并以 HCI 事件的形式将广告数据转发给主机。

在广告数据的生命周期中,广告数据分三个不同的步骤传输,每个步骤的格式都不同。首先,广告主机设置广告参数,其中一个参数是广告数据。其次,在控制器之间传输包含广告数据的 BLE 数据包。第三,接收控制器向主机发送包含广告数据的 HCI 事件。我们实际上并不关心前两个步骤中的数据格式,因为这与漏洞无关。重要的是了解它在概念上的工作原理。对我们来说重要的是主机在扫描广告数据时接收广告数据的格式。

有两个 HCI 事件将广告数据传输到主机:LE 广告报告和LE 扩展广告报告。在这种情况下,报告代表从特定远程设备接收的单个广告单元。扩展广告是“普通”广告的扩展,是在蓝牙 5.0 中引入的,其主要目的是允许在单个广告报告中包含大量广告数据。事件可以保存来自不同设备的多个报告,因为控制器可以决定将它们批处理在一起,避免在短时间内发送多个事件。这些事件传输的数据格式类似:

Num_Reports, -- number of advertising reports in the dataEvent_Type[i], -- type of the event, used to indicate whether the device can be connected to, if the advertisement was directed etc.Address_Type[i], -- type of the address of the advertising device, can be public, random, etc.Address[i], -- the address of the advertising deviceData_Length[i], -- length of the advertising dataData[i], -- advertising data in bytesRSSI[i] -- signal strength of the received packet

LE 广告报告结构

Num_Reports, -- same as aboveEvent_Type[i], -- same as aboveAddress_Type[i], -- same as aboveAddress[i], -- same as abovePrimary_PHY[i], -- not importantSecondary_PHY[i], -- not importantAdvertising_SID[i], -- not importantTX_Power[i], -- not importantRSSI[i], -- same as abovePeriodic_Advertising_Interval[i], -- not importantDirect_Address_Type[i], -- if the advertisement was directed, type of the address the advertisement was directed toDirect_Address[i], -- address that the advertisement was directed toData_Length[i], -- same as aboveData[i] -- same as above

LE 扩展广告报告结构

扩展广告报告允许传输更多数据,但也包含一些其他字段。这些字段主要与数据的物理传输有关,对我们来说并不重要,所以我没有费心解释它们。这两个事件之间的最大区别在于它们可以包含的广告数据的最大长度:

  • 传统 LE 广告报告最多可以包含31 个字节的广告数据,因为这是使用传统广告时可以在单个 PDU 中传输的广告数据的限制。

  • 扩展 LE 广告报告最多可包含1650字节的广告数据。这是因为扩展广告 PDU 最多可传输254字节的广告数据,并且可以将多个连续的 PDU 链接到单个广告报告中。但是,广告类型存在一些特定限制,我们稍后会看到。

广告数据本身的格式相当简单。它由一个或多个广告部分组成,每个广告部分都有特定的格式:

Length, -- length of the data in the section, including the type fieldType, -- type of the section, indicates what kind of data the section holdsData -- data in the section

广告版块结构

例如,设备可以在广告数据部分中传输其名称。该部分的类型将是 0x09(“完整本地名称”),数据的长度将是 ASCII 中名称的长度(加上终止字符),数据本身将包含以空字符结尾的 ASCII 字符串。常见广告部分类型的列表可在此处找到(PDF),但规范允许制造商特定的广告部分,其格式和内容可能保持不透明。

最后,了解存在哪些类型的广告事件类型以及广告报告可以携带哪些类型的地址也很有用。事件类型的目的是告诉广告的接收者是否可以连接或扫描广告设备,广告是否专门针对此接收者,以及其他一些信息。事件类型可以是:

  • ADV_IND- 广告设备可以连接并扫描,广告是无方向的。这种广告在希望连接到任何中央设备的外围设备中很常见,例如在与中央设备配对之前,蓝牙耳机就是如此。这种类型的单个报告中可以发送的广告数据的最大长度为251字节。

  • ADV_DIRECT_IND- 可以连接到广告设备,并将广告定向到特定接收器。重复前面的示例,这种广告将由已与中央设备配对的蓝牙耳机使用 - 通过使用这种广告,耳机请求连接到该特定设备。同样,单个报告中的最大数据量为251字节。

  • ADV_NONCONN_IND- 广告设备既不可连接也不可扫描,并且广告是无方向的。这在信标中很常见,信标的唯一目的是向所有附近的设备广播一些数据。单个报告中可以发送的广告数据的最大长度没有限制,即1650字节的限制就在这里。

  • ADV_SCAN_IND- 相同,ADV_NONCONN_IND但设备允许扫描。如果上例中的信标只想向感兴趣的各方传输信息,这将很有用 - 接收设备可以在收到广告后请求扫描并接收实际信息。这种类型的报告不包含广告数据。

  • SCAN_RSP- 表示此广告报告是对扫描请求的响应。在上例中,如果扫描到可扫描设备,则将以SCAN_RSP广告报告的形式收到响应。同样,单个报告中数据的最大长度不受限制。

至于地址,每个广告设备通常还会包含一个地址,接收方在想要回复时应使用该地址。地址由 6 个字节组成,可以是随机生成的,也可以是持久的(并且假定是唯一的)。地址的类型可以是:

  • 公共设备地址-这是设备的唯一 MAC 地址。

  • 随机设备地址 - 这是一个临时的随机地址。

  • 公共身份地址

  • 随机身份地址

这些几乎就是我们现在需要知道的所有信息。关于广告有很多话要说,但大多数都与当前的漏洞无关。如果您有兴趣了解更多本文未涵盖的内容,您可以在我上面链接的那篇博客文章中找到更多信息。

[4.0] 漏洞

Windows 蓝牙堆栈相当复杂,涵盖多个不同的驱动程序、服务和用户模式库。Microsoft在此处提供了该架构的简要概述。我将分享他们的表示:

CVE-2023-24871 - 简介和漏洞描述

由于广告数据可以包含不同类型的信息,因此广告报告最终可以在多个不同的地方进行解析。为了避免必须在各个地方单独实现解析过程,Microsoft 使用一个静态库,该库链接到需要此功能的模块中。此库中参与解析广告数据的两个函数是BTHLELib_ADValidateEx和BthLeLib_ADValidateBasic:

HRESULT BthLELib_ADValidateEx(const uint8_t* adv_data, uint16_t adv_data_size, uint8_t** out_sections, uint8_t* out_num_sections){  // initial validation  uint8_t num_sections = 0;  HRESULT validation_res = BthLeLib_ADValidateBasic(adv_data, adv_data_size, &num_sections);  if (validation_res < 0) return validation_res;  if (num_sections == 0) return validation_res;  // allocate the array for advertisement section data  // struct BTHLE_AD_SECTION { uint8_t size; uint8_t type; uint8_t data[0x151]; }  // sizeof(BTHLE_AD_SECTION) = 0x153  BTHLE_AD_SECTION* sections = BthLELibAllocatePoolEx(sizeof(BTHLE_AD_SECTION) * num_sections);  // validate data for each section and copy data into the array  uint16_t section_offset = 0;  for (uint8_t i = 0; section_offset < adv_data_size; ++i)  {    BTHLE_AD_SECTION* section = sections[i];    uint8_t section_size = adv_data[section_offset];    uint8_t section_type = adv_data[section_offset + 1];    if (section_size == 0) break;    if (section_size + section_offset + 1 >= adv_data_size) return 0xC000000D;    // write the section size and the section type into the output section    section->size = section_size;    section->type = section_type;    // based on section type, do validation and write output section data based on input section data    switch (section_type)    {      ...      // validate section data based on section type      // this is done by calling BthLELib_ADValidate???, where ??? denotes the type of the section      if (BthLELib_ADValidate???(...))      {        // If validation fails for a section, the function exits        ExFreePool(sections);        return 0xC000000D;      }      ...      // for some section types, more data is written into the output section      // e.g. vendor data, signed data sections...      case ... :      {        // but most commonly, data is copied as is into the output section        uint8_t section_data_offs = ...;        uint8_t section_data_size = ...;        memcpy(section->data, adv_data + section_offset + section_data_offs, section_data_size);      }    }    section_offset += section_size + 1;  }  *out_sections = sections;  *out_num_sections = num_sections;  return 0;}HRESULT BthLeLib_ADValidateBasic(const uint8_t* adv_data, uint16_t adv_data_size, uint8_t* out_num_sections){  uint16_t adv_data_offset = 0;  while (adv_data_offset < adv_data_size)  {    uint8_t section_size = adv_data[adv_data_offset];    if (section_size == 0) break;    if (section_size + adv_data_offset + 1 >= adv_data_size) return 0xC000000D;    /**** OVERFLOW HERE ***/    ++(*out_num_sections);    /**** OVERFLOW HERE ***/    adv_data_offset += section_size + 1;  }  while (adv_data_offset < adv_data_size)  {    if (adv_data[adv_data_offset++] != 0) return 0xC0000000D;  }  return 0;}

BTHLELib_ADValidateEx是外部模块调用的函数,用于将 HCI 事件中收到的广告数据转换为更合适的格式。该函数接收指向原始广告数据及其长度的指针,以及指示转换后的广告部分的输出数组和该数组长度的输出参数。在其最开始处,BthLeLib_ADValidateBasic被调用,以确保每个广告部分都具有正确的长度(即它不会超出数据的末尾),但也计算数据中的部分总数。然后使用计算出的计数BthLELib_ADValidateEx为输出部分数组分配内存。该函数的其余部分专注于解析特定的广告部分并将数据从输入缓冲区复制到与该部分对应的数组条目内的适当格式。虽然一些输出数据的格式更结构化,但对于绝大多数部分类型,输入数据只是以其原始形式复制到输出部分中。

该漏洞位于计算广告部分数量的代码中。由于为此目的使用了 8 位无符号整数,因此数据中的部分超过 255 个将导致变量的值溢出。一旦发生这种情况,函数返回的计数值将低于数据中实际存在的部分数量。分配给数组的内存量将低于sections预期,一旦将各个部分的数据复制到应该属于部分数组的内存中,就会发生越界写入。

触发漏洞的广告数据最简单的例子是包含 257 个“空”部分的数据,即每个部分如下:

Length = 0x01Type = 0x00, -- 0x00 is an invalid / reserved typeData = []

退出后BthLeLib_ADValidateBasicnum_sections将等于1,并且为节数组分配的内存量将为0x153字节。同时,中的循环BthLELib_ADValidateEx将遍历所有 257 个节,复制每个节的长度和类型,远远超出分配的内存的末尾,即在循环的第 2 次迭代中,将在分配的内存末尾之后写入,在第 3 次迭代中,将在内存末尾的0x01 0x00偏移处写入相同的值,等等。0x153

[4.1] 修复

微软修复了该漏洞,方法是如果达到 则退出BthLeLib_ADValidateBasic并显示错误。修复后的代码如下所示:*out_num_sections255

HRESULT BthLeLib_ADValidateBasic(const uint8_t* adv_data, uint16_t adv_data_size, uint8_t* out_num_sections){  uint16_t adv_data_offset = 0;  while (adv_data_offset < adv_data_size)  {    uint8_t section_size = adv_data[adv_data_offset];    if (section_size == 0) break;    /***** FIX *****/    if (*out_num_sections == 255) return 0xC000000D;    /***** FIX *****/    if (section_size + adv_data_offset + 1 >= adv_data_size) return 0xC000000D;    ++(*out_num_sections);    adv_data_offset += section_size + 1;  }  while (adv_data_offset < adv_data_size)  {    if (adv_data[adv_data_offset++] != 0) return 0xC0000000D;  }  return 0;}

虽然这可以防止整数溢出,但它不必要地限制了单个广告报告中可以包含的广告部分的数量。标准并没有规定这样的限制,这只会让微软不符合标准。虽然在现实生活中不太可能有人需要接近 255 个部分,所以这个限制可能不会产生太大的影响。

[4.2] 可利用性

我们稍后会看到,这个漏洞很容易被利用。当然,我们假设攻击者完全控制广告数据,因为它必须遵循预期的格式,但其内容可以是任意的。

  • 攻击者可以很好地控制分配的数据量,因为他们可以控制数据中的部分数量,从而控制 的值num_sections。这意味着他们可以让分配落入他们想要的几乎任何堆存储桶中。

  • 攻击者几乎可以完全控制将要越界写入的数据。限制包括写入必须从的倍数开始,0x153并且前两个字节必须表示部分的长度和类型。例如,某些部分(如“完整名称”部分)只是从输入缓冲区一一复制,复制到输出中的数组条目,这允许攻击者完全控制越界写入的数据。

  • 在发生类似这样的整数溢出时,溢出会导致输出缓冲区大小计算错误,由于内存损坏“太严重”,即必须遍历整个非溢出范围,漏洞变得毫无用处的情况并不少见。但是,可以通过滥用单个部分验证来避免这种情况。一旦攻击者不想破坏某个点之后的内存,他们就可以提供一个损坏的部分,使其无法通过为该部分类型设计的特定验证。这将使代码提前退出,同时仍保持内存损坏。

[4.3] 影响

此漏洞存在于整个 Windows 蓝牙堆栈的四个不同模块中:

  • (1)bthport.sys——位于堆栈最底层的内核驱动程序

  • (2)Microsoft.Bluetooth.Service.dll – 蓝牙支持服务使用的模块

  • (3)Windows.Internal.Service.dll——蓝牙支持服务使用的模块

  • (4)dafBth.dll——设备关联服务使用的模块

该函数用于解析模块 (1)、(2) 和 (4) 中的远程广告数据。在模块 (3) 中,它用于解析从堆栈上“上游”运行的本地程序发送的广告数据。因此,该漏洞可用作 RCE 和 LPE 的载体。我们将在下面分别介绍这两个载体,因为它们周围的环境非常不同。

这就是我们能说的有关这个漏洞的全部内容,无需深入探讨它可能被利用的具体利用方式。

CVE-2023-24871 - intro & vulnerability descriptionhttps://ynwarcs.github.io/y-cve-2023-24871-intro-descr#40-vulnerability

原文始发于微信公众号(Ots安全):CVE-2023-24871 - 简介和漏洞描述

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月25日14:18:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2023-24871 - 简介和漏洞描述https://cn-sec.com/archives/2882590.html

发表评论

匿名网友 填写信息