
Исследовательские материалы по атакам через побочный канал уведомлений о файлах в Linux, Windows и macOS, демонстрирующие утечку через inotify/FSEvents, тайминг нажатий клавиш и фингерпринтинг веб-сайтов.
Этот репозиторий содержит (всё ещё на рецензии!) артефакты для статьи "File Notification Attacks: Templating and Exploiting Side-Channel Leakage from the File-Notification Systems on Linux, Windows, and macOS", принятой на CCS '26.
Посетите сайт с демонстрациями: https://inoti.fyi/
Читайте статью: https://snee.la/pdf/pubs/file-notification-attacks.pdf или http://inoti.fyi/pubs/file-notification-attacks.pdf
В рамках оценки артефактов на CCS'26 (всё ещё продолжается!) мы предоставили виртуальную машину Debian 13 + KDE Plasma 6 с предварительно пропатченным ядром. Эта ВМ не использовалась для получения результатов оценки из статьи и не предназначена для воспроизведения их точной величины. Этот репозиторий может быть обновлён с учётом рецензий и отзывов наших оценщиков артефактов. Мы сделаем ВМ общедоступной после завершения оценки артефактов.
Мы использовали Claude (Opus 4.8) для обёртывания нашего кода в достаточно хорошо документированные и высокопроизводительные артефакты (например, версия для Windows была немного медленной). Однако код, скрипты, makefile, которые он сгенерировал, были основаны на
Приведённые ниже инструкции по тестированию кода для Linux относятся к запуску команд
в ВМ. Пожалуйста, вместо этого посмотрите на код в Linux/.
ВМ существует исключительно для удобства, чтобы монтирование каждой атаки не требовало настройки среды с нуля или понижения версии ядра. Базовый механизм, демонстрируемый в ВМ, идентичен описанному в статье, но обратите внимание, что мы не использовали ВМ для получения чисел, приведённых в статье. Абсолютные значения времени могут отличаться из-за виртуализированной среды и базового оборудования.
Результаты воспроизводятся внутри ВМ Debian 13 (linux-vm/), установленной с
официального live-ISO Debian с KDE Plasma 6 (Wayland) и ядром, которое предшествует
исправлению
fsnotify.
Установлены inotify-tools, заголовки разработки Qt6 и pkexec. Больше ничего не
обновляется, и ядро никогда не трогается после установки. Пожалуйста, НЕ запускайте
apt upgrade и не пытайтесь обновить ядро. Этот образ ВМ намеренно старый, не
обновлён и не имеет последних патчей безопасности. Этот образ ВМ предназначен только
для быстрой проверки и тестирования!
Код, атаки и демонстрации, находящиеся внутри ВМ, зеркально отражены в Linux/.
Примечание: В начале каждого блока команд мы указываем, от имени какого
пользователя запускать команды. Есть [host] — хост-машина, на которой запущена
ВМ, [user] — жертва атак в ВМ, и [spyuser], которого требуется настроить и который
будет атакующим в большинстве атак на Linux.
Образ диска поставляется как linux-vm-upload.qcow2, сжатый с помощью zstd.
Переименуйте его в linux-vm.qcow2 перед запуском run.sh (который ожидает именно
это имя файла):
# Run As: [host]
cd linux-vm/
mv linux-vm-upload.qcow2 linux-vm.qcow2
./run.sh
Для чтения qcow2, сжатого zstd, требуется qemu-img/qemu-system-x86_64 версии
5.2 или новее (2020+), проверьте с помощью qemu-img --version. Если вы застряли на
старом QEMU и он не может открыть образ, сначала распакуйте его в плоский qcow2:
qemu-img convert -O qcow2 linux-vm-upload.qcow2 linux-vm.qcow2
4 ядра, 4 ГБ ОЗУ, графический дисплей. Учётные данные для входа:
root, пароль password,user, пароль password,spyuser, пароль password (не входите как spyuser!)Предоставленная ВМ уже работает на уязвимом, предварительно пропатченном ядре
(6.12.43+deb13-amd64), поэтому для её использования понижение версии не требуется.
Для запуска этого образа (который имеет графический интерфейс) вам понадобится QEMU.
Артефакт находится по адресу /home/user/Linux-File-Notification-Attacks внутри ВМ.
Одна команда настраивает всё с нуля в ВМ: (i) непривилегированную учётную запись
spyuser (без sudo), (ii) её собственную копию каталога артефактов (иначе отдельная
непривилегированная учётная запись не сможет войти в домашний каталог user),
(iii) и все скрипты, сделанные исполняемыми в обеих копиях.
# In the VM, Run As: [user]
sudo bash ~/Linux-File-Notification-Attacks/setup_attacks.sh
Каждая атака ниже запускается от имени spyuser (su - spyuser, пароль password),
кроме auth-ui-redress, которая запускается от имени user (объяснено ниже).
Доказывает, что уведомления о файловых операциях доставляются для файлов, которые нельзя прочитать напрямую, если их родительский каталог доступен для чтения.
# Run As: [spyuser]
# To switch to spyuser, in a new terminal type `su - spyuser`. The password
# is `password`.
cd ~/Linux-File-Notification-Attacks/unreadable-file-bypass
./watch-syslog.sh
Оставьте его работающим, затем сгенерируйте строку лога из другого терминала от имени
user:
# Run As: [user]
logger "hello"
В этом образе Debian 13 нет rsyslog, поэтому нет файла /var/log/syslog, как
показано в статье. Вместо этого скрипт переключается на наблюдение за
/var/log/journal/<machine-id>/. Это та же идея, поскольку отслеживаемые файлы
.journal принадлежат root (пользователь) и systemd-journal (группа), ни один из
которых не является spyuser.
Измеряет задержку уведомлений inotify.
# Run As: [user], ensure numpy is installed (or pip3)
sudo apt install python3-numpy
# Run As: [spyuser]
cd ~/Linux-File-Notification-Attacks/temporal-resolution
./run.sh
watcher открывает наблюдение inotify за тестовым файлом и ставит метку времени для
каждого получаемого IN_ACCESS. accessor читает тот же файл 1000 раз со случайной
задержкой 5-10 мс между чтениями, самостоятельно ставя метку времени для каждого
чтения. stats.py сравнивает две записи меток времени и выводит среднюю/стандартное
отклонение/минимальную задержку между происходящим чтением и приходом уведомления,
т.е. временное разрешение inotify.
Не полная атака из статьи, а лишь примитив фильтрации — доказательство концепции, на
котором построена атака: для каждой клавиши, нажатой где угодно в системе, выводится
одно уведомление с меткой времени, без прямого чтения /dev/input.
# Run As: [spyuser]
cd ~/Linux-File-Notification-Attacks/inter-keystroke-timing
make
./find-keyboard.sh
Печатайте в любом окне (возможно, в новом терминале от имени user). Каждое нажатие
клавиши выводит одну строку KEYPRESS. Окно подавления (которое мы эвристически
выбрали для этой демонстрации — 130 мс) объединяет несколько событий IN_ACCESS,
сгенерированных одним физическим нажатием клавиши. На практике мы заметили, что это
окно различается для разного оборудования (например, механических клавиатур, стилей
печати).
Если автоопределение выбирает неверное устройство (или ни одного), проверьте
/proc/bus/input/devices и запустите его напрямую с ./build/keystroke-notify /dev/input event4.
Мы демонстрируем возможность обнаружения запуска
(pkexec)[https://polkit.pages.freedesktop.org/polkit/] и дальнейшего рисования
поддельного окна поверх него. Как мы показали в разделе 4.4.3, эта «атака подмены
интерфейса аутентификации» возможна в KDE Plasma 5 и 6 с Wayland. Мы выполняем эту
атаку в модели угрозы атакующего с той же учётной записью: сокет Wayland привязан к
вошедшей в систему сессии, и отдельная непривилегированная учётная запись не может
рисовать на нём.
В стандартной установке KDE pkexec подтягивается рабочим столом, но этот live-ISO —
более лёгкий образ, и поэтому в нём его нет. Поэтому мы установили его вручную,
(пытаясь) сохранить образ ВМ небольшим.
# Run As: [user]
cd ~/Linux-File-Notification-Attacks/auth-ui-redress
make
./inotify-watcher-with-gui
Оставьте его работающим в терминале. Из другого терминала (каталог не важен)
вызовите настоящий запрос pkexec, например pkexec ls. Наблюдатель видит
обращение к /usr/bin/pkexec и рисует поддельный диалог "Authentication Required"
(window-launcher) на экране поверх настоящего. Ввод текста в поддельный диалог и
нажатие Authenticate выводит введённый текст в терминал наблюдателя, затем закрывает
его. Обратите внимание, что поддельное окно намеренно отличается от настоящего окна
для удобства сравнения.
Наблюдение за каталогами шрифтов во время загрузки страницы выявляет, к каким файлам шрифтов она обращалась. Разные сайты подтягивают разные шрифты, поэтому набор путей, выводимых во время загрузки страницы, уже является отпечатком; для этой минимальной версии не нужны ни тайминг, ни классификатор.
Сначала от имени user откройте Firefox (нажмите на него через значок в доке
менеджера задач внизу экрана) и убедитесь, что не открыто ни одного веб-сайта. Затем
в терминале от имени spyuser:
# Run As: [spyuser]
cd ~/Linux-File-Notification-Attacks/website-fingerprinting-fonts
./compare-fonts.sh
Это собирает font-spy, который начинает мониторинг каталогов шрифтов, используемых
Firefox. Скрипт compare-fonts предлагает вам посетить два веб-сайта.
Сначала посетите один веб-сайт (возможно, wikipedia.com) и подождите несколько секунд, пока он загрузится, откройте новую вкладку, закройте старую вкладку (с веб-сайтом), а затем нажмите ENTER в терминале.
Затем сделайте то же самое с другим веб-сайтом (возможно, reddit.com): посетите веб-сайт и подождите несколько секунд, пока он загрузится, откройте новую вкладку, закройте старую вкладку и затем нажмите ENTER в терминале.
Скрипт должен вывести разницу в шрифтах, к которым обращались для каждого веб-сайта. Обратите внимание, что в статье мы также использовали временную информацию, т.е. когда был осуществлён доступ к шрифту. Для простого доказательства концепции мы пренебрегаем этой информацией и просто выводим разницу между двумя наборами обращений к шрифтам. Хотя точный набор обращений к шрифтам может различаться между запусками, есть определённые файлы шрифтов, к которым веб-сайт обращается всегда и уникально.
Обратите внимание, что веб-сайты могут меняться со временем, и, следовательно,
обращения к файлам шрифтов могут различаться. На момент составления этого артефакта мы
заметили, что Wikipedia всегда обращается к
/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf, а Reddit всегда
обращается к /usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf.
Физическое устройство или эмулятор с версией Android, чья модель Scoped Storage всё
ещё позволяет регистрировать FileObserver (обёртка уровня Java вокруг inotify) на
общих каталогах медиа WhatsApp, и второе устройство/учётная запись для отправки
сообщений. Либо соберите предоставленный исходный код (Android/source/), либо
установите прилагаемый APK напрямую (Android/app-release.apk).
Android предоставляет тот же примитив inotify, используемый во всех находках для
Linux, через android.os.FileObserver. Раздел 5.4.3 показывает, что непривилегированное
приложение без разрешений может зарегистрировать такой наблюдатель на каталогах медиа
WhatsApp и, исключительно по результирующему потоку событий open/close/access,
вывести факт получения приватного медиа, не имея никаких разрешений, которые
позволили бы ему прочитать само содержимое.
Запустите приложение и вызовите действие обновления; служба наблюдателя подключается и
начинает периодически выдавать записи о работоспособности. Прокрутите вниз до конца
представления лога и продолжайте обновлять, пока не появятся повторяющиеся записи
ObserverService: Still Running, подтверждающие, что наблюдение активно и стабильно.
Со второй учётной записи отправьте изображение или документ в наблюдаемую переписку и,
если он ещё не кэширован, скачайте его на наблюдаемом устройстве. Это порождает всплеск
строк лога файловых событий; сразу после последней записи ObserverService: Still Running перед всплеском лог должен показать события open/close/access, пути которых
соответствуют полученному файлу, демонстрируя, что его прибытие наблюдаемо исключительно
по метаданным уведомлений файловой системы.
Мы предоставляем исходный код, который можно скомпилировать с помощью msys2. В противном случае также можно использовать exe-файл (Windows/firefox-fingerprint/monitor.exe).
Машина (или ВМ) с Windows с установленным MSYS2 и
инструментальной цепочкой MinGW-w64 g++ (pacman -S mingw-w64-ucrt-x86_64-gcc,
запускать из оболочки MSYS2). Установленный Firefox.
Это доказательство концепции использует
ReadDirectoryChangesW
для рекурсивного наблюдения за всем C:\ и выводит только события, путь которых
содержит http (каталоги хранилища для каждого origin, которые Firefox называет по
схеме/хосту сайта, например в его папках cache/IndexedDB). Для наблюдения за
каталогом, который вы можете перечислить, права администратора не требуются. Это
хранилище для каждого origin записывается при каждой загрузке страницы, поэтому
наблюдение извне процесса браузера всё равно раскрывает, какой сайт только что был
посещён.
Либо используйте предоставленный нами exe (Windows/firefox-fingerprint/monitor.exe), либо скомпилируйте из оболочки MSYS2 UCRT64:
# Run in an MSYS2 UCRT64 shell
cd Windows/firefox-fingerprint
g++ -municode -static -O2 -o monitor.exe monitor.cpp
Используйте g++, а не gcc. Также запускайте monitor.exe из терминала (а не
двойным щелчком по нему), иначе к нему не будет подключена консоль для вывода.
Наблюдатель работает под второй, непривилегированной локальной учётной записью, отдельной от той, что используется для просмотра. Сначала создайте эту учётную запись через Параметры:
attacker, и пароль, затем Next.Это создаёт локальную учётную запись (не привязанную к учётной записи Microsoft, без сетевого входа), по умолчанию со стандартными (не административными) правами.
Затем поместите monitor.exe туда, где новая учётная запись действительно сможет его
прочитать и выполнить. Вместо этого скопируйте собранный бинарный файл в публичный,
доступный для чтения всем каталог:
copy monitor.exe C:\Users\Public\monitor.exe
Теперь из вашей основной (жертвенной) учётной записи запустите его под учётной записью
attacker, не переключая рабочие столы и не выходя из системы, с помощью runas:
runas /user:attacker "cmd /k C:\Users\Public\monitor.exe"
Введите пароль attacker при появлении запроса. cmd /k (вместо прямого запуска
monitor.exe) оставляет окно консоли открытым, чтобы вы могли наблюдать за его
выводом, тогда как просто runas /user:attacker C:\Users\Public\monitor.exe тоже
работает, но его окно закрывается в тот момент, когда процесс завершается. Это
исключительно для удобства.
Оставьте окно monitor.exe работающим, затем, вернувшись в свою основную учётную
запись, откройте Firefox и посетите пару разных веб-сайтов, например,
arstechnica.com и reddit.com.
Каждая выводимая строка — это создание/изменение/переименование файла по пути
хранилища, соответствующему http. Разные сайты получают разные каталоги origin, и,
таким образом, набор путей, к которым идёт обращение во время загрузки страницы, уже
является отпечатком.
Для простоты мы используем fswatch —
существующий, минимальный, широко используемый CLI-инструмент, который оборачивает
нативный API FSEvents. Атакующий может использовать API FSEvents и без этого
инструмента.
Установите fswatch или соберите из исходного кода:
brew install fswatch
Наблюдайте за /Applications рекурсивно, как непривилегированный пользователь:
fswatch -xr /Applications
Оставьте его работающим, затем из Finder или браузера скачайте и установите Zoom
(или любое другое приложение), а затем удалите его (возможно, придётся также очистить
корзину). fswatch выводит каждый путь, о котором сообщает FSEvents в
/Applications, по мере их появления. Для быстрой оценки тот же пользователь может
наблюдать за каталогом. В противном случае вы можете создать нового пользователя и
использовать fswatch от его имени.
Выпущено под лицензией MIT.