Всех, кто исполь­зует отно­ситель­но новые вер­сии Java-фрей­мвор­ка Oracle, в минув­шую сре­ду ждал неп­рият­ный сюр­приз. Иссле­дова­тель из ком­пании ForgeRock Нил Мэд­ден сооб­щил о кри­тичес­кой уяз­вимос­ти в Java, которая поз­воля­ет зло­умыш­ленни­кам лег­ко под­делывать сер­тифика­ты и под­писи TLS, сооб­щения двух­фактор­ной аутен­тифика­ции и учет­ные дан­ные авто­риза­ции. Эксперт опуб­ликовал в сети под­робное опи­сание уяз­вимос­ти, с основны­ми тезиса­ми которо­го мы сегод­ня хотим тебя поз­накомить.

Хронология событий

Нил Мэд­ден обна­ружил эту ошиб­ку в OpenJDK еще 11 нояб­ря 2021 года и сра­зу же сооб­щил о ней в Oracle. 18 нояб­ря раз­работ­чик под­твер­дил наличие проб­лемы и пообе­щал добавить исправ­ление в сле­дующее кри­тичес­кое обновле­ние безопас­ности, которое выш­ло 19 апре­ля 2022 года. В этот же день ForgeRock опуб­ликовал отчет с опи­сани­ем уяз­вимос­ти.

В популяр­ном бри­тан­ском телесе­риале «Док­тор Кто» есть пов­торя­ющий­ся сюжет: глав­ный герой с успе­хом выпуты­вает­ся из раз­личных неп­рият­ностей, показы­вая окру­жающим совер­шенно пус­той документ, изго­тов­ленный из спе­циаль­ной «пси­хобу­маги». Эта бумага зас­тавля­ет смот­рящего на нее челове­ка видеть то, что хочет про­демонс­три­ровать ему вла­делец арте­фак­та: про­пуск, удос­товере­ние полицей­ско­го, судеб­ный ордер или что‑то иное. Схо­жим обра­зом работа­ет уяз­вимость, обна­ружен­ная в нес­коль­ких недав­них выпус­ках Java, вер­нее, в механиз­ме широко исполь­зуемо­го алго­рит­ма с откры­тым клю­чом для соз­дания циф­ровых под­писей ECDSA. Этой уяз­вимос­ти, получив­шей обоз­начение CVE-2022-21449, под­верже­ны вер­сии Java 15, 16, 17 и 18, вышед­шие до кри­тичес­кого обновле­ния от апре­ля 2022 года. Кро­ме того, в офи­циаль­ном сооб­щении Oracle так­же упо­мина­ются более ста­рые вер­сии Java, вклю­чая 7, 8 и 11. С дру­гой сто­роны, в ре­комен­даци­ях OpenJDK перечис­лены толь­ко вер­сии 15, 17 и 18, зат­ронутые этой кон­крет­ной уяз­вимостью.

С исполь­зовани­ем CVE-2022-21449 зло­умыш­ленни­ки могут лег­ко под­делать некото­рые типы SSL-сер­тифика­тов и SSL-рукопо­жатий, что, в свою оче­редь, поз­воля­ет перех­ватывать и изме­нять сооб­щения. Кро­ме того, ста­новит­ся воз­можной под­мена под­писан­ных JSON Web Tokens (JWT), дан­ных SAML, токенов иден­тифика­ции OIDC и даже сооб­щений аутен­тифика­ции WebAuthn. Фак­тичес­ки это и есть пол­ный ана­лог кинош­ной «пси­хобу­маги», толь­ко в элек­трон­ной фор­ме.

Серь­езность этой проб­лемы труд­но недо­оце­нить. Если ты исполь­зуешь под­писи ECDSA для любого из перечис­ленных механиз­мов безопас­ности, а на сер­вере уста­нов­лена уяз­вимая вер­сия Java, зло­умыш­ленник может без тру­да обой­ти эти механиз­мы. В реаль­нос­ти поч­ти все устрой­ства WebAuthn/FIDO (вклю­чая Yubikeys) исполь­зуют под­писи ECDSA, а мно­гие пос­тавщи­ки OIDC — токены JWT, под­писан­ные тем же методом.

Oracle прис­воила это­му CVSS оцен­ку 7,5 бал­ла, пос­читав, что уяз­вимость не ока­зыва­ет серь­езно­го вли­яния на кон­фиден­циаль­ность или дос­тупность дан­ных, одна­ко иссле­дова­тели из ForgeRock оце­нили проб­лему в 10 бал­лов из‑за широко­го спек­тра воз­дей­ствий на раз­личные фун­кции в кон­тек­сте управле­ния дос­тупом. Как же все‑таки работа­ет уяз­вимость CVE-2022-21449? Что­бы разоб­рать­ся, необ­ходимо нем­ного углу­бить­ся в теорию.

 

Подписи ECDSA

ECDSA рас­шифро­выва­ется как алго­ритм циф­ровой под­писи на эллипти­чес­ких кри­вых (Elliptic Curve Digital Signature Algorithm) и широко исполь­зует­ся в качес­тве стан­дарта для под­писи всех видов циф­ровых докумен­тов. По срав­нению со ста­рым стан­дартом RSA клю­чи и под­писи на осно­ве эллипти­чес­кой крип­тогра­фии име­ют нам­ного мень­шие раз­меры в бай­тах, но при этом обес­печива­ют экви­вален­тную безопас­ность, в резуль­тате чего они при­меня­ются в тех слу­чаях, ког­да раз­мер име­ет боль­шое зна­чение. Нап­ример, стан­дарт WebAuthn для двух­фактор­ной аутен­тифика­ции поз­воля­ет про­изво­дите­лям устрой­ств выбирать из широко­го спек­тра алго­рит­мов соз­дания под­писи, но на прак­тике поч­ти все про­изве­ден­ные на сегод­няшний день устрой­ства под­держи­вают толь­ко ECDSA (замет­ным исклю­чени­ем явля­ется раз­ве что Windows Hello, которая исполь­зует RSA, пред­положи­тель­но для сов­мести­мос­ти со ста­рым обо­рудо­вани­ем TPM).

Не вда­ваясь в тех­ничес­кие детали, мож­но ска­зать, что под­пись ECDSA сос­тоит из двух зна­чений, называ­емых r и s. Что­бы про­верить такую под­пись, верифи­катор реша­ет урав­нение, вклю­чающее зна­чения r, s, откры­тый ключ под­писав­шего и хеш сооб­щения. Если две час­ти урав­нения рав­ны, под­пись счи­тает­ся дей­стви­тель­ной, в про­тив­ном слу­чае она откло­няет­ся.

Од­на часть урав­нения дол­жна быть рав­на r, а дру­гая часть умно­жает­ся на r и зна­чение, получен­ное из s. Оче­вид­но, было бы очень пло­хо, если бы r и s ока­зались рав­ны 0, потому что тог­да мы про­веря­ли бы равенс­тво 0 = 0 ⨉ [куча вещей], которое будет истинным незави­симо от зна­чения «кучи вещей». При­том что эта самая «куча вещей» — важ­ные дан­ные, такие как сооб­щение и откры­тый ключ. Вот почему самая пер­вая про­вер­ка в алго­рит­ме ECDSA выпол­няет­ся с целью удос­товерить­ся, что зна­чения r и s >= 1.

До­гадай­ся, какую про­вер­ку забыли в Java? Бин­го: валида­тор под­писи ECDSA в Java не про­верял, рав­ны ли r или s нулю, поэто­му ты при желании можешь соз­дать под­пись с нулевы­ми зна­чени­ями этих парамет­ров. Тог­да Java при­мет такую под­пись для любого сооб­щения или пуб­лично­го клю­ча как дей­стви­тель­ную.

Вот инте­рак­тивный сеанс JShell, показы­вающий реали­зацию этой уяз­вимос­ти, — здесь исполь­зует­ся абсо­лют­но пус­тая под­пись, которая при­нима­ется в качес­тве дей­стви­тель­ной:

| Welcome to JShell -- Version 17.0.1
| For an introduction type: /help intro
jshell> import java.security.*
jshell> var keys = KeyPairGenerator.getInstance("EC").generateKeyPair()
keys ==> java.security.KeyPair@626b2d4a
jshell> var blankSignature = new byte[64]
blankSignature ==> byte[64] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... , 0, 0, 0, 0, 0, 0, 0, 0 }
jshell> var sig = Signature.getInstance("SHA256WithECDSAInP1363Format")
sig ==> Signature object: SHA256WithECDSAInP1363Format<not initialized>
jshell> sig.initVerify(keys.getPublic())
jshell> sig.update("Hello, World".getBytes())
jshell> sig.verify(blankSignature)
$8 ==> true
// Oops, that shouldn't have verified...

Ква­лифи­катор InP1363Format упро­щает демонс­тра­цию ошиб­ки. Под­писи в фор­мате ASN.1 DER могут исполь­зовать­ся таким же обра­зом: прос­то сна­чала нуж­но нем­ного повозить­ся с кодиров­кой. Но обра­ти вни­мание, что JWT и дру­гие стан­дарты при­меня­ют необ­работан­ный фор­мат IEEE P1363.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии