На OFFZONE была серия задач на смарт-карте с интерфейсом ISO/IEC 7816. Особо внимательных даже предупредили заранее. Тут опубликовали фотографию бейджа и прямо заявили, что «бейдж можно будет взломать, если вам это будет по силам». Нам оказалось это по силам, и из этой статьи ты узнаешь — как. 🙂
Чтобы решать задачи, нужно было иметь подходящий картридер. Те, кто пришел на конференцию неподготовленным, имели возможность приобрести картридер в «Лавке старьевщика» за OFFCOIN или российские рубли. Правда, продавалось всего 30 ридеров, и их быстро разобрали.
Описание задач было приведено на этой странице.
Авторы заданий сделали очень хорошее «краткое введение» в терминологию, базовые принципы и популярные инструменты.
Общение со смарт-картами ведется при помощи USB-картридеров, которые можно найти в GAME.ZONE, а также купить в «Лавке старьевщика».
APDU-команда
Пакеты, которые воспринимает карта, называются APDU-командами. APDU-команда представляет собой последовательность 4-байтового заголовка и данных команды.
Общий формат APDU-команды:
В ответ мы получаем код ошибки, состоящий из 2 байт (SW1, SW2), и опциональное поле данных размера <= Le байт.
Для отправки APDU-команд удобно пользоваться библиотекой pyscard для Python. Например, так выглядит код для получения серийного номера Java-карты:
from smartcard.System import readers from smartcard.util import * r = readers() reader = r[0x00] # Let's assume that we only have one reader connection = reader.createConnection() connection.connect() SELECT_MANAGEMENT = [0x00, 0xA4, 0x04, 0x00, 0x08] + [0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00] (data, sw1, sw2) = connection.transmit(SELECT_MANAGEMENT) GET_CPLC_DATA = [0x80, 0xCA, 0x9F, 0x7F, 0x00] (data, sw1, sw2) = connection.transmit(GET_CPLC_DATA) print data
Если давать высокоуровневое описание Java-карты, то она представляет собой совокупность Java-апплетов, которые выполняют определенные задачи. Апплеты идентифицируются с помощью уникального Applet Identifier (AID), задаваемого при разработке апплета. По умолчанию Java-карта должна содержать Manager-апплет (AID = [0xA0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00]), который используется для управления другими апплетами на карте. Задания также представляют собой отдельные апплеты. Перед началом работы с апплетом нужно выполнить команду SELECT и AID апплета (AID=[…]; SELECT_APPLET = [0x00, 0xA4, 0x04, 0x00] + [len(AID)] + AID).
В качестве основного способа общения с картой предполагалось использовать язык Python и библиотеку pyscard. Небольшая сложность, с которой лично я столкнулся на начальном этапе, была в том, что я не нашел готового инсталлятора и не справился со сборкой pyscard под Python 3.7. А после установки старой версии pyscard 1.7.0 под Python 2.7 выяснилось, что простейшие примеры из документации pyscard падают с ошибкой. Зато связка pyscard 1.9.3 и Python 3.6.7 x64 заработала сразу и без нареканий.
Так как во всех заданиях требовалось выполнять однотипные действия (инициализация, отсылка APDU-команд и получение результатов), мне показалось правильным реализовать базовые функции в виде класса.
from smartcard.System import readers def hx(ab): return ".".join("%02X" % v for v in ab) # Convert bytes to hex def sx(ab): return "".join(chr(v) for v in ab) # Convert bytes to string SELECT_APPLET = [0x00, 0xA4, 0x04, 0x00] class OFFZONE(object): dCmds = { # List of known commands (for debugging) hx(SELECT_APPLET): "SELECT_APPLET", } def __init__(self, ind=0): self.DBG = False self.r = readers() if self.DBG: if self.r: print("Readers:") for i, v in enumerate(self.r): print("%3d: %s" % (i, v)) else: print("No readers") self.conn = self.r[ind].createConnection() self.conn.connect() def exch(self, cmd, arg=None): # Perform APDU command exchange if self.DBG: msg = [self.dCmds.get(hx(cmd), hx(cmd))] if arg is not None: msg.append(hx(arg)) print("Send: %s" % " + ".join(msg)) ext = [] if arg is None else [len(arg)] + list(arg) data, sw1, sw2 = self.conn.transmit(cmd + ext) if self.DBG: print("Recv: %02X.%02X + [%s]" % (sw1, sw2, hx(data))) return data, sw1, sw2 def select(self, AID): # Select applet by AID or task index if isinstance(AID, int): # Task index provided AID = [0x4F, 0x46, 0x46, 0x5A, 0x4F, 0x4E, 0x45, 0x30+AID, 0x10, 0x01] return self.exch(SELECT_APPLET, AID) def command(self, cla, ins, arg=None): # Execute command cmd = [cla, ins, 0, 0] return self.exch(cmd, arg) def main(): oz=OFFZONE() ## Task1(oz) ## Task2(oz) ## Task3(oz) ## Task4(oz)
Получите основные навыки для работы с Java-картой.
Похоже, при переносе описания задания затерлась часть информации, и мы потеряли корректный номер INS.
AID: [0x4f, 0x46, 0x46, 0x5a, 0x4f, 0x4e, 0x45, 0x31, 0x10, 0x01] CLA: 0x10 INS: 0x??: getFlag: [0x10, 0x??, 0x00, 0x00] 0xE0: checkFlag(flag): [0x10, 0xE0, 0x00, 0x00, 0xNN] + flag (0xNN bytes of flag)
В первой задаче требовалось найти однобайтовое значение INS, и очевидно, что проще всего это было сделать перебором 256 возможных вариантов.
def Task1(oz): print("nTask #1 (Training Mission)") oz.select(1) CLA = 0x10 INS_CheckFlag = 0xE0 for INS_GetFlag in range(0x100): data, sw1, sw2 = oz.command(CLA, INS_GetFlag) if 0x90 == sw1 and 0x00 == sw2 and data: print("INS_GetFlag=0x%02X, Flag: %s" % (INS_GetFlag, repr(sx(data)))) data, sw1, sw2 = oz.command(CLA, INS_CheckFlag, data) print("CheckFlag: %02X.%02X + [%s]" % (sw1, sw2, hx(data))) break else: print("INS_GetFlag value not found")
Легко заметить, что на большинство запросов (с неподдерживаемыми значениями INS) в качестве статуса ответа (sw1 и sw2) возвращаются значения 0x6D
и 0x00
. Если свериться с таблицей кодов APDU-ответов, становится понятно, что 6D 00 соответствует ошибке Instruction code not supported or invalid. Для правильного запроса возвращается статус 90 00
и флаг.
Материалы из последних выпусков можно покупать отдельно только через два месяца после публикации. Чтобы продолжить чтение, необходимо купить подписку.
Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке
1 год7190 р. Экономия 1400 рублей! |
1 месяц720 р. 25-30 статей в месяц |
Уже подписан?
Читайте также
Последние новости