[하루한줄] CVE-2025-24919 : Dell ControlVault3의 입력 검증 미흡으로 인한 RCE 취약점
URL
Target
- Broadcom BCM5820X
- Dell ControlVault3 5.14.3.0
- Dell ControlVault3 Driver and Firmware prior to 5.15.10.14
- Dell ControlVault3 Plus Driver and Firmware prior to 6.2.26.36
Explain
Dell ControlVault3은 생체인식, 보안코드 등을 저장하는 하드웨어 보안 솔루션으로, Windows 환경에서는 bcmbipdll.dll 내 cvhDecapsulateCmd
함수를 통해 펌웨어와 통신합니다.
먼저 펌웨어에 명령어를 보낼 때 InitParam_List
함수를 통해 두 개의 파라미터가 생성되고, 모두 타입과 길이를 포함한 8바이트 헤더와 데이터로 이뤄져있습니다. 이 두 데이터는 하나는 인풋, 하나는 아웃풋으로 사용되는데, 펌웨어로부터 전달받는 아웃풋은 cvhDecapsulateCmd
함수를 통해 데이터가 처리됩니다.
__int64 __fastcall cvhDecapsulateCmd(
cv_out_buffer *encapsulated_cmd, //data coming from the firmware
cv_session_info *session,
unsigned int *pNumParams, // ouput of the function, total number of parameters unpacked
cv_param_list_entry **dst_params) //[0]
{
(...) // skipped variable definition
dst_params_ = dst_params;
pNumParams_ = pNumParams;
cur_param_num = 0i64;
if ( (encapsulated_cmd->header.field_A & 1) == 0 || (decryptedCmdSize = 0, encapsulated_cmd->header.field_8 == 4) )
{
cur_pos_buffer = &encapsulated_cmd->first_byte;
v25 = &encapsulated_cmd->first_byte + encapsulated_cmd->header.cmd_size;
v39 = v25;
goto SKIP_DECRYPTION;
}
(...) // skipped case where data is encrypted
SKIP_DECRYPTION:
if ( cur_pos_buffer >= v25 )
{
*pNumParams_ = cur_param_num;
goto DONE;
}
param_type = cur_pos_buffer->paramType;
if ( cur_pos_buffer->paramType >= 2 && cur_pos_buffer->paramType - 2 >= 2 ) // ParamType is between 0 and 3
{
if ( bLoggingStuff )
{
log_more_stuff(
"d:\\BuildAutomation\\BCMSecurity_int_cs_5.14\\sw\\phoenixII\\host\\windows\\BCMSecurity\\BCMSecurity\\src\\B"
"CMIDll\\BCMILib_Src\\HelperFunctions.c");
log_stuff("%s %s%s:%d %s\n", "Invalid Status Type", v51, &pNumParams_, 888i64, "is_valid_param_type");
}
status_ = INVALID_PARAM_TYPE;
goto DONE;
}
status_ = 0;
status = encapsulated_cmd->header.status;
if ( !status || status == 143 || status == 41 || status == 154 && param_type != CV_PACK_MAGIC_BUFFER )
{
COPY_PARAM_CONTENT:
Size = 0i64;
param_size = cur_pos_buffer->paramLen + 8;
decryptedCmdSize = param_size;
v49 = param_size;
param_buffer = j__malloc_base(param_size); // Allocates a buffer with a +8 size to account for the header (type, length)
param_buffer_ = param_buffer;
Size = param_buffer;
if ( !param_buffer )
{
status_ = MEM_ERROR;
free_stuff(&Size, 1u);
goto DONE;
}
memset(param_buffer, 0, param_size);
v32 = param_size;
param_size_aligned = param_size & 0xFFFFFFFC;
param_size_reminder_non_aligned = v32 & 3;
if ( !param_size_reminder_non_aligned )
param_size_aligned = decryptedCmdSize;
v35 = param_buffer_;
if ( param_size_aligned >= 4 )
{
memmove(param_buffer_, cur_pos_buffer, param_size_aligned); // [1] copy to param_buffer the parameter entry, including the header coming from the cmd header
v35 = ¶m_buffer_[param_size_aligned];
Size = ¶m_buffer_[param_size_aligned];
cur_pos_buffer = (cur_pos_buffer + param_size_aligned);
}
cvhDecapsulateCmd
함수에서는 먼저 인자로 전달된 데이터를 헤더와 함께 저장하기 위해 인자로 전달된 헤더에 저장된 길이보다 8 크게 할당하고 저장합니다.
if ( param_size_reminder_non_aligned )
{
memmove(v35, cur_pos_buffer, param_size_reminder_non_aligned);
cur_pos_buffer = (cur_pos_buffer + 4);
}
Size = param_buffer_;
v36 = &dst_params_[cur_param_num];
**v36 = 0i64;
memmove(*v36, param_buffer_, v49); //[2] copy the whole parameter entry including its header to the cur_param_num entry of the dst_params buffer (aka the decapsulated parameters)
if ( param_buffer_ )
free(param_buffer_);
goto NEXT_PARAM;
}
[1]에서 복사한 헤더 정보가 포함된 데이터를 출력 구조체 리스트로 순차적으로 복사합니다.
(...) // skipped handling of other edge cases
NEXT_PARAM:
cur_param_num = (cur_param_num + 1);
cur_param_num_ = cur_param_num;
if ( cur_param_num > 0x18 ) // [3] there's no check if the number of parameters parsed doesn't go beyond the request number of parameters in *pNumParameters, leading to a potential wild memcpy at [2]
{
status_ = CV_STATUS_TOO_MANY_PARAM;
goto DONE;
}
}
}
반복적으로 펌웨어로부터 수신된 데이터를 복사하면서 0x18 개 이상 복사할 경우 데이터 복사를 중단합니다. 하지만 출력 데이터 구조체 리스트에 몇 개의 구조체가 할당되었고 복사한 갯수가 해당 리스트 길이보다 높은지는 검사하지 않습니다. 만약 악의적으로 조작된 펌웨어와 통신할 경우, 이를 통해 OOB를 만들어낼 수 있습니다.
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.