戴尔ControlVault3驱动程序曝出多个内存越界漏洞

思科Talos披露了戴尔ControlVault3生物识别驱动程序中存在的多个内存越界读写漏洞。攻击者可通过特制的WinBioControlUnit调用触发这些漏洞,可能导致内存破坏、信息泄露甚至系统权限代码执行。

TALOS-2025-2175 || 思科Talos情报组 - 全面威胁情报

漏洞报告

TALOS-2025-2175

Dell ControlVault3 ControlVault WBDI Driver Broadcom Storage Adapter 越界写入漏洞

2025年11月17日

CVE编号 CVE-2025-36462, CVE-2025-36463, CVE-2025-36460, CVE-2025-36461

概要 Dell ControlVault3 5.14.3.0的ControlVault WBDI Driver Broadcom Storage Adapter功能中存在多个越界读取和写入漏洞。特制的WinBioControlUnit调用可导致内存破坏。攻击者可发出API调用来触发此漏洞。

确认受影响版本 以下版本经Talos测试或验证为易受攻击,或已由供应商确认易受攻击。

  • Broadcom BCM5820X
  • Dell ControlVault3 5.14.3.0

产品URL

CVSSv3评分 7.3 - CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H

CWE CWE-805 - 使用不正确长度值的缓冲区访问

详细说明 Dell ControlVault是一种基于硬件的解决方案,可安全存储密码、生物特征模板和安全代码。它可以与智能卡、近场通信(NFC)设备和指纹读取器交互。该硬件解决方案基于Broadcom BCM5820X芯片系列。

Control Vault WBDI Driver是Broadcom对Windows生物识别驱动程序接口(WBDI)的实现,利用Control Vault硬件通过Windows API提供生物识别功能。相关功能在三个DLL文件(BrcmEngineAdapter.dll、BrcmSensorAdapter.dll、BrcmStorageAdapter.dll)内实现。本报告中的漏洞均在Broadcom的Storage Adapter实现中发现。生物识别驱动程序由以SYSTEM权限运行的WinBio服务(位于svchost中)实例化。

在实现各种WinBio接口时,Adapter可以通过实现ControlUnit和/或ControlUnitPrivileged函数来提供类似于DeviceIoControl的机制。任何非特权用户都可以首先打开WinBio会话(WinBioOpenSession),锁定相关的生物识别单元(WinBioLockUnit),最后发出控制命令(WinBioControlUnit)来访问ControlUnit函数。这些控制命令是供应商定义的,因此会因供应商/型号而异。也可以通过将敏感功能实现在ControlUnitPrivileged函数而非ControlUnit函数中来限制对更关键API的访问。

BrcmStorageAdapter.dll没有实现任何ControlUnitPrivileged函数,而是在ControlUnit函数内有4个不同的命令。供参考,任何ControlUnit函数的原型如下:

1
2
3
4
5
6
7
8
9
NTSTATUS __stdcall StorageAdapterControlUnit(
    PWINBIO_PIPELINE Pipeline,
    ULONG ControlCode,
    PUCHAR SendBuffer,
    SIZE_T SendBufferSize,
    PUCHAR ReceiveBuffer,
    SIZE_T ReceiveBufferSize,
    PSIZE_T ReceiveDataSize,
    PULONG OperationStatus)

需要注意的关键点是:ControlCode指定调用者想要触发的功能,而SendBuffer、SendBufferSize、ReceiveBuffer、ReceiveBufferSize是通过调用者请求控制的参数。

BrcmStorageAdapter未能正确边界检查Receive/Send缓冲区,这导致了多个越界读取(如果SendBuffer小于预期)和越界写入(如果ReceiveBuffer太小)。

WinBioSVC内部的内存破坏可导致拒绝服务(服务崩溃)、信息泄露以及可能以SYSTEM权限执行代码。

CVE-2025-36460 - WBIO_USH_GET_IDENTITY:越界写入 当向StorageAdapter提交ControlCode为2(WBIO_USH_GET_IDENTITY)且ReceiveBufferSize满足 4 <= ReceiveBufferSize < 80 的WinBioControlUnit调用时,会触发此漏洞。这将导致最多75字节的越界写入。这些可以是空字节,或者如果利用另一个漏洞将攻击者控制的数据作为WINBIO_IDENTITY对象放入存储的数据库中,则可能是攻击者控制的数据(参见TALOS-2025-2174)。

相关代码如下:

 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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// ControlCode:
//    0: WBIO_USH_GET_TEMPLATE
//    2: WBIO_USH_GET_IDENTITY
//    3: WBIO_USH_CREATE_CHALLENGE
//    4: WBIO_USH_ADD_RECORD
NTSTATUS __stdcall StorageAdapterControlUnit(
        PWINBIO_PIPELINE Pipeline,
        ULONG ControlCode,
        PUCHAR SendBuffer,
        SIZE_T SendBufferSize,
        PUCHAR ReceiveBuffer,
        SIZE_T ReceiveBufferSize,
        PSIZE_T ReceiveDataSize,
        PULONG OperationStatus)
{
   (...) // 变量初始化

  log_stuff((int)L"StorageAdapterControlUnit() enter\n");
  if ( !Pipeline
    || !SendBuffer
    || !SendBufferSize
    || (ReceiveBuffer_ = ReceiveBuffer) == 0i64
    || (ReceiveBufferSize_ = ReceiveBufferSize) == 0   //[0]
    || (ReceiveDataSize_ = ReceiveDataSize) == 0i64
    || (OperationStatus_ = OperationStatus) == 0i64 )
  {
    log_stuff((int)L"StorageAdapterControlUnit() ERROR: invalid parameter\n");
    status = E_POINTER;
    goto DONE;
  }

   (....)
  if ( !ControlCode )
  {
     //Case 0
  }
  control_code_minus_2 = ControlCode - 2;
  if ( control_code_minus_2 )
  {
      // case 3-4
  }
  // Case ControlCode == 2
  log_stuff((int)L"StorageAdapterControlUnit() WBIO_USH_GET_IDENTITY\n");
  *(_OWORD *)ReceiveBuffer_ = 0i64;
  *((_OWORD *)ReceiveBuffer_ + 1) = 0i64;  //[1]
  *((_OWORD *)ReceiveBuffer_ + 2) = 0i64;
  *((_OWORD *)ReceiveBuffer_ + 3) = 0i64;
  *((_OWORD *)ReceiveBuffer_ + 4) = 0i64;
  *ReceiveDataSize_ = 0i64;
  if ( ReceiveBufferSize_ < 4 )            //[2]
    goto BAD_ReceiveBufferToSmall;
  if ( !WBFUSH_ExistsCVObject(*(_DWORD *)SendBuffer, &objType) )
  {
    log_stuff((int)L"StorageAdapterControlUnit. Object does not exist\n");
    status = 0;
    *OperationStatus_ = 8;
    goto DONE;
  }
  if ( objType != CV_TYPE_FINGERPRINT )
  {
    log_stuff((int)L"StorageAdapterControlUnit. Object is not an FP template\n");
    status = 0;
    *OperationStatus_ = 10;
    goto DONE;
  }
  v21 = StorageAdapterQueryByCVHandle(Pipeline, *(_DWORD *)SendBuffer); // [5]
  if ( v21 >= 0 )
  {
    v22 = Pipeline->StorageInterface;
    if ( v22 )
    {
      FirstRecord_ = v22->FirstRecord;
      if ( FirstRecord_ )
      {
        status = FirstRecord_(Pipeline);
        if ( status >= 0 )
        {
          status = BrcmStorageAdapterGetCurrentRecord(Pipeline, &v32);
          if ( status >= 0 )
          {
            Indentity = v32.Indentity;
            status = 0;
            *(_OWORD *)ReceiveBuffer_ = *(_OWORD *)&v32.Indentity->Type;                    //[3]
            *((_OWORD *)ReceiveBuffer_ + 1) = *(_OWORD *)&Indentity->Value.SecureId[12];
            *((_OWORD *)ReceiveBuffer_ + 2) = *(_OWORD *)&Indentity->Value.SecureId[28];
            *((_OWORD *)ReceiveBuffer_ + 3) = *(_OWORD *)&Indentity->Value.SecureId[44];
            *((_QWORD *)ReceiveBuffer_ + 8) = *(_QWORD *)&Indentity->Value.SecureId[60];
            *((_DWORD *)ReceiveBuffer_ + 18) = *(_DWORD *)&Indentity->Value.SecureId[68];
            ReceiveBuffer_[76] = v32.Subfactor;                                            // [4]
            *ReceiveDataSize_ = 0x50i64;
            *OperationStatus_ = 0;
            goto DONE;
          }
      // 错误处理
DONE:
  log_stuff((int)L"StorageAdapterControlUnit() exit\n");
  return status;
}

可以看到在[0]处进行检查以确保相关大小和缓冲区不为0。在[1]处,向ReceiveBuffer写入0x50个空字节(注意OWORD*转换说明了写入的数据量);在[2]处检查ReceiveBuffer的大小是否至少为4(为时已晚)。然后函数逻辑正常继续,尝试根据调用者函数提供的句柄从本地存储数据库中检索WINBIO_IDENTITY对象。如果该WINBIO_IDENTITY对象的内容由攻击者控制(并且可以被控制),那么最多可能有72(76-4)个攻击者控制的字节被越界写入。随后,Subfactor也可能被越界写入(作为一个BYTE),但该值并非完全由攻击者控制,因为Storage Api只允许有限的范围。

注意:如果 0< SendBufferSize < 4,在[5]处也可能存在越界读取,但内存对齐考虑可能使其难以有针对性地利用。

崩溃信息 以下是在触发越界写入后不久发生的崩溃示例:

  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
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
0:013> !analyze -v
*******************************************************************************
*                                                                             *
*                        异常分析                                             *
*                                                                             *
*******************************************************************************

KEY_VALUES_STRING: 1

    Key  : AV.Dereference
    Value: NullPtr

    Key  : AV.Type
    Value: Write

    Key  : Analysis.CPU.mSec
    Value: 718

    Key  : Analysis.Elapsed.mSec
    Value: 28299

    Key  : Analysis.IO.Other.Mb
    Value: 10

    Key  : Analysis.IO.Read.Mb
    Value: 11

    Key  : Analysis.IO.Write.Mb
    Value: 32

    Key  : Analysis.Init.CPU.mSec
    Value: 640

    Key  : Analysis.Init.Elapsed.mSec
    Value: 31213

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 110

    Key  : Analysis.Version.DbgEng
    Value: 10.0.27793.1000

    Key  : Analysis.Version.Description
    Value: 10.2410.02.02 amd64fre

    Key  : Analysis.Version.Ext
    Value: 1.2410.2.2

    Key  : Failure.Bucket
    Value: SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE_NULL_INSTRUCTION_PTR_INVALID_POINTER_WRITE_c0000005_wbiosrvc.dll!std::list_std::shared_ptr_Binding_,std::allocator_std::shared_ptr_Binding_____::_Unchecked_erase

    Key  : Failure.Exception.Code
    Value: 0xffffffffc0000005

    Key  : Failure.Exception.IP.Address
    Value: 0x7ff8cb794dc8

    Key  : Failure.Exception.IP.Module
    Value: wbiosrvc

    Key  : Failure.Exception.IP.Offset
    Value: 0x14dc8

    Key  : Failure.Hash
    Value: {f0bb19bb-0b4e-e7c4-4847-8fb3f45a7134}

    Key  : ProblemClass.Collapse.After.BUCKET_ID
    Value: SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE_NULL_INSTRUCTION_PTR_INVALID_POINTER_WRITE

    Key  : ProblemClass.Collapse.After.DEFAULT_BUCKET_ID
    Value: SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE_NULL_INSTRUCTION_PTR_INVALID_POINTER_WRITE

    Key  : ProblemClass.Collapse.After.PRIMARY_PROBLEM_CLASS
    Value: SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE

    Key  : ProblemClass.Collapse.Before.BUCKET_ID
    Value: SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE_NULL_INSTRUCTION_PTR_INVALID_POINTER_WRITE

    Key  : ProblemClass.Collapse.Before.DEFAULT_BUCKET_ID
    Value: SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE_NULL_INSTRUCTION_PTR_INVALID_POINTER_WRITE

    Key  : ProblemClass.Collapse.Before.PRIMARY_PROBLEM_CLASS
    Value: SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 1948

    Key  : Timeline.Process.Start.DeltaSec
    Value: 617

    Key  : WER.OS.Branch
    Value: vb_release

    Key  : WER.OS.Version
    Value: 10.0.19041.1

    Key  : WER.Process.Version
    Value: 10.0.19041.4355

FILE_IN_CAB:  sensor_dump1.dmp

NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

CONTEXT:  (.ecxr)
rax=0000000000000000 rbx=00000187b06f2640 rcx=00000187af619810
rdx=00000076fc37f6f0 rsi=00000187af619810 rdi=00000187af63b610
rip=00007ff8cb794dc8 rsp=00000076fc37f670 rbp=00000076fc37f700
 r8=0000000000000000  r9=fffffffffffe0898 r10=00000fff1bc71f6c
r11=280a112240100000 r12=0000000000000001 r13=0000000000000002
r14=00000076fc37f6f0 r15=00000187af619810
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010200
wbiosrvc!std::list<std::shared_ptr<Binding>,std::allocator<std::shared_ptr<Binding> > >::_Unchecked_erase+0x28:
00007ff8`cb794dc8 4c8900          mov     qword ptr [rax],r8 ds:00000000`00000000=????????????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ff8cb794dc8 (wbiosrvc!std::list<std::shared_ptr<Binding>,std::allocator<std::shared_ptr<Binding> > >::_Unchecked_erase+0x0000000000000028)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000001
   Parameter[1]: 0000000000000000
Attempt to write to address 0000000000000000

PROCESS_NAME:  svchost.exe

WRITE_ADDRESS:  0000000000000000

ERROR_CODE: (NTSTATUS) 0xc0000005 - 指令在 0x%p 引用了 0x%p 的内存。该内存不能为 %s。

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000001

EXCEPTION_PARAMETER2:  0000000000000000

GROUP:  WbioSvcGroup

FAULTING_SERVICE_NAME:  WbioSrvc

STACK_TEXT:
00000076`fc37f670 00007ff8`cb7d0249     : 00000187`af63b610 00000076`fc37f7b0 00000187`af63b610 00007ff8`cb7d066e : wbiosrvc!std::list<std::shared_ptr<Binding>,std::allocator<std::shared_ptr<Binding> > >::_Unchecked_erase+0x28
00000076`fc37f6a0 00007ff8`cb7c51aa     : 00000076`fc37f7b0 00000187`b0d302d0 00000000`ffffffff 00000187`b0d2a240 : wbiosrvc!BindingManager::Release+0x79
00000076`fc37f730 00007ff8`cb7c1baa     : 00000187`af645ad0 00000076`fc37f7b0 00000187`b0d2a250 00000076`fc37f960 : wbiosrvc!CSensorPool::ReleaseBinding+0x16
00000076`fc37f760 00007ff8`cb803564     : 00000187`af645ad0 00000187`b0d2a240 00000187`b0d2a240 00000187`b0d2a250 : wbiosrvc!CBiometricServiceProvider::ReleaseBinding+0x276
00000076`fc37f860 00007ff8`cb7ac9b7     : 00000000`00000006 00000000`ffffffff 00000000`00000006 00000000`ffffffff : wbiosrvc!CWinBioSrvUnlockUnit::Execute+0x204
00000076`fc37f9e0 00007ff8`cb786e53     : 00000000`00000000 00000000`00000000 00000187`b0df7a50 00000000`00000000 : wbiosrvc!_sensorControlThreadRoutine+0x9a7
00000076`fc37fde0 00007ff8`cb786ea5     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wbiosrvc!CThread::ThreadMain+0x1f
00000076`fc37fe10 00007ff8`e01f7374     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wbiosrvc!CThread::ThreadStartup+0x45
00000076`fc37fe40 00007ff8`e04bcc91     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0x14
00000076`fc37fe70 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

SYMBOL_NAME:  wbiosrvc!std::list<std::shared_ptr<Binding>,std::allocator<std::shared_ptr<Binding> > >::_Unchecked_erase+28

MODULE_NAME: wbiosrvc

IMAGE_NAME:  wbiosrvc.dll

STACK_COMMAND:  ~13s; .ecxr ; kb

FAILURE_BUCKET_ID:  SVCHOSTGROUP_WbioSvcGroup_NULL_POINTER_WRITE_NULL_INSTRUCTION_PTR_INVALID_POINTER_WRITE_c0000005_wbiosrvc.dll!std::list_std::shared_ptr_Binding_,std::allocator_std::shared_ptr_Binding_____::_Unchecked_erase

OS_VERSION:  10.0.19041.1

BUILDLAB_STR:  vb_release

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

IMAGE_VERSION:  10.0.19041.4355

FAILURE_ID_HASH:  {f0bb19bb-0b4e-e7c4-4847-8fb3f45a7134}

Followup:     MachineOwner
---------

这是托管StorageAdapter的winbiosrvc中的空指针写入崩溃。这是因为越界写入破坏了相邻内存导致崩溃。由于与适配器一起加载的某个dll缺乏ASLR,如果攻击者可以控制正在写入的数据(前面代码描述中的步骤[3]),这可能会演变成对其数据部分的有针对性的破坏。

CVE-2025-36461 - WBIO_USH_GET_TEMPLATE:越界读/写 当向StorageAdapter提交ControlCode为0(WBIO_USH_GET_TEMPLATE)且满足 0 < ReceiveBufferSize < 4 和/或 0 < SendBufferSize < 76 的WinBioControlUnit调用时,会触发此漏洞。前者将导致最多3字节的越界写入,后者将触发最多75字节的越界读取。

相关代码如下:

  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
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// ControlCode:
//    0: WBIO_USH_GET_TEMPLATE
//    2: WBIO_USH_GET_IDENTITY
//    3: WBIO_USH_CREATE_CHALLENGE
//    4: WBIO_USH_ADD_RECORD
NTSTATUS __stdcall StorageAdapterControlUnit(
        PWINBIO_PIPELINE Pipeline,
        ULONG ControlCode,
        PUCHAR SendBuffer,
        SIZE_T SendBufferSize,
        PUCHAR ReceiveBuffer,
        SIZE_T ReceiveBufferSize,
        PSIZE_T ReceiveDataSize,
        PULONG OperationStatus)
{
   (...) // 变量初始化

  log_stuff((int)L"StorageAdapterControlUnit() enter\n");
  if ( !Pipeline
    || !SendBuffer
    || !SendBufferSize
    || (ReceiveBuffer_ = ReceiveBuffer) == 0i64
    || (ReceiveBufferSize_ = ReceiveBufferSize) == 0   //[0]
    || (ReceiveDataSize_ = ReceiveDataSize) == 0i64
    || (OperationStatus_ = OperationStatus) == 0i64 )
  {
    log_stuff((int)L"StorageAdapterControlUnit() ERROR: invalid parameter\n");
    status = E_POINTER;
    goto DONE;
  }

   (....)
  if ( !ControlCode )
  {
  log_stuff((int)L"StorageAdapterControlUnit() WBIO_USH_GET_TEMPLATE\n");
    *(_DWORD *)ReceiveBuffer_ = 0;              // 越界写入   [1]
    *ReceiveDataSize_ = 0i64;
    if ( ReceiveBufferSize_ < 4 )               // [2]
      goto BAD_ReceiveBufferToSmall;
    SubFactor = SendBuffer[76];                 // [3]
    if ( SubFactor == 0xFF )                    // 越界读取 ?
    {
      log_stuff((int)L"StorageAdapterControlUnit() ERROR: Unsupported sub factor\n");
      status = 0x80070057;
      goto DONE;
    }
    StorageInterface = Pipeline->StorageInterface;
    if ( StorageInterface && (QueryBySubject = StorageInterface->QueryBySubject) != 0i64 )
    {
      v28 = QueryBySubject(Pipeline, (PWINBIO_IDENTITY)SendBuffer, SubFactor);   // [4]
      if ( v28 >= 0 )
      {
        v29 = Pipeline->StorageInterface;
        if ( v29 )
        {
          FirstRecord = v29->FirstRecord;
          if ( FirstRecord )
          {
            status = FirstRecord(Pipeline);
            if ( status >= 0 )
            {
              status = BrcmStorageAdapterGetCurrentRecord(Pipeline, &v32);
              if ( status >= 0 )
              {
                memmove(ReceiveBuffer_, (const void *)v32.TemplateBlob, v32.TemplateBlobSize);
                *ReceiveDataSize_ = 4i64;
                status = 0;
                *OperationStatus_ = 0;
                goto DONE;
              }
              goto LABEL_60;
            }
LABEL_63:
            log_stuff((int)L"StorageAdapterControlUnit. WbioStorageFirstRecord Failed\n");
            *OperationStatus_ = 1;
            goto DONE;
          }
BAD_E_NOTIMPL:
          status = E_NOTIMPL;
          goto LABEL_63;
        }
LABEL_62:
        status = E_POINTER;
        goto LABEL_63;
      }
      log_stuff((int)L"StorageAdapterControlUnit. WbioStorageQueryBySubject Failed\n");
      if ( v28 == WINBIO_E_DATABASE_NO_RESULTS )
      {
        *OperationStatus_ = 1;
        status = 0;
        goto DONE;
      }
    }
    else
    {
      log_stuff((int)L"StorageAdapterControlUnit. WbioStorageQueryBySubject Failed\n");
    }
    status = 0;
    goto DONE;
}
  control_code_minus_2 = ControlCode - 2;
  if ( control_code_minus_2 )
  {
      // case 3-4
  }
  // Case ControlCode == 2
       (...)

      (...) // 错误处理
DONE:
  log_stuff((int)L"StorageAdapterControlUnit() exit\n");
  return status;
}

在[0]处进行检查以确保指针已定义且大小不为零。然后,在[1]处写入一个空DWORD,如果 0< ReceiveBufferSize < 4,则可能越界,随后在[2]处进行边界检查。在[0]处的初始空检查之后,没有对SendBuffer进行后续检查,然后在[3]和[4]处可以越界读取。后一个调用假设SendBuffer包含一个WINBIO_IDENTITY对象,并尝试在本地数据库中查找它。如果攻击者可以控制本地数据库的部分内容,这可以作为一个oracle来泄露几个字节的堆内存。

CVE-2025-36462 - WBIO_USH_CREATE_CHALLENGE:越界写入 当向StorageAdapter提交ControlCode为3(WBIO_USH_CREATE_CHALLENGE)且满足 0 < ReceiveBufferSize < 4 的WinBioControlUnit调用时,会触发此漏洞。最多三个空字节将被写入到ReceiveBuffer的末尾之外。

相关代码如下:

 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
60
61
62
63
64
65
66
67
68
69
// ControlCode:
//    0: WBIO_USH_GET_TEMPLATE
//    2: WBIO_USH_GET_IDENTITY
//    3: WBIO_USH_CREATE_CHALLENGE
//    4: WBIO_USH_ADD_RECORD
NTSTATUS __stdcall StorageAdapterControlUnit(
        PWINBIO_PIPELINE Pipeline,
        ULONG ControlCode,
        PUCHAR SendBuffer,
        SIZE_T SendBufferSize,
        PUCHAR ReceiveBuffer,
        SIZE_T ReceiveBufferSize,
        PSIZE_T ReceiveDataSize,
        PULONG OperationStatus)
{
   (...) // 变量初始化

  log_stuff((int)L"StorageAdapterControlUnit() enter\n");
  if ( !Pipeline
    || !SendBuffer
    || !SendBufferSize
    || (ReceiveBuffer_ = ReceiveBuffer) == 0i64
    || (ReceiveBufferSize_ = ReceiveBufferSize) == 0   //[0]
    || (ReceiveDataSize_ = ReceiveDataSize) == 0i64
    || (OperationStatus_ = OperationStatus) == 0i64 )
  {
    log_stuff((int)L"StorageAdapterControlUnit() ERROR: invalid parameter\n");
    status = E_POINTER;
    goto DONE;
  }

   (....)
  if ( !ControlCode )
  {
  // case 0
    (...)
  }
  control_code_minus_2 = ControlCode - 2;
  if ( control_code_minus_2 )
  {
      control_code_minus_3 = control_code_minus_2 - 1;
      if ( control_code_minus_3 )
      {
         if ( control_code_minus_3 == 1 )
         {
                           //case 4
         }
     }
     else {
         //case 3
         *(_DWORD *)ReceiveBuffer_ = 0;         //[1]
         *ReceiveDataSize_ = 0i64;
        if ( ReceiveBufferSize_ >= 4 )          //[2]
        {
            (...)  //WBIO_USH_CREATE_CHALLENGE函数的逻辑
            goto DONE;
        }

       BAD_ReceiveBufferToSmall:
        log_stuff((int)L"StorageAdapterControlUnit. Receive buffer too small\n");
        status = TPC_E_INSUFFICIENT_BUFFER;
        goto DONE;
      }
  }
  //case 2
DONE:
  log_stuff((int)L"StorageAdapterControlUnit() exit\n");
  return status;
}

可以看到在[0]处代码确保ReceiveBuffer不为空或大小不为零。在[1]处向ReceiveBuffer写入4个空字节,在[2]处验证ReceiveBuffer的大小是否至少为4字节。这导致在 0 < ReceiveBufferSize < 4 的情况下,最多有3字节的越界写入。内存对齐约束可能使此漏洞难以触发。

CVE-2025-36463 - WBIO_USH_ADD_RECORD:越界读取 当向StorageAdapter提交ControlCode为4(WBIO_USH_ADD_RECORD)且满足 0 < SendBufferSize < 104 的WinBioControlUnit调用时,会触发此漏洞。可以从SendBuffer末尾之后读取不同数量的字节。围绕此利用的约束相当严格,可能使得此漏洞的利用可能性较低,或仅限于拒绝服务。

相关代码如下:

  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
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// ControlCode:
//    0: WBIO_USH_GET_TEMPLATE
//    2: WBIO_USH_GET_IDENTITY
//    3: WBIO_USH_CREATE_CHALLENGE
//    4: WBIO_USH_ADD_RECORD
NTSTATUS __stdcall StorageAdapterControlUnit(
        PWINBIO_PIPELINE Pipeline,
        ULONG ControlCode,
        PUCHAR SendBuffer,
        SIZE_T SendBufferSize,
        PUCHAR ReceiveBuffer,
        SIZE_T ReceiveBufferSize,
        PSIZE_T ReceiveDataSize,
        PULONG OperationStatus)
{
   (...) // 变量初始化

  log_stuff((int)L"StorageAdapterControlUnit() enter\n");
  if ( !Pipeline
    || !SendBuffer
    || !SendBufferSize
    || (ReceiveBuffer_ = ReceiveBuffer) == 0i64
    || (ReceiveBufferSize_ = ReceiveBufferSize) == 0   //[0]
    || (ReceiveDataSize_ = ReceiveDataSize) == 0i64
    || (OperationStatus_ = OperationStatus) == 0i64 )
  {
    log_stuff((int)L"StorageAdapterControlUnit() ERROR: invalid parameter\n");
    status = E_POINTER;
    goto DONE;
  }

   (....)
  if ( !ControlCode )
  {
  // case 0
    (...)
  }
  control_code_minus_2 = ControlCode - 2;
  if ( control_code_minus_2 )
  {
      control_code_minus_3 = control_code_minus_2 - 1;
      if ( control_code_minus_3 )
      {
         if ( control_code_minus_3 == 1 )
         {
                           //case 4

            log_stuff((int)L"StorageAdapterControlUnit() WBIO_USH_ADD_RECORD\n");
            if ( *(_QWORD *)(SendBuffer + 84) == *(_QWORD *)&StorageContext->Challenge.field_0         //[1]
              && *(_QWORD *)(SendBuffer + 92) == *((_QWORD *)&StorageContext->Challenge.field_0 + 1)
              && *((_DWORD *)SendBuffer + 25) == StorageContext->Challenge.field_10 )
            {
              if ( WBFUSH_ExistsCVObject(*((_DWORD *)SendBuffer + 20), &objType) )                //    [2]
              {
                if ( objType == CV_TYPE_FINGERPRINT )
                {
                  v32.Subfactor = SendBuffer[76];                                                // [3]
                  v32.IndexVector = 0i64;
                  v32.IndexElementCount = 0i64;
                  v32.PayloadBlob = 0i64;
                  v32.PayloadBlobSize = 0i64;
                  v32.Indentity = (WINBIO_IDENTITY *)SendBuffer;                                 // [4]
                  v32.TemplateBlob = (__int64)(SendBuffer + 80);// templateBlob_cv_handle
                  v32.TemplateBlobSize = 4i64;
                  if ( StorageContext->BrcmStorageAdapterAddRecord(Pipeline, &v32) >= 0 )        // [5]
                  {
                    *ReceiveDataSize_ = 0i64;
                    status = 0;
                    *OperationStatus_ = 0;
                  }
                  else
                  {
                    log_stuff((int)L"StorageAdapterControlUnit. BrcmAddRecord\n");
                    *OperationStatus_ = 9;
                    status = 0;
                  }
                }
                else
                {
                  log_stuff((int)L"StorageAdapterControlUnit. Not a FP template\n");
                  status = 0;
                  *OperationStatus_ = 10;
                }
              }
              else
              {
                log_stuff((int)L"StorageAdapterControlUnit. WBFUSH_ExistsCVObject Failed\n");
                status = 0;
                *OperationStatus_ = 8;
              }
            }
            else
            {
              log_stuff((int)L"StorageAdapterControlUnit. WBIO_USH_ADD_RECORD authorization Failed\n");
              status = 0;
              *OperationStatus_ = 7;
            }

     }
     
     else {
         //case 3
         (...)
  
       BAD_ReceiveBufferToSmall:
        log_stuff((int)L"StorageAdapterControlUnit. Receive buffer too small\n");
        status = TPC_E_INSUFFICIENT_BUFFER;
        goto DONE;
      }
  }
  //case 2
DONE:
  log_stuff((int)L"StorageAdapterControlUnit() exit\n");
  return status;
}

可以看到在[0]处代码确保SendBuffer不为空或大小不为零。在[1]处读取越界数据(范围SendBuffer[84:104])并与一个20字节的Challenge进行比较。该Challenge默认为空,但可以在运行时操作中更改(在这种情况下,StorageContext->Challenge 将不受攻击者控制)。如果Challenge检查成功,则从SendBuffer[80:84]读取数据作为模板句柄,将在[2]处用于查找ControlVault设备以确认其与设备上存储的模板匹配。成功后,SendBuffer[0:76]作为WINBIO_IDENTITY对象存储在本地数据库中(在[4]、[5]处),前提是满足某些检查。堆塑造技术可能用于触发这样一种情况:在两个攻击者控制的Blob之间创建一个约70字节的“洞”,在这种情况下,本地数据库中存储的身份可以被检索,并通过WBIO_USH_GET_IDENTITY命令用于泄露数据。此攻击的许多约束使其成功的可能性较低,而由于读取未映射的内存区域导致的拒绝服务是此攻击更可能的结果;这将导致WinBio服务崩溃。

时间线

  • 2025-04-22 - 向供应商披露
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计