Назад к блогу

Rsync 3.5.0 в Debian: 33 CVE, новый номер протокола и что ломается в конфигах

Rsync 3.5.0 в Debian: 33 CVE, новый номер протокола и что ломается в конфигах

Обновление rsync до версии 3.5.0 в Debian закрывает 33 уязвимости разом, но вместе с исправлениями приносит изменения в номере протокола, поведении с симлинками и разборе параметров rsyncd.conf. Разбираем, какие правки затрагивают типовые конфиги и скрипты бэкапов и что стоит проверить до апгрейда, чтобы ничего не сломалось незаметно.

Debian обновил пакет rsync до версии 3.5.0, чтобы закрыть 33 CVE одним махом, а не бэкпортировать патчи по отдельности. Обновление затрагивает не только безопасность: в 3.5.x изменился номер протокола, поведение по умолчанию для символических ссылок и разбор нескольких параметров rsyncd.conf. Часть этих изменений молча меняет то, как ведут себя существующие конфиги и скрипты бэкапа.

Ниже — что именно изменилось, как это работает и что стоит проверить перед обновлением.

Что именно изменилось

В Debian пакет подняли до 3.5.0, потому что бэкпортировать все патчи по отдельности оказалось нецелесообразно: «In order to fix 33 CVEs, I have decided to bump the package to 3.5.0 rather than backporting all patches individually.» В NEWS для 3.5.0 сказано, что релиз исправляет 33 проблемы безопасности, найденные при аудите обработки путей и протокола демона, а также в ходе фаззинга протокола демона и по отчётам внешних исследователей. Идентификаторы CVE присвоены VulnCheck как CNA.

Среди перечисленных — CVE-2026-53802 (HIGH, произвольное чтение файлов через симлинки в операторских входных файлах), CVE-2026-53803 (HIGH, произвольная запись и повышение привилегий через симлинки в операторских выходных путях), CVE-2026-53785 (HIGH, создание родительских каталогов при --relative), CVE-2026-53784 (HIGH, побег из корня модуля демона при use chroot = no), CVE-2026-53793 (HIGH, побег из внутреннего модуля chroot через /./), CVE-2026-53795 (HIGH, абсолютные --temp-dir или --link-dest отключали ограничение rename/link), CVE-2026-53796 (MEDIUM, неполное ограничение перехода получателя в названный оператором каталог).

Отдельно в NEWS более раннего релиза упоминается, что в нём исправлено шесть CVE: CVE-2026-29518, CVE-2026-43617, CVE-2026-43619, CVE-2026-43618, CVE-2026-43620 и CVE-2026-45232.

Как устроены исправления

Общая идея большинства фиксов — привязать операции к файловому дескриптору, а не к пути. Путь между проверкой и использованием может быть подменён симлинком (классическая TOCTOU), поэтому вместо повторного разрешения пути по имени используется уже открытый дескриптор.

Операторские пути. Для CVE-2026-53803 выходные пути оператора теперь проходят обход с проверкой владельца каждого компонента: симлинк принимается, только если он принадлежит uid 0 или euid. Дополнительно для файла --read-batch добавлена проверка S_ISREG — так исключается подмена ожидаемого файла специальным объектом. Затронуты --log-file, --write-batch/--read-batch, а также motd, lock, early-input и --config демона.

Открытие файлов у не-daemon sender. Раньше содержимое каждого передаваемого файла открывалось по пути, и единственной защитой был leaf O_NOFOLLOW — запрет следовать симлинку только в последнем компоненте. Если непривилегированный пользователь успевал подменить родительский компонент пути на симлинк после сканирования списка файлов, открытие следовало по нему и читало файл вне дерева-источника. Теперь открытие привязано к корню передачи: файл открывается относительно уже удерживаемого дескриптора корня, а не по накопленному пути, поэтому подмена промежуточного компонента на симлинк ни к чему не приводит. Режимы -L / --copy-unsafe-links / -k по-прежнему следуют симлинкам, а --insecure-links возвращает прежнее поведение.

Обход каталогов. Раньше sender перечислял каждый исходный каталог, открывая его по накопленному пути. Гонка родительского компонента на симлинк между сканированием списка файлов и рекурсивным обходом — либо, в режиме следования daemon (-L/--copy-dirlinks/--copy-unsafe-links), симлинк на каталог внутри модуля, указывающий наружу, — позволяла sender'у с более высокими правами перечислить каталог вне дерева и скопировать имена записей, метаданные и цели симлинков. Теперь обход ограничен удерживаемым дескриптором каталога, привязанным к корню передачи или модулю.

Управляющие символы. Имена, приходящие от пира, проверяются на управляющие символы: токен конвертера имён отвергается, если содержит байт меньше пробела или равный 0x7f. В лог-файле управляющие байты в именах экранируются — C0 (кроме таба) и C1 0x80–0x9f, включая CSI 0x9b, — чтобы имя не могло внедрить терминальные escape-последовательности при просмотре лога администратором. При квотировании аргументов для удалённой оболочки обратный слэш добавляется перед символами из набора SHELL_CHARS, куда входят \t, \n и \r.

Новый номер протокола и изменения по умолчанию

В 3.5.x номер протокола изменён на 33. В 3.4.0 он был изменён на 32 — тогда это сделали, чтобы администраторам было проще проверять, что их серверы обновлены. В --stats добавлен счётчик затронутых логических блоков по 4 КиБ; он сообщается, когда оба узла согласовали протокол 33.

Изменилось поведение по умолчанию при обработке символических ссылок: не-daemon получатель следует за указанной оператором символической ссылкой на каталог назначения, только если ссылка принадлежит root или запущенному пользователю, иначе отказывает. Для proxy protocol = true без proxy protocol hosts все соединения теперь отклоняются (fail-closed), и демон предупреждает об этом при запуске.

Что ломается на практике

Конфиги rsyncd.conf. Значения auth users, начинающиеся с запятой, теперь разбиваются только по запятым, как и задокументировано. Из-за этого правило deny или :ro, называющее группу с пробелом в имени, теперь вступает в силу там, где раньше молча игнорировалось. Шаблоны hosts allow / hosts deny теперь сворачивают регистр и внутри скобочного выражения [...], так что правило вида [A-Z]* совпадает с хостами, которые раньше пропускались.

Опции командной строки. Добавлена опция --stderr=errors|all|client, заменяющая --msgs2stderr и --no-msgs2stderr (которые всё ещё принимаются). Режим stderr по умолчанию изменён на --stderr=errors; старое поведение запрашивается через --stderr=client. Нестандартный режим stderr передаётся удалённому rsync старыми именами опций без необходимости использовать --remote-option (-M). Клиентский --compress-threads ограничивается значением 8. Добавлена отсутствовавшая длинная опция --dirs. Пустой аргумент назначения теперь считается ошибкой, как и пустой аргумент источника, но это не затрагивает аргумент вида host:; использование --old-args (в том числе через RSYNC_OLD_ARGS) возвращает прежнее поведение, при котором пустой аргумент назначения трактуется как ..

rsync-ssl. Теперь проверяет сертификат сервера: openssl-бэкенд дополнительно привязывает его к запрошенному имени хоста, а stunnel и gnutls отказываются работать, если не задан RSYNC_SSL_CA_CERT или не установлен RSYNC_SSL_ALLOW_INSECURE_STUNNEL=1 / RSYNC_SSL_ALLOW_INSECURE_GNUTLS=1 для отказа от проверки.

Изменения поведения в 3.5.1. Ассемблерная реализация AVX2 rolling-checksum (--enable-roll-asm) читала до 64 байт за концом буфера, что могло приводить к SIGSEGV на границе страницы, но сообщаемые контрольные суммы не изменились. --link-dest больше не завершает передачу ошибкой, если назначение отказывается создавать жёсткую ссылку на симлинк, устройство, FIFO или сокет: такая запись теперь копируется. --out-format / --log-file-format теперь выводят литеральный % для %%. Файл .cvsignore (или -C) с токеном ! больше не прерывается ложной ошибкой. --chmod=a+s теперь устанавливает оба бита setuid и setgid, как это делает стандартная утилита смены прав.

Совместимость протокола со старыми rsync

При обмене со старым rsync версии согласуются. Подпротокол — это дополнительный номер версии внутри основного протокола, который позволяет сторонам договориться о наборе поддерживаемых возможностей; при несовпадении подпротокола сервер понижает версию протокола до общей, а затем версия урезается до версии удалённой стороны:

remote_protocol = read_int(f_in);
if (protocol_version > remote_protocol)
	protocol_version = remote_protocol;

Если удалённая версия ниже MIN_PROTOCOL_VERSION или выше MAX_PROTOCOL_VERSION, обмен завершается ошибкой «protocol version mismatch»; если ниже OLD_PROTOCOL_VERSION, выводится лишь рекомендация обновиться. Для протокола ниже 30 опции --acls и --xattrs при нелокальном сервере приводят к фатальной ошибке, а append_mode принудительно меняется с 1 на 2.

Ограничения и открытые вопросы

Авторы признают, что часть исправлений безопасности не получила CVE: дизайн rsync с fork-per-connection ограничивает влияние каждого такого дефекта собственным соединением атакующего, что эквивалентно тому, как если бы клиент сам закрыл сокет. Тем не менее они исправлены в порядке гигиены и для снижения вероятности будущей эксплуатируемой комбинации. Граница между security issue и ожидаемым поведением, по признанию авторов, в некоторых случаях была удивительно трудной для проведения.

В комментариях на Lobsters обсуждают, что LLM-код может создавать security churn: «I think only time will tell if LLM's end up producing security churn. I suspect they will to an extent because they don't really "know" anything and probably aren't going to program with "secure programming practices".» Там же высказывают сомнение, что исправления, предложенные LLM, могут сами содержать новые проблемы. Отдельно отмечается, что только один из CVE уровня high и выше имеет диапазон затронутых версий >= 3.4.1 (CVE-2026-70455, >= 3.4.2, <= 3.4.4), и он, судя по всему, не связан с вайбкодингом. Также сообщается, что rrsync в 3.5.0 был сильно сломан и исправлен в 3.5.1.

Хронология релизов

3.4.0 (14 Jan 2025, выпуск безопасности), 3.4.1 (15 Jan 2025, исправление регрессий в 3.4.0), 3.4.2 (28 Apr 2026, обычный выпуск), 3.4.3 (20 May 2026, крупный выпуск безопасности), 3.4.4 (8 Jun 2026, консервативный точечный выпуск с исправлениями регрессий поверх 3.4.3 без новых возможностей), 3.5.0 (13 Aug 2026), 3.5.1 (21 Sep 2026).

Источники

Похожее