
Artefatos de pesquisa para ataques de canal lateral de notificação de arquivos no Linux, Windows e macOS, demonstrando vazamento de inotify/FSEvents, temporização de teclas e fingerprinting de sites.
Este repositório contém os artefatos (ainda em revisão!) para o artigo "File Notification Attacks: Templating and Exploiting Side-Channel Leakage from the File-Notification Systems on Linux, Windows, and macOS", aceito no CCS '26.
Visite o site com demonstrações em: https://inoti.fyi/
Leia o artigo em: https://snee.la/pdf/pubs/file-notification-attacks.pdf ou http://inoti.fyi/pubs/file-notification-attacks.pdf
Como parte da avaliação de artefatos no CCS'26 (ainda em andamento!), disponibilizamos uma VM Debian 13 + KDE Plasma 6 com um kernel pré-patchado. Esta VM não foi usada para produzir os resultados de avaliação do artigo e não se destina a reproduzir sua magnitude exata. Este repositório pode ser atualizado para incorporar revisões e feedback dos nossos avaliadores de artefatos. Tornaremos a VM publicamente acessível quando nossa avaliação de artefatos for concluída.
Empregamos o Claude (Opus 4.8) para empacotar nosso código em artefatos razoavelmente bem documentados e de alto desempenho (por exemplo, o do Windows estava um pouco lento). No entanto, o código, scripts e makefiles que ele gerou foram baseados em
As instruções para testar o código Linux abaixo são específicas para executar
comandos na VM. Por favor, consulte o código em Linux/.
A VM existe puramente por conveniência, para que a montagem de cada ataque não exija uma configuração de ambiente do zero ou um downgrade de kernel. O mecanismo subjacente demonstrado na VM é idêntico ao descrito no artigo, mas observe que não usamos a VM para produzir os números relatados no artigo. Os tempos absolutos podem diferir devido ao ambiente virtualizado e ao hardware subjacente.
Os achados são reproduzidos dentro de uma VM Debian 13 (linux-vm/), instalada
a partir da ISO live oficial do Debian com KDE Plasma 6 (Wayland) instalado com
um kernel anterior à correção do
fsnotify.
inotify-tools, cabeçalhos de desenvolvimento do Qt6 e pkexec estão
instalados. Nada mais é atualizado, e o kernel nunca é tocado após a instalação.
Por favor, NÃO execute apt upgrade nem tente atualizar o kernel. Esta
imagem de VM é deliberadamente antiga, não está atualizada e não possui os
últimos patches de segurança. Esta imagem de VM destina-se apenas a validação e
testes rápidos!
O código, os ataques e as demonstrações localizados dentro da VM estão
espelhados em Linux/.
Nota: Mencionamos como qual usuário executar os comandos no topo de cada
bloco de comando. Há o [host], que é a máquina hospedeira que executa a VM, o
[user], que é a vítima dos ataques na VM, e o [spyuser], que precisa ser
configurado e será o atacante na maioria dos ataques Linux.
A imagem de disco é fornecida como linux-vm-upload.qcow2, comprimida com zstd.
Renomeie-a para linux-vm.qcow2 antes de executar run.sh (que espera esse
nome de arquivo):
# Run As: [host]
cd linux-vm/
mv linux-vm-upload.qcow2 linux-vm.qcow2
./run.sh
Ler um qcow2 comprimido com zstd requer qemu-img/qemu-system-x86_64 versão
5.2 ou mais recente (2020+), verifique com qemu-img --version. Se você estiver
preso em um QEMU mais antigo e ele falhar ao abrir a imagem, descompacte-a em um
qcow2 plano primeiro:
qemu-img convert -O qcow2 linux-vm-upload.qcow2 linux-vm.qcow2
4 núcleos, 4GB de RAM, exibição GUI. Os logins são:
root, senha password,user, senha password,spyuser, senha password (não faça login como spyuser!)A VM fornecida já executa um kernel vulnerável e pré-patchado
(6.12.43+deb13-amd64), então nenhum downgrade é necessário para usá-la. Você
precisará do QEMU para executar esta imagem (que possui uma GUI). O artefato
está em /home/user/Linux-File-Notification-Attacks dentro da VM.
Um comando configura tudo do zero na VM: (i) uma conta spyuser sem
privilégios (não-sudo), (ii) sua própria cópia do diretório de artefatos (uma
conta sem privilégios separada não consegue, de outra forma, acessar o
diretório home do user), (iii) e todos os scripts tornados executáveis em
ambas as cópias.
# In the VM, Run As: [user]
sudo bash ~/Linux-File-Notification-Attacks/setup_attacks.sh
Cada ataque abaixo é executado como spyuser (su - spyuser, senha
password), exceto auth-ui-redress, que é executado como user (explicado
abaixo).
Prova que notificações de operações de arquivo são entregues para arquivos que não podem ser lidos diretamente, desde que seu diretório pai seja legível.
# 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
Deixe-o em execução, então gere uma linha de log de outro terminal como user:
# Run As: [user]
logger "hello"
Esta imagem Debian 13 não possui rsyslog, então não há arquivo /var/log/syslog
como mostrado no artigo. O script recorre a monitorar
/var/log/journal/<machine-id>/ em vez disso. É a mesma ideia, pois os arquivos
.journal sendo monitorados pertencem a root (usuário) e systemd-journal
(grupo), nenhum dos quais é spyuser.
Mede o atraso de notificação do 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 abre um watch do inotify em um arquivo de teste e marca o timestamp de
cada IN_ACCESS que recebe. accessor lê esse mesmo arquivo 1000 vezes com um
atraso aleatório de 5-10ms entre leituras, marcando o timestamp de cada leitura
ele mesmo. stats.py compara as duas gravações de timestamp e imprime o
atraso médio/desvio padrão/mínimo entre a leitura ocorrer e a notificação
chegar, ou seja, a resolução temporal do inotify.
Não é o ataque completo do artigo, mas apenas a primitiva de prova de conceito
de filtragem na qual o ataque é construído: para cada tecla pressionada em
qualquer lugar do sistema, imprime uma notificação com timestamp, sem nunca ler
/dev/input diretamente.
# Run As: [spyuser]
cd ~/Linux-File-Notification-Attacks/inter-keystroke-timing
make
./find-keyboard.sh
Digite em qualquer janela (talvez um novo terminal como user). Cada tecla
pressionada imprime uma linha KEYPRESS. A janela de supressão (que escolhemos
heurísticamente para esta demonstração é de 130ms) mescla vários eventos
IN_ACCESS gerados por um único pressionamento físico de tecla. Na prática,
notamos que esta janela é diferente para hardwares diferentes (por exemplo,
teclados mecânicos, estilos de digitação).
Se a detecção automática escolher o dispositivo errado (ou nenhum), verifique
/proc/bus/input/devices e execute-o diretamente com ./build/keystroke-notify /dev/input event4.
Demonstramos ser capazes de detectar quando
(pkexec)[https://polkit.pages.freedesktop.org/polkit/] é executado, e ainda
desenhar uma janela falsa sobre ele. Como mostramos na Seção 4.4.3, este
'ataque de redress de UI de autenticação' é possível no KDE Plasma 5 e 6 com
Wayland. Executamos este ataque no modelo de ameaça de atacante com o mesmo
usuário: o socket do Wayland é restrito à sessão logada, e uma conta sem
privilégios separada não consegue desenhar nele.
Em uma instalação padrão do KDE, o pkexec é trazido pela área de trabalho, mas
esta ISO live é uma imagem mais enxuta e, portanto, não o possui instalado. Por
isso, o instalamos manualmente enquanto (tentávamos) manter a imagem da VM
pequena.
# Run As: [user]
cd ~/Linux-File-Notification-Attacks/auth-ui-redress
make
./inotify-watcher-with-gui
Deixe-o em execução em um terminal. De outro terminal (o diretório não importa),
dispare um prompt real do pkexec, por exemplo pkexec ls. O watcher vê o
acesso a /usr/bin/pkexec e desenha um diálogo falso de "Authentication
Required" (window-launcher) na tela sobre o real. Digitar no diálogo falso e
pressionar Authenticate imprime o texto digitado no terminal do watcher e então
o fecha. Observe que a janela falsa é deliberadamente diferente da janela real
para facilitar a comparação.
Monitorar diretórios de fontes enquanto uma página carrega revela quais arquivos de fonte ela acessou. Sites diferentes puxam fontes diferentes, então o conjunto de caminhos impressos enquanto uma página carrega já é uma impressão digital, sem necessidade de temporização ou classificador para esta versão mínima.
Primeiro, como usuário, abra o Firefox (clique nele via o ícone no dock do
gerenciador de tarefas na parte inferior da tela) e certifique-se de que nenhum
site esteja aberto. Então, em um terminal como spyuser:
# Run As: [spyuser]
cd ~/Linux-File-Notification-Attacks/website-fingerprinting-fonts
./compare-fonts.sh
Isso compila font-spy, que começa a monitorar os diretórios de fontes usados
pelo Firefox. O script compare-fonts solicita que você visite dois sites.
Primeiro, visite um site (talvez wikipedia.com) e espere alguns segundos para ele carregar, abra uma nova aba, feche a aba antiga (com o site) e então pressione ENTER no terminal.
Segundo, faça o mesmo com outro site (talvez reddit.com): visite o site e espere alguns segundos para ele carregar, abra uma nova aba, feche a aba antiga e então pressione ENTER no terminal.
O script deve imprimir a diferença nas fontes acessadas por site. Observe que no artigo também usamos a informação temporal, ou seja, quando a fonte foi acessada. Para uma prova de conceito simples, desconsideramos essa informação e simplesmente imprimimos a diferença entre os dois conjuntos de acessos a fontes. Embora o conjunto exato de acessos a fontes possa diferir entre execuções, há certos arquivos de fonte que são sempre e unicamente acessados por um site.
Observe que os sites podem mudar com o tempo e, portanto, os acessos a arquivos
de fonte podem diferir. No momento da redação deste artefato, notamos que a
Wikipedia sempre acessa
/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf e o Reddit sempre
acessa /usr/share/fonts/truetype/vlgothic/VL-Gothic-Regular.ttf.
Um dispositivo físico ou emulador executando uma versão do Android cujo modelo
de Scoped Storage ainda permita registrar um FileObserver (o wrapper em nível
Java em torno do inotify) nos diretórios de mídia compartilhada do WhatsApp, e
um segundo dispositivo/conta para atuar como remetente da mensagem. Compile o
código-fonte fornecido (Android/source/) ou instale o APK fornecido
diretamente (Android/app-release.apk).
O Android expõe a mesma primitiva inotify usada em todos os achados de Linux
via android.os.FileObserver. A Seção 5.4.3 mostra que um app sem privilégios e
sem permissões pode registrar tal observer nos diretórios de mídia do WhatsApp
e, puramente a partir do fluxo resultante de eventos de open/close/access,
inferir que mídia privada foi recebida, sem possuir qualquer permissão que lhe
permitisse ler o conteúdo em si.
Inicie o app e dispare a ação de atualização; o serviço observer se conecta e
começa a emitir entradas periódicas de atividade. Role até o final da
visualização de log e continue atualizando até que entradas repetidas de
ObserverService: Still Running apareçam, confirmando que o watch está ativo e
estável.
De uma segunda conta, envie uma imagem ou documento para a conversa observada e,
se ainda não estiver em cache, baixe-o no dispositivo observado. Isso produz uma
rajada de linhas de log de eventos de arquivo; imediatamente após a última
entrada ObserverService: Still Running antes da rajada, o log deve mostrar
eventos de open/close/access cujos caminhos correspondem ao arquivo recebido,
demonstrando que sua chegada é observável puramente a partir dos metadados de
notificação do sistema de arquivos.
Fornecemos o código-fonte que pode ser compilado com msys2. Caso contrário, o arquivo exe (Windows/firefox-fingerprint/monitor.exe) também pode ser usado.
Uma máquina Windows (ou VM) com MSYS2 instalado, e a
toolchain g++ do MinGW-w64 (pacman -S mingw-w64-ucrt-x86_64-gcc, executado a
partir de um shell MSYS2). Firefox instalado.
Esta prova de conceito usa
ReadDirectoryChangesW
para monitorar recursivamente todo o C:\, e imprime apenas eventos cujo
caminho contém http (diretórios de armazenamento por origem que o Firefox
nomeia segundo o esquema/host do site, por exemplo, sob suas pastas
cache/IndexedDB). Nenhum direito de administrador é necessário para monitorar um
diretório que você pode listar. Este armazenamento por origem é escrito a cada
carregamento de página, então monitorar de fora do processo do navegador ainda
revela qual site acabou de ser visitado.
Use o exe que fornecemos (Windows/firefox-fingerprint/monitor.exe) ou compile a partir de um shell MSYS2 UCRT64:
# Run in an MSYS2 UCRT64 shell
cd Windows/firefox-fingerprint
g++ -municode -static -O2 -o monitor.exe monitor.cpp
Use g++, não gcc. Também execute monitor.exe a partir de um terminal (não
clicando duas vezes nele), caso contrário não há console anexado para imprimir.
O watcher é executado como uma segunda conta local sem privilégios, separada da que navega. Primeiro, crie essa conta via Configurações:
attacker, e uma senha, depois
Avançar.Isso cria uma conta local (não vinculada a uma conta Microsoft, sem login de rede), com privilégios padrão (não-admin) por padrão.
Em seguida, coloque monitor.exe em algum lugar onde a nova conta possa
realmente lê-lo e executá-lo. Copie o binário compilado para o diretório
público, legível por todos, em vez disso:
copy monitor.exe C:\Users\Public\monitor.exe
Agora, da sua conta principal (vítima), inicie-o sob a conta attacker sem
trocar de área de trabalho ou fazer logout, usando runas:
runas /user:attacker "cmd /k C:\Users\Public\monitor.exe"
Digite a senha do attacker quando solicitado. cmd /k (em vez de executar
monitor.exe diretamente) mantém a janela do console aberta para que você possa
observar sua saída, enquanto simplesmente runas /user:attacker C:\Users\Public\monitor.exe também funciona, mas sua janela fecha no instante
em que o processo termina. Isso é puramente por conveniência.
Deixe a janela do monitor.exe em execução, então, de volta na sua conta
principal, abra o Firefox e visite alguns sites diferentes, por exemplo,
arstechnica.com e reddit.com.
Cada linha impressa é uma criação/modificação/renomeação de arquivo sob um
caminho de armazenamento correspondente a http. Sites diferentes recebem
diretórios de origem diferentes e, portanto, o conjunto de caminhos acessados
enquanto uma página carrega já é uma impressão digital.
Por simplicidade, usamos o fswatch,
uma ferramenta CLI existente, mínima e amplamente usada que envolve a API nativa
FSEvents. Um atacante também pode usar a API FSEvents sem esta ferramenta.
Instale o fswatch, ou compile a partir do código-fonte:
brew install fswatch
Monitore /Applications recursivamente, como um usuário sem privilégios:
fswatch -xr /Applications
Deixe-o em execução, então, do Finder ou de um navegador, baixe e instale o Zoom
(ou qualquer outro aplicativo), depois desinstale-o (você pode ter que esvaziar
a lixeira também). O fswatch imprime cada caminho que o FSEvents reporta sob
/Applications conforme acontece. Para avaliação rápida, o mesmo usuário pode
monitorar o diretório. Caso contrário, você pode configurar um novo usuário e
usar o fswatch como esse usuário.
Lançado sob a Licença MIT.