4 мин на чтение

Уж не знаю как так вышло, но в последнее время мне то и дело хочется подебагать USB. И пока я для себя не решил, каким должен быть идеальный USB Sniffer (напишу как узнаю ^^), решил запилить небольшую демку подглядывания в USB трафике PIN от PIV в Yubikey.

cover

Это постик о проекте выходного дня, погнали :)

Ю-би-кей?

Но сперва немного про сам Yubikey. Не бойтесь, я не псих, писать полноценный обзор не вижу смысла - их и так хватает в интернетах (например). Скажу лишь, что в общем случае Yubikey (любой 5x серии) - крутая маленькая штучка. Быстрый микрочип, поддержка кучи апплетов (FIDO2, PIV, OpenPGP, etc), аттестация, вариации user presence, не экспортируемость ключей (по модулю наличия уязвимостей ^^) и т.д.

Этот постик про PIV (Personal Identity Verification) card, поэтому далее говорим только о нем. В двух словах, PIV апплет Yubikey позволяет хранить RSA/ECDSA/Ed25519 ключевые пары, X.509* сертификат к ним и производить базовые криптографические операции. А коль это покрывает большую часть аутентификаций по ключу - мне то и дело пытаются продать идею хранения всего и вся в нем. Идея эта, скажем так, не всегда находит отклик во мне, т.к. сильно зависит от конкретного применения.

Для примера давайте представим себе сетап какого-нить работяги из разработки:

  • PIV хранит RSA/ECDSA ключ + X.509 сертификат для доступа в сеть
  • PIV хранит RSA/ECDSA/Ed25519 ключ для доступа по SSH и/или Github
  • OPGP хранит OpenPGP ключи для подписи пакетов, писем, etc
  • FIDO2 используется для многофакторной аутентификации

Если говорить о безопасносте PIV, то мы имеем:

  • экспортировать/скопировать ключи злоумышленник не может
  • любая малварь должна будет закрепиться, мутить какой-нить канал для подписи и (возможно) даже ждать подходящий момент для подписи
  • при физическом же доступе злоумышленник должен знать PIN от PIV

Погоди-ка PIN? Ну да, смарт-карта должна авторизовать запрос перед подписью, делается это с помощью PIN (6-8 символов). По умолчанию Yubikey имеет лимит в 3 попытки ввода PIN + 3 попытки ввода PUK (позволяет сбросить PIN). Звучит логично.

Так, ну и что не так?

Да все так, покуда мы считаем физический доступ out of scope. Но вынести физический доступ за скоуп в своей модели угроз это одно, а что будет в случае реального инцидента - совсем другое. Ведь скорее всего наш работяга (я, например):

  • всегда держит свой yubikey в ноуте, а значит он и теряет/оставляет их вместе
  • хранит PIN на fs и автоматически поднимает себе VPN, иначе, опять-таки, жизни нет
  • но шифрует fs, иначе у нас куда большие проблемки ;)

Это означает, что исключать физический доступ мы не можем, а любой физический доступ даст злоумышленнику и ключи для аутентификации/авторизации, и пароль для доступа к ним.

Эмммм…но ведь…

Но ведь коммуникация между Yubikey и софтом как-то шифруется? Поди на каком-нить сессионном ключе, так ведь? Ну…если найдете способ это провернуть - напишите на dev@buglloc.cc плиз, с меня пивас ;)

А пока имеем, что имеем - любая авторизация с человеком посредине - gg. Например, это смена PIN (см. APDU: Change reference data) с 123456 на 654321: cover

Получается, все по классике MitM:

  • злоумышленник начинает пассивно (!) слушать USB трафик
  • открывает крышку ноута
  • тот просыпается, идет поднимать VPN
  • тот авторизуется в PIV
  • вот злоумышленник и получает все, чего желал

На мой вкус, это столь же удивительный факт, сколь и известный. История знает разнообразные исследования, сам я проворачивал такое в 2022 с помощью эмулятора смарт-карт и малинки. В общем, я вас предупредил ;)

О PicoYubiPin

Но это все слова, давайте уже к делу! Как я и говорил в самом начале, какое-то время назад я начал мечтать о идеальном USB сниффере. А пока его не нашлось, решил написать простенький сниффер для получения PIN от PIV. Так субботним вечером и родился PicoYubiPin.

Написан PicoYubiPin на С и по своей сути является USB сниффером (cпасибо pico_usb_sniffer за вдохновение) с фильтром, который должен пропустить всего один пакетик (уровни снизу вверх):

  • USB Link Layer: PID == 0x3 || PID == 0xB (DATA0 или DATA1)
  • CCID: MessageType == XfrBlock
  • PC/SC: APDU VerifyPin (см. APDU: Verify PIN)

Пример такого пакетика в деталях:

c3  <--- USBLL (SYNC + PID "DATA0")
6f  <--- CCID: MessageType (PC_to_RDR_XfrBlock)
0e  <--|
00  <- | CCID: Message length
00  <- |
00  <--|
00  <--- CCID: Slot
16  <--- CCID: Seq No
00  <--|
00  <- | CCID: Message specific bytes
00  <--|
00  <--- APDU: CLA
20  <--- APDU: INS
00  <--- APDU: P1
80  <--- APDU: P2
08  <--- APDU: Lc
4d  <--|
65  <- |
6d  <- |
65  <- |
43  <- | APDU: Data (our PIN padded with 0xFF)
68  <- |
61  <- |
74  <--|
00  <--- APDU: Le
15  <--|
5a  <--| USBLL: CRC16

Как видите, ничего хитрого или тайного. Осталось попарсить PIN и вывести на экран, а потому давайте о хардваре!

PicoYubiPin: хардваре

Название намекает, потому основу я взял RP2040 Zero:

  • дешевый ($1-2 на распродаже)
  • популярный
  • с быстрым доступом к портам

И IIC дисплейчик 0.96’’ OLED от WeAct (долго не выбирал, был в наличии). По итогу получилась такая схема: cover

Осталось взять пару usb гнезд, горстку проводков и все подсобрать: cover

PicoYubiPin: демо

Вот и все, самое время подключить Yubikey и попросить yubico-piv-tool проверить подпись на ключе из слота 0x9a:

  • готовимся: cover
  • запускаем yubico-piv-tool : cover
  • Та-дам, вот и PIN, который мы использовали при авторизации

Если будете повторять эксперимент - используйте USB-A порт (PC или USB хаба), т.к. подключать CC пины USB-C я поленился :)

Closure

Каких-то особых выводов у меня сегодня нет. Просто будьте бдительны, прошу :)

А пока у меня все, всем кота (づ˶•༝•˶)づ♡

Разделы:

Дата изменения: