Перевод Уязвимости во временной блокировке Monero

Тема в разделе "Статьи", создана пользователем Mr. Pickles, 4 мар 2021.

  1. Mr. Pickles

    Команда форума Модератор Редактор

    Регистрация:
    11 сен 2017
    Сообщения:
    970
    Симпатии:
    246
    TLDR: В случае с Monero, да и в целом с криптовалютами, в основе которых лежит протокол Cryptonote, значения unlock_time транзакций интерпретировались в соответствии с местным временем, что позволяло использовать множество некритических эксплойтов, нацеленных на целостность блокчейна. Проблема была исправлена в версии 0.17.0.0 Monero. Никакого ущерба в отношении средств пользователей обнаружено не было.

    Помимо кода, активирующего новый алгоритм подписи CLSAG, версия v0.17.0.0 также содержит патч, устраняющий ошибки защиты. В примечаниях к версии патч указан как Deterministic Unlock Times (детерминированное время разблокировки). Причина появления этой новой функции ранее не сообщалась, что привело к появлению некоторых слухов относительно её природы. И вот теперь мы приводим объяснение в этой статье, а также в заявлении HackerOne. Это уже моя вторая статья из серии, посвящённой времени разблокирования в Monero. Первую статью можно найти здесь.

    Описание

    Давайте коротко вспомним, что примерно содержит транзакция Monero. Как и у Bitcoin, транзакции Monero содержат входы и выходы. В выходах указаны суммы, которые потенциально можно потратить, а входы представляют собой уже потраченные выходы предыдущей транзакции. Кроме того, при создании транзакции Monero в качестве входа выбирается один выход из n выходов предшествующей транзакции (из участников кольца), а кольцевая подпись подтверждает, что автор транзакции действительно владеет одним из них. Затем автор транзакции из этих входов создаёт новые выходы и при помощи криптографии доказывает, что суммы сбалансированы.

    Поле unlock_time в транзакции Monero указывает, когда монеты из созданной транзакции можно будет снова потратить или, если говорить точнее, когда можно будет снова потратить выходы, содержащиеся в транзакции. Эти выходы нельзя будет использовать при создании какой-либо иной транзакции до тех пор, пока не истечёт время блокировки. Деньги, передаваемые в транзакциях с временной блокировкой, можно тратить только спустя какое-то определённое время.

    Правило исполняется в соответствии с кодом консенсуса (src/cryptonote_core/blockchain.cpp) и состоит из двух частей. Если значение unlock_time будет ниже 500’000’000, оно будет истолковано как высота блока и будет сравниваться с текущей высотой блока. Если значение unlock_time предыдущего выхода, на котором основан вход транзакции, будет ниже текущей высоты блока, транзакция, использующая такой выход, считается действительной. В противном случае она таковой считаться не будет и не будет ретранслироваться.

    Если время разблокировки превышает значение 500’000’000, оно интерпретируется как время Unix в секундах, а не как высота блока. Удивительно, но эти временные значения unlock_times не сравниваются с сетевым временем или временем создания блока, но сравниваются с локальным временем машины, на которой запущен узел Monero. Такое сравнение с локальным временем представляет собой проблему. В процессе верификации транзакций узлам приходится использовать максимально возможный объём общих данных блокчейна, или, если выразить мысль кратко, правилам консенсуса приходится учитывать переменные консенсуса. Так как у разных машин время будет разным, это значение не является переменной консенсуса.

    Я обсудил этот вопрос с постоянным контрибьютором Исследовательской лаборатории Monero Ithmus (известным в Twitter как @Mitchellpkt0), и мы вместе исследовали возможные уязвимости. Я считаю их достаточно серьёзными, чтобы написать отчёт и соблюдать некоторую степень секретности, но я не рассматриваю их в качестве критических или опасных.

    Эксплойты

    Используя выходы, заблокированные при помощи тщательно подобранного значения unlock_time, можно узнавать локальное время других узлов. Злоумышленник может создать любое количество транзакций с самым широким диапазоном значений unlock_time, например, со значением unlock_time 20 блоков на время ожидаемого в будущем блока, а затем распределить это по секундным интервалам. Затем злоумышленник создаёт транзакции с этими заблокированными выходами и соединяет два узла с узлом, за которым собирается следить. Он проводит транзакцию с одного из узлов на узел, за которым будет следить, а затем проверяет, получил ли другой узел эту транзакцию. Если использованное значение unlock_time будет недействительным для злоумышленника и большей части сети в соответствии с правилами консенсуса, но действительным для узла, за которым ведётся наблюдение, транзакция злоумышленника будет ретранслирована также и на другой его узел. А это будет означать, что локальное время узла, за которым ведётся слежка, совпадает с временем транзакции злоумышленника. Используя метод двоичного поиска, злоумышленник сможет определить время узла с точностью до секунд.

    Если время такого узла будет другим (более ранним или поздним) или же если злоумышленник сможет манипулировать локальным временем узла через другой канал, то, как мы выяснили, есть два дополнительных способа использования валидации значения unlock_time.

    Предположим, время узла более позднее (более высокое значение времени Unix). Злоумышленник создаёт транзакцию, используя по крайней мере один выход с таким достаточно высоким значением unlock_time, чтобы оно было недействительным для большинства узлов в сети, но действительным для узла, временное значение которого выше. Если злоумышленник ретранслирует эту транзакцию на узел жертвы, тот подтвердит её как действительную транзакцию. Это, в частности, полезно для злоумышленника в контексте майнинга. Например, если узел жертвы является большим майнинговым пулом, его можно использовать, чтобы узел занимался бесполезной работой с блоком, который не является действительным и включает в себя заблокированную транзакцию согласно правилам остальной сети. Если злоумышленник сам является майнинговым пулом, он может воспользоваться этим с выгодой для себя самого.

    Если время узла будет более ранним (более низкое значение времени Unix), транзакции, использующие выходы с недавно истекшим временем блокировки, будут действительными и доступными для майнинга другими узлами сети, но не будут таковыми для узлов с более низким значением локального времени Unix. Фактически любой узел с более низким значением локального времени Unix отклонит любую транзакцию и любой блок, содержащий транзакции с выходами, которые ещё заблокированы с его точки зрения. Объяснение может показаться несколько запутанным, но, предположим, некто создаёт транзакцию с выходом со значением unlock_time, равным 1500000020, когда фактическое значение времени Unix на тот момент составляет 1500000040. Узлы Monero, запущенные на системе с правильным фактическим временем Unix, примут эту транзакцию, поскольку время блокировки unlock_time истекло. Узлы с более низким значением локального времени Unix, скажем, 1500000000, не примут эту транзакцию. С их точки зрения время unlock_time ещё не истекло, что делает транзакцию недействительной.

    Злоумышленник может непрерывно передавать транзакции с выходом, значение unlock_time которого будет чуть позже времени узла жертвы, и это гарантирует, что узел никогда не достигнет конца наилучшей цепочки сети. Этим можно воспользоваться для проведения краткосрочной атаки Eclipse, и даже потенциально это даёт злоумышленнику достаточно времени для того, чтобы создать более «медленную» вредоносную цепочку. Как только в конечном счёте время узла пройдёт значение unlock_time, узел снова сможет принять блок, содержащий заблокированную по времени транзакцию, из наилучшей цепочки. Так как соединения с одноранговыми узлами обрываются, если они выдают «недействительные» блоки, нельзя сказать, как долго будет поддерживаться атака, так как узлу сначала, возможно, придётся восстанавливать соединения с другими одноранговыми узлами. Эту форму атаки Eclipse можно применить против биржи (если время узла биржи будет неверным) для проведения двойной траты.

    Некоторые факторы и данные, позволяющие избежать проблемы

    Чтобы оценить риск, связанный с этой угрозой, без дальнейшего использования времени NTP и без манипуляций с временем машины, мною был составлен следующий график:
    timestamp_diff.png
    На нём показана разница между временными метками двух последовательных блоков. Отрицательное значение времени указывает на то, что по крайней мере у одного из двух блоков временная метка блока отличается от фактического времени. Блоки, созданные в прошлом, не могут появляться после новых блоков! Мы можем воспользоваться этими отрицательными разностными значениями в качестве грубой оценки (в отрыве от любого реального понимания статистической основы) для блоков, созданных машиной со значительным временным расхождением. Это относится примерно к 2,6% всех созданных блоков, что даёт нам предположительную оценку количества майнеров, уязвимых для вышеописанного эксплойта, без дальнейших манипуляций со временем.

    Разрешение

    После того как нами была обнаружена эта угроза, мы отправили отчёт HackerOne, где описали вышеуказанные проблемы и возможные исправления, необходимые для её решения.

    Подобная проблема также существовала в случае с полем nlocktime Bitcoin. Тем не менее на тот момент она была менее серьёзной, поскольку семантика nlocktime отличается от семантики поля unlock_time Monero (nlocktime влияет на завершённость самой транзакции, но не на последующие транзакции), а также потому, что вместо локального времени использовалась временная метка блока. Проблема была решена посредством BIP113, и сейчас используется среднее значение временной метки за последние 11 блоков, что позволяет определить является ли время блокировки действительным. Исправление также потребовало этапа с применением кодов операции CSV и CLTV, что позволило применить схожий механизм блокировки выходов к полю unlock_time Monero.

    Самая последняя версия Monero содержит изменение в правилах консенсуса, что делает время блокировки на базе времени Unix зависимым от временных меток предыдущих блоков. На создание оригинального патча сильно повлияло то, как обрабатываются значения времени разблокировки в случае с Bitcoin. Берётся среднее значение временной метки за последние 60 блоков и сравнивается со значением времени разблокировки.

    Затем данный патч был расширен другими рецензентами до следующей формулы:

    минимум (средне значение (временные метки последних 60 блоков)) + 61 минута, временная метка текущего конечного блока + 2 минуты)

    Здесь цель состоит в более точном представлении текущего времени.

    Полное содержание и обсуждение вопроса можно найти на GitHub. Эти новые правила валидации были введены при реализации последнего хардфорка Monero наряду с введением схемы подписи транзакций CLSAG и валидацией формата.

    Заключительные замечания по коду Monero

    Интересен тот факт, что код уже содержал функцию get_adjusted_time, которая выдавала локальное время системы, на которой работает monerod. Были некоторые зловещие комментарии, в которых отмечалось , что это не было её заданной формой. Теперь задача функции изменилась, и она используется для вычисления этого фактического «откорректированного» времени.

    Я обнаружил эту уязвимость, когда читал комментарии TODO в коде. Monero унаследовала значительную часть некомментированного кода от разработчиков Cryptonote. В частности, сомнительные места комментируются либо //TODO, либо //FIXME. Я бы настоятельно рекомендовал другим исследователям вопросов безопасности также ознакомиться с этим. Имеется примерно 120 TODO и 30 FIXME, так что ещё есть чем заняться.

    Написано 19 сентября 2020

    ---

    Источник: Monero timelock vulnerabilities

    Перевод:
    Mr. Pickles (@v1docq47)
    Редактирование:
    Agent LvM (@LvMi4)
    Коррекция:
    Kukima (@Kukima)
     
    #1 Mr. Pickles, 4 мар 2021
    Последнее редактирование: 8 мар 2021
  • О нас

    Наш сайт является одним из уникальных мест, где русскоязычное сообщество Monero может свободно общаться на темы, связанные с этой криптовалютой. Мы стараемся публиковать полезные мануалы и статьи (как собственные, так и переводы с английского) о криптовалюте Monero. Если вы хорошо владеете английским (или можете писать собственные статьи/мануалы) и хотите помочь в переводах и общем развитии Monero для русскоязычной аудитории - свяжитесь с одним из администраторов.