Windows bluetooth vulnerability exploit (CVE-2022-44675)

作者: B1aN 分类: Windows安全 发布时间: 2023-03-31 07:25

Foreward

Two vulnerabilities that I found (CVE-2022-44674, CVE-2022-44675) have been fixed by microsoft last year. The LPE exploit of one vulnerability (CVE-2022-44675) has been accomplished by me before the microsoft patch.

In this article, I will share:

  1. details about the vulnerability
  2. how to exploit the vulnerability
  3. details about the pool-fengshui

Details about the vulnerability (CVE-2022-44675)

The vulnerability is an integer overflow vulnerability in bthport.sys.

Introducing of bthport.sy

The bthport.sys is a Windows bluetooth bus driver. It is used for

  1. interaction with upper bus driver (Eg: USB bus)
  2. providing service for lower driver (Eg: Bluetooth Enumerator)
  3. processing service request from usermode (Eg: Inquiry command of HCI)

Bluetooth SDP specification

The Service Discovery protocol (SDP) provides a means for applications to
discover which services are available and to determine the characteristics of
those available services. 1

SDP is an C/S architecture. SDP server maintains a SDP database which consists of a list of service records. A client can retrive information from a service record by issuing a SDP request.

Service record and service attribute

A service record is a list of service attributes. Each service attribute describes a single characteristic of a service.

  1. attribute ID An attribute ID is a 16-bit unsigned integer that distinguishes each service attribute from other service attributes within a service record. The attribute ID also identifies the semantics of the associated attribute value.
  2. attribute value The attribute value is a field of indeterminate length. Its meaning is determined by the attribute ID to which it is associated. An attribute value can contain many different types of information.

Data representation

SDP defines a simple mechanism to describe the data contained within an attribute ID, attribute ID range, and attribute value. The primitive construct used is the data element. 2

The data element consists of two fields: a header field and a data field. The header field is composed of two parts: a type descriptor and a size descriptor.

type descriptor

size descriptor

The data is a sequence of bytes whose length and meaning is specified in the header field.

Data element examples

Windows implementation

Windows provides serveal IOCTLs to usermode, in bthport.sys, whose function is accessing and manipulation the SDP Service Records. The vulnerability lies in this part of function.

Here are some functions related to the vulnerability.

  • SdpDB_AddRecord This function requires the input of a service record represented by the data representation. If executed correctly, the service record will be saved in the SDP database and a service record handle will be returned.
  • SdpDB_RemoveRecord This function requires the input of a service handle. If executed correctly, the service record related to the service handle will be deleted.
  • SdpInt_ServiceAndAttributeSearch This function requires the input of service UUID and attribute ID. If executed correctly, the complete queried service record will be returned. NOTE: Service UUID is a special type of service attribute, its attribute ID is 0x0001, and the attribute value is UUID.

vulnerability detail

The root reason of the vulnerability is that the code does not check interger overflow when accumulating the size of the queried attribute value. That will make the code allocate a memory block whose size is little than actually needed.

// ......
  while ( v12 != a1 )
  {
    if ( (a7 || (*((_DWORD *)v12 + 5) & 2) == 0)
    // search and return the service record containing the specified service UUID
      && SdpUS_SearchStream(v34, *((unsigned __int8 **)v12 + 5), *((_DWORD *)v12 + 9)) )
    {
      v14 = *((_DWORD *)v12 + 9);
      v15 = (unsigned __int8 *)*((_QWORD *)v12 + 5);
      v33 = 0i64;
      // search and return the service attribute whose attribute id is in the specified attribute id range in service record
      v10 = SdpFindAttributeInStreamInternal(
              v15,
              v14,
              *(struct _SdpAttributeRange **)v35,
              *((_DWORD *)v35 + 2),
              &v33,
              v13);
      if ( v10 < 0 )
        break;
      v16 = v33;
      if ( v33 )
      {
        v17 = v32;
        // save the result in the double-linked list
        if ( *v32 != &P )
LABEL_32:
          __fastfail(3u);
        *((_QWORD *)v33 + 1) = v32;
        *(_QWORD *)v16 = &P;
        *v17 = v16;
        v32 = (PVOID *)v16;
        // ------------ BUG HERE ------------
        v8 += *((_DWORD *)v16 + 4); // accumulate the size of attributes value
        // code does not check integer overflow, thus some well-designed service records can achieve integer overflow
      }
    }
    v12 = *(struct _SDP_DATABASE **)v12;
  }
// ......
  v18 = 4;
  if ( v8 <= 0xFFFF )
    v18 = 2;
  if ( v8 <= 0xFF )
    v18 = 1;
  v19 = v8 + v18 + 1;
  // overflowed integer will be the size argument of alloca function and return the memory block whose size is smaller than accaully needed
  v20 = (unsigned __int8 *)ExAllocatePoolWithTag(NonPagedPoolNx, v19, 0x44706453u);
  v21 = v20;  if ( !v20 )
  {
    v27 = 6;
    v10 = 0xC000009A;
LABEL_26:
    *v13 = v27;
    goto LABEL_30;
  }
  memset(v20, 0, v19);
  v22 = a5;
  *a4 = v21;
  *v22 = v19;
  v23 = SdpWriteVariableSizeToStream(6, v8, v21);
  v24 = (PVOID *)P;
  v25 = v23;
  if ( P != &P )
  {
    do
    {
      // ------------ Out of Bounds Write  ------------
      memmove(v25, (char *)v24 + 20, *((unsigned int *)v24 + 4));
      v26 = *((unsigned int *)v24 + 4);
      v24 = (PVOID *)*v24;
      v25 += v26;
    }
    while ( v24 != &P );
// ......

Therefore, if there are two service records, its attribute value size is 0x8000’0000, the vulnerability will be triggered when the attribute value in these two service records is queried.

For example:

std::uint8_t data[] = {
    0x37, 0x80, 0x00, 0x00, 0x1e, // total length minus the header size 5 bytes
    0x09, 0x00, 0x01, // Attribute id 1
    0x35, 0x11, 0x1c, 0xaa, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xdd, // GUID
    0x09, 0x01, 0x00, // Attribute id 0x100
    0x27, 0x80, 0x00, 0x00, 0x00 // Attribute size 0x8000'0000
    // ...... more data
};

This is a service record containing two service attributes. An attribute of one of them is service UUID, its attribute ID is 1, its attribute value is GUID{0xaaaaaaaa, 0xbbbb, 0xbbbb, { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc } }; another attribute is a service attribute whose attribute ID is 0x100 and the size of attribute value is 0x8000’0000. If there are two service record like that in SDP database, the accumulated size of two attribute values will overflow when we query attribute values that service UUID is GUID{0xaaaaaaaa, 0xbbbb, 0xbbbb, { 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc } } and the service attribute range is [0x100,0x100].

Pool-fengshui and vulnerability exploit

The key difficult point of the exploit progress is that it’s a 32 bit integer overflow vulnerability, which means that it will override 4GB memory. To exploit the vulnerability stably, it needs to spary 4GB memory in kernel pool to avoid BSoD before the exploitation process is completed.

Windows kernel pool

There are lots of articles talk about Windows kernel pool on internet. However,it would be a different topic if we go deeper. So here is a brief about Windows kernel pool and more words about Segment Alloc.

There are four pools in Windows kernel:

  • Low Fragmentation Heap : <= 0x200
  • Variable Size: <= 0x2’0000
  • Segment Alloc: <= 0x7f’0000
  • Large Alloc: > 0x7f’0000

Segment Alloc is the allocator when the size is in range ( 0x20'000, 0x7f0'000 ]. Segment Alloc is managed by a HEAP_SEG_CONTEXT structure.

Segment Alloc allocates variable sized segment. Each segment has pages to allocate and a HEAP_PAGE_SEGMENT header.

In fact Segment Alloc will differ in some details depending on the allocation size.

  1. ( 0x2'0000, 0x7'f000 ] The size of HEAP_PAGE_SEGMENT is 0x2000. Allocatable pages follow the header.
  2. ( 0x7'f000, 0x7f'0000 ] The size of HEAP_PAGE_SEGMENT is 0x2000. Allocatable pages are localed at 0x1’0000 after the start of HEAP_PAGE_SEGMENT.

how to do pool-fengshui

The pool-fengshui needs to meet some requirements.

  1. fast When pool-fengshui, it should be fast because there are 4GB memory to spary. To do that can avoid unexcepted memory inserted into the 4GB which crashes the system.
  2. stable When pool-fengshui, the stability is required to ensure that the memory layout can be the same every time as much as possible.
  3. Good availability When pool-fengshui, it is necessary to ensure the flexibility of pool-fengshui to lower the diffculty of the next exploit steps.

To meet the fast speed requirement, we should choose a block larger allocation interval during heap layout, not the Large Alloc. After we did the test, Large Alloc isn’t stable enough. Segment Alloc is the next choice. If using Segment Alloc, the maximum allocation memory block size should not bigger than 0x7’f0000. Because, if allocation is bigger than 0x7’f000, there will be a (0x1’0000-0x2000=0xe000) bytes gap between header and allocable pages. This gap may not map to physical memory.

In the end, the 0x7’f000 size memory block should be used to spary the 4GB. That can meet the requirements as much as possible.

vulnerability exploit

After overcoming the difficulty which is 4GB pool-fengshui, the vulnerability becomes an out of bounds write vulnerablity with arbitrary size and content. To abtain arbitrary address read primitive and arbitrary address write primitive, NamedPipe4 can be exploited. The detail of NamedPipe technique will not be talked about in here. But three possible problems are listed here.

  1. Before the start of pool-fengshui, enough memory block should be reserved for operating system. Doing so can prevent 4GB pool-fenshui from interrupting as much as possible.
  2. If sparying 4GB only, opearting system may BSoD when accessing FreeThunkTree after the last HEAP_PAGE_SEGMENT be overrided. To avoid that, more pool-fengshui should be done after sparying 4GB to make the illegal pointer diffcult more to be accessed.
  3. Setting thread priority higher can be adopted to ensure that 4GB pool-fengshui does not be interrupted as much as possible.

shorage

Although a lot of effort has been made to make the exploit stable, the exploit is still not 99.9% stable. The reason is that it spends a long time to spray 4GB, and the operating system will do a lot of memory allocation and de-allocation operations during this period, which is unpredictable.

Reference

[1]: https://www.bluetooth.com/specifications/specs/core-specification-5-3/ (Bluetooth Core Specification, Vol 3: Host, Part B: Service Discovery Protocol (SDP) Specification, 1 INTRODUCTION, 1.1 General Description)

[2]: https://www.bluetooth.com/specifications/specs/core-specification-5-3/ (Bluetooth Core Specification, Vol 3: Host, Part B: Service Discovery Protocol (SDP) Specification, 3 DATA REPRESENTATION)

[3]: https://speakerdeck.com/scwuaptx/windows-kernel-heap-segment-heap-in-windows-kernel-part-1 (Windows Kernel Heap: Segment heap in windows kernel Part 1)

[4]: https://github.com/vp777/Windows-Non-Paged-Pool-Overflow-Exploitation (Windows Non-Paged Pool Overflow Exploitation)