Що записано всередині безконтактних карток Київського метрополітену

Безконтактні карти у київському метро почали запроваджувати у 2007 році (інформація на сайті метро, ​​укр), але широкого поширення та впровадження вони набули лише до кінця 2008 року. На сьогоднішній день існують два основні типи проїзних квитків: проїзні з терміном дії та проїзні на кількості поїздок. У проїзних використовують безконтактні карти MIFARE Classic 1K.

записано
Фото — Metromuseum.net

Про вразливість чіпів MIFARE Classic стало відомо в 2007 році. Детальну історію відкриття вразливостей можна почитати у статті. Стаття хоч і 2008 року, але досі актуальна, і в ній наведено основні етапи знаходження вразливостей. Поєднавши ці знання, можна подивитися, що ж записується в карти київського метро на прикладі проїзного на кількість поїздок.

Дисклаймер: Всі дії та інформація, описані нижче, наведені виключно для розширення особистого кругозору, і не мають на меті особистої вигоди.

Налаштовуємо робоче місце

Для зчитування безконтактних карток ми будемо використовувати:

  • рідер - SCL3711;
  • відкриту бібліотеку для роботи з безконтактними картками - libnfc;
  • утиліту для отримання ключів - mfoc із комплекту nfc-tools.

безконтактних
Так як ми будемо використовувати libnfc, рідер необхідно вибирати з тих, з якими бібліотека найкраще сумісна. Вибираємо на сторінці сумісності відповідний та купуємо. Я зупинився на рідері SCL3711 із чіпом PN533 v2.7 (на фото, купував на eBay за 35$).

Бібліотека libnfc

Я буду описувати процедуру складання libnfc для Ubuntu 12.04, для інших платформ докладна інструкція по встановленню є на сайті.

Встановлюємо залежностінеобхідні для складання пакетів та роботи з SVN (для тих, у кого не встановлено):

sudo apt-get install subversion dpkg-dev debhelper dh-autoreconf libtool

Встановлюємо пакети, які необхідні для збирання libnfc:

sudo apt-get install libuсsb-dev libpcsclite-dev

Завантажуємо та розпаковуємо останню версію бібліотеки (1.6.0-rc1), із репозиторію завантажуємо файли, необхідні для збирання deb пакету:

wget http:// libnfc.googlecode.com / files / libnfc-1.6.0-rc1.tar.gz tar -xvzf libnfc-1.6.0-rc1.tar.gz cd libnfc-1.6.0 -rc1 / svn checkout http:// libnfc.googlecode.com / svn / tags / libnfc-1.6.0-rc1 / debian

Я рекомендую прибрати виведення налагоджувальних повідомлень. Для цього у файліdebian/rulesтреба забрати ключ--enable-debugу рядкуdh_auto_configure.

dpkg-buildpackage -b -us -uc

Встановлюємо залежності та пакети:

sudo apt-get install libusb- 0.1 - 4 libpcsclite1 libccid pcscd sudo dpkg -i .. / libnfc * .deb

Для перевірки рідер має бути підключений та поруч із ним (у радіусі його дії) лежати картка. Перевіряємо працездатність командою nfc-list, яка виводить список рідерів та карт, у полі рідера:

$ nfc-list nfc-list використовує libnfc 1.6.0-rc1 (експортований) NFC прилад: SCM Micro / SCL3711-NFC&R : 00 04 UID (NFCID1): 5b b8 5f 28 SAK (SEL_RES): 08

Якщо ви побачили щось подібне - значить все працює. У мене спочатку видавало помилку "libnfc.driver.pn53x_usb Змінюваний набір USB налаштування (Device or resource busy)". Справа в тому, що У Ubuntu 12.04 за замовчуванням встановлені якісь драйвера для чіпа PN533, і libnfc не може отримати доступ до пристрою. Лікується відключенням вбудованого драйвера"sudo modprobe -r pn533".

Утиліта mfoc

Використовуватимемо утиліту mfoc з останньої версії nfc-tools з репозиторію. Проблем виникнути не повинно:

svn checkout http://nfc-tools.googlecode.com/svn/trunk/mfoc/mfoc cd mfoc dpkg-buildpackage -b -us -uc sudo dpkg -i mfoc_0.10.2pre3. 1- 0 _amd64.deb

Заглядаємо всередину

Теоретична частина

Карти MIFARE Classic 1K мають 1 Кбайт пам'яті, яка розбита на 16 секторів. Кожен сектор складається із 4 блоків по 16 байт. Кожен сектор захищений двома 48-бітними ключами A та B (які зберігаються у 4 блоці).

безконтактних

Практична частина

Отримуємо ключі до карти за допомогою утиліти mfoc:

Десь за хвилину, ви отримаєте відповідь "Auth with all sectors succeeded, dumping keys to a file!". Після цього файлkeys.mfdлежать ключі до карти. Ключі для всіх карт однакові, і до речі давно викладені в інтернет (очевидно, одним із співробітників).

Для аналізу зливаємо дамп карти:

nfc-mfclassic r a new00-04-11.mfd keys.mfd

Повторюємо процедуру після кожної поїздки на метро або поповнення рахунку та отримуємо достатній набір дампів для аналізу.

Не наводитиму довгих роздумів, зупинюся на висновках. Після кожної операції змінюються дві ділянки пам'яті заголовок та історія операція.

  • дату та час операції,
  • номер терміналу, який робив запис,
  • номер запису в журналі терміналу,
  • кількість подорожей.

Номер терміналу має залежати від станції метро, ​​але я не впевнений. Дата і час записані в іншому, але такому ж чудовому форматі, як і в заголовку.

Програма на пітоні, яка читає дані з дампа, отриманого за допомогою nfc-mfclassic:

#!/usr/bin/env python import sys from struct import unpack from datetime import datetime

def get_crc ( блок ): """XOR всі байти в блоці""" повернути зменшити ( лямбда x, y: x ^ ord ( y ) , блок, 0 )

def get_bits ( i, s, l ): """ Отримати l бітів, починаючи з s""" mask = ( 1 > s ) & маска)

def print_info ( дані ): # номер картки номер = розпакувати ( ' HLH' , дані [ 0x2E2:0x2EA ] ) діяльність = діяльність1 + ( діяльність2 H' , дані [ 0x147:0x149 ] ) індекс_активності = (позиція_активності / 0x40) - 32 надрукувати "nОстання активність #t<>" . format ( номер_діяльності ) print "Positiont (<>)" . формат (позиція_діяльності, індекс_діяльності) вивести "Дата:tt" + дата_діяльності. ізоформат ( ) вивести "Лічильник:t<>" . формат ( кількість_діяльності )

# позиції останніх дій positions = [ 0xC0, 0xD0, 0xE0, 0x100, 0x110, 0x120 ] вивести "n" . формат ( "Дата" , "неприбраний" , "Термінал" , "Операція") друк "31>5>4>10>7>" . формат ( "ID" , "Type" , "Cnt" , "Type" , "Cnt")

# отримати правильний порядок i = 5 якщо activity_index > 5 else activity_index positions_ordered = positions [ ( i+ 1 ) : ] + positions [ : ( i+ 1 ) ] for pos in positions_ordered: block = data [ pos:pos+0x10 ] # if undefined або порожній блок if ( get_crc ( block ) <> 0 ) or ( ord ( block [ 0 ] ) == 0 ) : continue date_i, unk = unpack ( '> LH' , блок [ 1 : 7 ] ) term_id, term_type, term_cnt = unpack ( '>BBH' , block [ 7 : 11 ] ) op_type, op_cnt_i = unpack ( '>HH' , block [ 11 : 15 ] ) op_cnt = op_cnt_i / 0x40 h = get_bits ( date_i, 2, 5 ) m = get_bits ( date_i, 7, 6 ) s = get_bits ( date_i, 13 , 5 ) * 2 y = 2000 + get_bits ( date_i, 18 , 5 ) M = get_bits( date_i, 23 , 4 ) d = get_bits ( date_i, 27 , 5 ) date = datetime ( y, M, d, h, m, s ) print "<>#5x> ;#7x>#5x>#7x>#9x>4> " . format ( date. isoformat ( ) , unk, term_id, term_type, term_cnt, op_type, op_cnt )

def main (filename): з Open (filename, "rb") as f: data = f. read ( 1024 ) print_info ( data )

if __name__ == "__main__" : main (sys. argv [1])

Приклад роботи програми:

записано

Як можна використовувати

З іншого боку, можна збирати статистику щодо метрополітену. Оскільки кожен турнікет записує свій номер транзакції на картку, можна дивитися, скільки пройшло людей через турнікет. Наприклад, на дампі зверху видно, що через турнікет на політехнічному інституті (Номер терміналу 0x14) за добу пройшло приблизно 1000 осіб.