Function: CONFIGURE_DEVICES_ReceivedAttribute()

When receiving a ZigBee Cluster Library (ZCL) message of type READ_ATTRIBUTE_RESPONSE, each attribute is parsed inside (SMARTLINK_UTILS_ReadAttributeValue) and then forwarded onward to be handled inside the configuration module (CONFIGURE_DEVICES_ReceivedAttribute).

Function #1 - SMARTLINK_UTILS_ReadAttributeValue

  1. A string attribute (type=0x42) has a length field of 1 byte
  2. A heap buffer of size length + 1 is allocated for the data (without length checks)
  3. The data is memcpy()ed into the buffer (using length)
  4. The attribute is stored using the internal opcode of 0x10 (Array)
  5. The attribute is then passed to the second phase in a different thread

Function #2 - CONFIGURE_DEVICES_ReceivedAttribute

  1. The attribute was passed to the applicationprocess thread (applproc), and to the above function
  2. Here the internal opcode is checked again:
    i. 0x0F – string handling using strdup()
    ii. 0x10 – array handling using malloc() and memcpy()
  3. The heap buffer is allocated using 0x2B bytes (43 bytes)
  4. The heap buffer is cleared (memset(0)) using 0x2B bytes
  5. The attribute’s payload is copied to the buffer using its own length field (length)
  6. There are no checks that length <= 0x2B, thus triggering a heap-based buffer overflow

A simplified code snippet will look roughly like this:

/* First Function: SMARTLINK_UTILS_ReadAttributeValue */
case 0x42 /* ZCL_String */:
	uint8_t length = UTIL_Fetch_uint8(ppData, pLength, pIsError);
	ctx->type = 0x10; /* Array */
	ctx->pData = malloc(length + 1);
	/* EI-DBG: Should probably memset(0) using "length + 1" instead of "length" */
	memset(ctx->pData, 0, length);
	UTIL_Fetch_Buffer(ctx->pData, length, ppData, pLength);
/* Second Function: CONFIGURE_DEVICES_ReceivedAttribute */
if (ctx->type == 0x10 /* Array */)
	uint8_t pBuffer = malloc(0x2B);
	memset(pBuffer, 0, 0x2B);
	/* EI-DBG: Copying into a fixed buffer *without* checking the length field */
	memcpy(pBuffer, ctx->pData, ctx->length);