2023.09.15
CVE-2022-20445 | A-225876506 | ID | High | 10, 11, 12, 12L, 13 |
---|
patch

分析
没有校验packet中的cur_handles
,导致后续的三次整数溢出。但是我没有明白哪里会溢出啊,[5]处的循环根本进不去啊。。。

// packages/modules/Bluetooth/system/stack/sdp/sdpint.h
/* Define the SDP Connection Control Block */
struct tCONN_CB {
......
uint32_t
handles[SDP_MAX_DISC_SERVER_RECS]; /* Discovered server record handles */
uint16_t num_handles; /* Number of server handles */
uint16_t cur_handle; /* Current handle being processed */
......
};
/******************************************************************************
*
* Function process_service_search_rsp
*
* Description This function is called when there is a search response from
* the server.
*
* Returns void
*
******************************************************************************/
static void process_service_search_rsp(tCONN_CB* p_ccb, uint8_t* p_reply,
uint8_t* p_reply_end) { // p_reply指向本次解析的packet的开头;p_reply_end指向本次解析的packet的结尾
uint16_t xx;
uint16_t total, cur_handles, orig;
uint8_t cont_len;
if (p_reply + 8 > p_reply_end) {
android_errorWriteLog(0x534e4554, "74249842");
sdp_disconnect(p_ccb, SDP_GENERIC_ERROR);
return;
}
/* Skip transaction, and param len */
p_reply += 4;
BE_STREAM_TO_UINT16(total, p_reply);
// [1] cur_handles从packet中读取,可控,取值范围为0~UINT16_MAX。该变量代表本次解析需要处理的handles数量
BE_STREAM_TO_UINT16(cur_handles, p_reply);
// [2] orig的类型是uint16_t。变量orig表示之前的解析过程中处理过的handles总数
orig = p_ccb->num_handles;
// [3] 这行代码的目的是计算:加上本次解析,一共需要解析的handles数量
// 如果cur_handles足够大(比如前面读取的时候传入负数),那么这里将会发生整数回绕,p_ccb->num_handles < orig
p_ccb->num_handles += cur_handles;
if (p_ccb->num_handles == 0) {
SDP_TRACE_WARNING("SDP - Rcvd ServiceSearchRsp, no matches");
sdp_disconnect(p_ccb, SDP_NO_RECS_MATCH);
return;
}
/* Save the handles that match. We will can only process a certain number. */
if (total > sdp_cb.max_recs_per_search) total = sdp_cb.max_recs_per_search;
if (p_ccb->num_handles > sdp_cb.max_recs_per_search)
p_ccb->num_handles = sdp_cb.max_recs_per_search;
// 这个if判断的含义是:计算一个偏移量,以判断需要解析的数据是否超过 p_reply_end,即判断是否会发生越界。
// 符号 > 左边的表达式用于计算一个指针的偏移量。具体来说,它计算了从指针p_reply开始,向后移动的字节数。
// 逐步理解:(p_ccb->num_handles - orig) 的值其实就是cur_handles,代表本次解析需要处理的handles数量
// ((p_ccb->num_handles - orig) * 4):乘以4是因为handle的长度是4个字节(可以查看p_ccb->handles[]数组,该数组的类型是uint32_t)
// p_reply + ((p_ccb->num_handles - orig) * 4):将p_reply往后移动((p_ccb->num_handles - orig) * 4)个字节,然后就能理解if判断的作用了
// [4] 再次发生两次整数溢出:
// [4.1] p_ccb->num_handles - orig 的结果其实是cur_handles,一个很大的值
// [4.2] p_reply + ((p_ccb->num_handles - orig) * 4) + 1,发生整数回绕后,变得很小,从而绕过该if检查
if (p_reply + ((p_ccb->num_handles - orig) * 4) + 1 > p_reply_end) {
android_errorWriteLog(0x534e4554, "74249842");
sdp_disconnect(p_ccb, SDP_GENERIC_ERROR);
return;
}
// [5] 如果按照前面的溢出情况,xx > p_ccb->num_handles,不会进循环体里啊。。。。哪里溢出了。。。
for (xx = orig; xx < p_ccb->num_handles; xx++)
BE_STREAM_TO_UINT32(p_ccb->handles[xx], p_reply);
BE_STREAM_TO_UINT8(cont_len, p_reply);
if (cont_len != 0) {
if (cont_len > SDP_MAX_CONTINUATION_LEN) {
sdp_disconnect(p_ccb, SDP_INVALID_CONT_STATE);
return;
}
if (p_reply + cont_len > p_reply_end) {
android_errorWriteLog(0x534e4554, "68161546");
sdp_disconnect(p_ccb, SDP_INVALID_CONT_STATE);
return;
}
/* stay in the same state */
sdp_snd_service_search_req(p_ccb, cont_len, p_reply);
} else {
/* change state */
p_ccb->disc_state = SDP_DISC_WAIT_ATTR;
/* Kick off the first attribute request */
process_service_attr_rsp(p_ccb, NULL, NULL);
}
}
测试[4]中的两次整数溢出:
// 假设p_ccb->num_handles - orig == xx
#include<stdio.h>
#include<stdint.h>
void main(){
uint16_t xx = 16384;
uint16_t a = (xx*4) + 1;
printf("%hu\n", a);
}
编译执行后,结果如下:
❯ ./a.out
1