CVE: CVE-2021-0950
Tested Versions:
- RQ1A.210205.004
Product URL(s):
Description of the vulnerability
An Out-Of-Bounds Write bug was found in nfc_nci_nxp.so
. Specifically, in file "hardware/nxp/nfc/halimpl/hal/phNxpNciHal_ext.cc"
, function phNxpNciHal_write_ext
, due to lack of proper validation of the length of supplied command prior to increasing length of it, leading to 3 bytes overflow problem. This vulnerability can be turned into a read past the end of a global buffer. An attacker can leverage this in conjunction with other vulnerabilities to execute arbitrary code in the context of NFC HIDL service.
The phNxpNciHal_write
function (defined in "hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc"
) is responsible for writing NCI command/data from Device Host to NFC controller. To invoke this function from client, the bellow code can be use in client:
hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc
#include <android/hardware/nfc/1.2/INfc.h>
using android::sp;
using INfcV1_2 = android::hardware::nfc::V1_2::INfc;
...
sp<INfcV1_2> mHal = INfcV1_2::getService();
...
mHal->write(...);
When the mHal->write()
function is invoked from the client side, the phNxpNciHal_write
in the service side is also invoked accordingly. From phNxpNciHal_write
function, phNxpNciHal_write_internal
function is invoked:
hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc
930 int phNxpNciHal_write_internal(uint16_t data_len, const uint8_t* p_data) {
/* ... */
936 if (data_len > NCI_MAX_DATA_LEN) {
937 NXPLOG_NCIHAL_E("cmd_len exceeds limit NCI_MAX_DATA_LEN");
938 android_errorWriteLog(0x534e4554, "121267042");
939 goto clean_and_return;
940 }
941 /* Create local copy of cmd_data */
942 memcpy(nxpncihal_ctrl.p_cmd_data, p_data, data_len);
943 nxpncihal_ctrl.cmd_len = data_len;
944 #ifdef P2P_PRIO_LOGIC_HAL_IMP
/* ... */
954 #endif
955
956 /* Check for NXP ext before sending write */
957 status =
958 phNxpNciHal_write_ext(&nxpncihal_ctrl.cmd_len, nxpncihal_ctrl.p_cmd_data,
959 &nxpncihal_ctrl.rsp_len, nxpncihal_ctrl.p_rsp_data);
/* ... */
988 }
p_data
is client-supplied command buffer and its length is data_len
. The maximum length of p_data
buffer is NCI_MAX_DATA_LEN
(300 bytes).
In phNxpNciHal_write_internal
function, p_data
is copied into a nxpncihal_ctrl.p_cmd_data
buffer.
nxpncihal_ctrl
is a phNxpNciHal_Control_t
global variables, p_cmd_data
and p_rsp_data
are 300 bytes buffer, are defined in "hardware/nxp/nfc/halimpl/hal/phNxpNciHal.h"
:
hardware/nxp/nfc/halimpl/hal/phNxpNciHal.h
89 typedef struct phNxpNciHal_Control {
/* ... */
120 uint16_t cmd_len;
121 uint8_t p_cmd_data[NCI_MAX_DATA_LEN];
122 uint16_t rsp_len;
123 uint8_t p_rsp_data[NCI_MAX_DATA_LEN];
/* ... */
132 } phNxpNciHal_Control_t;
Dig into phNxpNciHal_write_ext
function, which is defined in file "hardware/nxp/nfc/halimpl/hal/phNxpNciHal_ext.cc"
. There is 2 branchs:
hardware/nxp/nfc/halimpl/hal/phNxpNciHal_ext.cc
613 NFCSTATUS phNxpNciHal_write_ext(uint16_t* cmd_len, uint8_t* p_cmd_data,
614 uint16_t* rsp_len, uint8_t* p_rsp_data) {
/* ... */
679 if (bEnableMfcReader && p_cmd_data[0] == 0x21 && p_cmd_data[1] == 0x00) {
680 NXPLOG_NCIHAL_D("Going through extns - Adding Mifare in RF Discovery");
681 p_cmd_data[2] += 3;
682 p_cmd_data[3] += 1;
683 p_cmd_data[*cmd_len] = 0x80; // <-- Out-Of-Bounds Write
684 p_cmd_data[*cmd_len + 1] = 0x01; // <-- Out-Of-Bounds Write
685 p_cmd_data[*cmd_len + 2] = 0x80; // <-- Out-Of-Bounds Write
686 *cmd_len += 3;
687 status = NFCSTATUS_SUCCESS;
688 bEnableMfcExtns = false;
689 NXPLOG_NCIHAL_D(
690 "Going through extns - Adding Mifare in RF Discovery - END");
691 }
/* ... */
790 } else if (p_cmd_data[0] == 0x21 && p_cmd_data[1] == 0x00) {
791 NXPLOG_NCIHAL_D(
792 "> Going through workaround - Add Mifare Classic in Discovery Map");
793 p_cmd_data[*cmd_len] = 0x80; // <-- Out-Of-Bounds Write
794 p_cmd_data[*cmd_len + 1] = 0x01; // <-- Out-Of-Bounds Write
795 p_cmd_data[*cmd_len + 2] = 0x80; // <-- Out-Of-Bounds Write
796 p_cmd_data[5] = 0x01;
797 p_cmd_data[6] = 0x01;
798 p_cmd_data[2] += 3;
799 p_cmd_data[3] += 1;
800 *cmd_len += 3;
801 }
/* ... */
879 }
There is no bound check when increasing cmd_len
lead to 3 byte 0x80, 0x01, 0x80 can be written out of p_cmd_data
buffer. Look at the phNxpNciHal_Control_t
object definition, rsp_len
is defined following p_cmd_data
buffer. It leads to rsp_len
variable could be overwritten by 3 bytes in phNxpNciHal_write_ext
function.
Build fingerprint
$ getprop ro.build.fingerprint
google/sargo/sargo:11/RQ1A.210205.004/7038034:user/release-keys
Proof of Concept
Turning 3 bytes Out-Of-Bounds Write to Infomation Disclosure.
To successful exploit the 3 bytes Out-Of-Bounds Write vulnerability and turn it into a Infomation Disclosure, we must combine this vulnerability with a race condition, details are as follow:
phNxpNciHal_client_thread
(defined in "hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc"
) is a thread handler which handles all TML and NCI messages. NCI is sent to this thread by invoking phTmlNfc_DeferredCall
function (defined in "hardware/nxp/nfc/halimpl/tml/phTmlNfc.cc"
).
In phNxpNciHal_write_ext
function, there are many branches which lead to set return value to NFCSTATUS_FAILED
, for example:
hardware/nxp/nfc/halimpl/hal/phNxpNciHal_ext.cc
613 NFCSTATUS phNxpNciHal_write_ext(uint16_t* cmd_len, uint8_t* p_cmd_data,
614 uint16_t* rsp_len, uint8_t* p_rsp_data) {
/* ... */
623 if (p_cmd_data[0] == PROPRIETARY_CMD_FELICA_READER_MODE &&
624 p_cmd_data[1] == PROPRIETARY_CMD_FELICA_READER_MODE &&
625 p_cmd_data[2] == PROPRIETARY_CMD_FELICA_READER_MODE) {
626 NXPLOG_NCIHAL_D("Received proprietary command to set Felica Reader mode:%d",
627 p_cmd_data[3]);
628 gFelicaReaderMode = p_cmd_data[3];
629 /* frame the dummy response */
630 *rsp_len = 4;
631 p_rsp_data[0] = 0x00;
632 p_rsp_data[1] = 0x00;
633 p_rsp_data[2] = 0x00;
634 p_rsp_data[3] = 0x00;
635 status = NFCSTATUS_FAILED;
636 }
/* ... */
878 return status;
879 }
If the phNxpNciHal_write_ext
function return NFCSTATUS_FAILED
, the phTmlNfc_DeferredCall
is invoked to send a message to phNxpNciHal_client_thread
thread: hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc
930 int phNxpNciHal_write_internal(uint16_t data_len, const uint8_t* p_data) {
/* ... */
957 status =
958 phNxpNciHal_write_ext(&nxpncihal_ctrl.cmd_len, nxpncihal_ctrl.p_cmd_data,
959 &nxpncihal_ctrl.rsp_len, nxpncihal_ctrl.p_rsp_data);
960 if (status != NFCSTATUS_SUCCESS) {
961 /* Do not send packet to PN54X, send response directly */
962 msg.eMsgType = NCI_HAL_RX_MSG;
963 msg.pMsgData = NULL;
964 msg.Size = 0;
965
966 phTmlNfc_DeferredCall(gpphTmlNfc_Context->dwCallbackThreadId,
967 (phLibNfc_Message_t*)&msg);
968 goto clean_and_return;
969 }
/* ... */
988 }
The NCI_HAL_RX_MSG
message is processed in phNxpNciHal_client_thread
function as following:
hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc
171 static void* phNxpNciHal_client_thread(void* arg) {
/* ... */
270 case NCI_HAL_RX_MSG: {
271 REENTRANCE_LOCK();
272 if (nxpncihal_ctrl.p_nfc_stack_data_cback != NULL) {
273 (*nxpncihal_ctrl.p_nfc_stack_data_cback)(nxpncihal_ctrl.rsp_len,
274 nxpncihal_ctrl.p_rsp_data);
275 }
276 REENTRANCE_UNLOCK();
277 break;
278 }
/* ... */
285 }
Although lock operation is performing here, with the primitive that overwriting the rsp_len
variable without lock operation by exploit 3 bytes out-of-bound write vulnerability above, we can trigger race condition.
The size of p_rsp_data
buffer is 300 bytes and with the out-of-bound write bug, we can overwrite the value of rsp_len
variable to 0x180 = 384
. Combining 2 vulnerabilities, we can leak the 84 bytes memory behind the p_rsp_data
buffer.
Look at declaration of nxpprofile_ctrl
variable in file "hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc"
: hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc
60 /* NCI HAL Control structure */
61 phNxpNciHal_Control_t nxpncihal_ctrl;
62
63 /* NXP Poll Profile structure */
64 phNxpNciProfile_Control_t nxpprofile_ctrl;
65
66 /* TML Context */
67 extern phTmlNfc_Context_t* gpphTmlNfc_Context;
68 extern void phTmlNfc_set_fragmentation_enabled(
69 phTmlNfc_i2cfragmentation_t result);
70 /* global variable to get FW version from NCI response*/
71 uint32_t wFwVerRsp;
72 EseAdaptation *gpEseAdapt = NULL;
gpEseAdapt
looks interesting, it is a pointer to a heap memory in HAL service. This variable is set in phNxpNciHal_MinOpen
function.
In file "hardware/nxp/nfc/halimpl/hal/phNxpNciHal.cc"
, phNxpNciHal_MinOpen
function:
550 int phNxpNciHal_MinOpen (){
...
605 gpEseAdapt = &EseAdaptation::GetInstance();
...
771 }
EseAdaptation::GetInstance
function is defined in file "hardware/nxp/nfc/halimpl/src/adaptation/EseAdaptation.cpp"
: hardware/nxp/nfc/halimpl/src/adaptation/EseAdaptation.cpp
98 EseAdaptation& EseAdaptation::GetInstance() {
99 AutoThreadMutex a(sLock);
100
101 if (!mpInstance) mpInstance = new EseAdaptation; <-- allocation memory in heap.
102 return *mpInstance;
103 }
Confirm this thing by look at library file "/vendor/lib64/nfc_nci_nxp.so"
which NFC HAL code is compiled into (this library is fetched from Pixel 3a phone, firmware version: RQ1A.210205.004
, md5sum: b8b1d55f451117d5b6c67de7bd662883). Open this binary in IDA:
.bss:0000000000030610 byte_30610 % 0x12C <-- the nxpncihal_ctrl.p_rsp_data buffer
.bss:000000000003073C word_3073C % 2 ; DATA XREF: phNxpNciHal_MinOpen(void)+51C↑r
.bss:000000000003073C ; phNxpNciHal_write_unlocked(ushort,uchar const*)+48↑w ...
.bss:000000000003073E byte_3073E % 1 ; DATA XREF: sub_1A2BC+38↑r
.bss:000000000003073E ; sub_1A2BC+44↑w
.bss:000000000003073F byte_3073F % 1 ; DATA XREF: phNxpNciHal_MinOpen(void)+358↑w
.bss:000000000003073F ; phNxpNciHal_MinOpen(void)+530↑r ...
.bss:0000000000030740 byte_30740 % 1 ; DATA XREF: sub_1A2BC+46C↑r
.bss:0000000000030740 ; sub_1A2BC+480↑w ...
.bss:0000000000030741 ALIGN 4
.bss:0000000000030744 dword_30744 % 4 ; DATA XREF: sub_1A2BC:loc_1A578↑r
.bss:0000000000030744 ; sub_1AD2C+118↑w ...
.bss:0000000000030748 word_30748 % 2 ; DATA XREF: sub_1A2BC+334↑w
.bss:0000000000030748 ; sub_1F4C8+98↑r ...
.bss:000000000003074A ALIGN 4
.bss:000000000003074C byte_3074C % 1 ; DATA XREF: phNxpNciHal_MinOpen(void)+4C↑w
.bss:000000000003074C ; phNxpNciHal_MinOpen(void)+6B0↑w ...
.bss:000000000003074D ALIGN 0x10
.bss:0000000000030750 nxpprofile_ctrl % 8 ; DATA XREF: LOAD:0000000000001E38↑o
.bss:0000000000030750 ; phNxpNciHal_MinOpen(void)+268↑w ...
.bss:0000000000030758 EXPORT wFwVerRsp
.bss:0000000000030758 wFwVerRsp % 4 ; DATA XREF: LOAD:0000000000001358↑o
.bss:0000000000030758 ; phNxpNciHal_MinOpen(void)+80C↑r ...
.bss:000000000003075C ALIGN 0x20
.bss:0000000000030760 EXPORT gpEseAdapt
.bss:0000000000030760 gpEseAdapt % 8
The offset between gpEseAdapt
variable and nxpncihal_ctrl.p_rsp_data
buffer is (0x30760 - 0x30610) = 336 bytes, so it is able to leak value of gpEseAdapt
variable.
Since gpEseAdapt
point to an allocation memory chunk in heap memory of NFC HAL service, it can be used to chain with another vulnerability to execute code in NFC HAL service context.
Timeline
- 2021-03-05 Reported to Vendor
- 2021-12-02 Vendor assign CVE