Проект выходного дня: PicoYubiPin
Уж не знаю как так вышло, но в последнее время мне то и дело хочется подебагать USB. И пока я для себя не решил, каким должен быть идеальный USB Sniffer (напишу как узнаю ^^), решил запилить небольшую демку подглядывания в USB трафике PIN от PIV в Yubikey.
Это постик о проекте выходного дня, погнали :)
Ю-би-кей?
Но сперва немного про сам 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
:
Получается, все по классике 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 (долго не выбирал, был в наличии). По итогу получилась такая схема:
Осталось взять пару usb гнезд, горстку проводков и все подсобрать:
PicoYubiPin: демо
Вот и все, самое время подключить Yubikey и попросить yubico-piv-tool проверить подпись на ключе из слота 0x9a
:
- готовимся:
- запускаем
yubico-piv-tool
: - Та-дам, вот и PIN, который мы использовали при авторизации
Если будете повторять эксперимент - используйте USB-A порт (PC или USB хаба), т.к. подключать CC пины USB-C я поленился :)
Closure
Каких-то особых выводов у меня сегодня нет. Просто будьте бдительны, прошу :)
А пока у меня все, всем кота (づ˶•༝•˶)づ♡