Malwares que detectam o Wine e infectam o Windows E o Linux?
Se você é um usuário um pouco acima do comum – certamente você é, caso contrário não estaria lendo esse artigo =D – já viu a eterna discussão Linux vs Windows. E um ponto que sempre se levanta é: Linux não tem vírus. Sabemos que isso não é verdade. Mas realmente a maioria dos malwares são feitos para o Windows. Isso porque de forma geral, desenvolvedores de malwares focam no sistema mais usado para aumentar a chance de sucesso na infecção. Assim, o alvo principal sempre foi o Windows porque este é o sistema operacional com a maior fatia do mercado de Desktops (enquanto no mercado mobile a maior fatia é do Android – sistema alvo da maior parte dos malwares para este segmento).
Porém os malwares para Linux vem crescendo a cada ano! Uma notícia em 2006 mostrava que o número de malwares criados para Linux havia dobrado no ano anterior¹. Shane Coursen, consultor técnico sênior da Kaspersky na época, dizia que haviam muitos usuários do Windows migrando para o Linux “pensando que o Linux daria mais segurança a eles”². Porque o uso de Linux em Desktops estava crescendo (e ainda está), o número de malwares para este sistema também estava (e ainda está)! Como dizia Shane Coursen ainda em 2006: “O uso de um Sistema Operacional está diretamente relacionado ao interesse de escritores de malwares em desenvolver malwares para este Sistema Operacional” ³.
Ok…, mas afinal por que estou falando tudo isso? Pula para uma análise de malware que fiz essa semana
Grandoreiro – Detectando se está rodando em Wine ou não
Eu estava analisando um malware intitulando “Grandoreiro” – famoso malware bancário da América Latina – (SHA256 3687ce1b0865f1c821d471d3f85b8403b5cfd127f807bb1f3aa9cd2fef7db4d9) e vi algo bastante interessante: ele tem uma detecção para saber se está rodando em um “wine”.
Esse trecho é bem simples. “Traduzindo” para C, isso seria basicamente essa linha de código:
If (GetProcAddress(GetModuleHandle(“ntdll.dll”), “wine_get_version”)) return(1); return(0);
Assim, o malware tentará localizar o endereço da função “wine_get_version” na biblioteca “ntdll.dll” na memória. Se o endereço retornado for diferente de zero, retorna verdadeiro, caso contrário, significa que a função não foi encontrada e então retorna falso.
Esse trecho de código já foi usado em diversos malwares para saber se estão rodando em um Linux ou não. De forma geral, ao detectar que rodam com o wine, eles finalizam sua operação porque, nesse caso, não estão rodando no ambiente alvo (Windows).
Diante de tudo que escrevi até aqui, quero demonstrar alguns pontos:
- Demonstrar que executar um executável infectado do Windows no Linux pode não ser seguro
- É possível criar um malware que funcione tanto em Windows quanto em Linux
- O wine não oferece isolamento do sistema operacional (e não é – nem nunca foi – o objetivo do projeto)
Para demonstrar esses pontos, primeiro preciso explicar o que é o Wine.
Wine – Wine não é um Emulador
Segundo o site oficial, “Wine (originalmente um acrônimo para ‘Wine Is Not an Emulator’ – Wine Não É Um Emulador) é uma camada de compatibilidade capaz de rodar aplicações Windows em diversos sistemas operacionais compatíveis com POSIX como Linux, MacOS e BSD”4.
O site continua explicando de forma breve como o Wine funciona e é essa parte aqui que é fundamental entender: “Ao invés de simular a lógica interna do Windows como uma Máquina Virtual ou um Emulador, Wine traduz a chamada a API do Windows em POSIX em tempo real, eliminando as penalidades de performance e memória de outros métodos e te permitindo integrar aplicações Windows ao seu Desktop de forma limpa.”5
O ponto aqui é perceber que o wine “traduz a chamada a API do Windows em POSIX em tempo real”. Vamos ver como isso funciona. Mas primeiro é necessário baixar o código fonte do Wine. Na página principal, clique no link da última versão estável:
Então clique em “Source code” para baixar o código fonte.
Descompacte o arquivo está pronto.
Entendendo a tradução de API do Windows para POSIX – CreateProcessA
Agora vamos entender como essa tradução funciona. Para isso vou pegar uma função típica do Windows: CreateProcessA que está declarada em KERNEL32.DLL no Windows.
O Wine, obviamente, reproduz as bibliotecas e funções do Windows. Portanto a função a função CreateProcessA também estará em KERNEL32.DLL. Indo na pasta “dlls/kernel32”, se tem estes arquivos:
Faz sentido ver a implementação da função em “process.c”. Neste arquivo a implementação de “CreateProcessA” é a seguinte:
Portanto CreateProcessA apenas chama “CreateProcessInternalA”. Analisando a implementação de CreateProcessInternalA, no fim ele apenas passa para “CreateProcessInternalW”. Já “CreateProcessInternalW” verifica se o processo é um processo de linha de comando (.cmd, .bat, etc) , um processo antigo (16 bits por exemplo), enfim. O “normal” é ser um executável comum. Então essa função acabará chamando “create_nt_process”. Já “create_nt_process” chama a função “NtCreateUserProcess”.
Finalmente começamos a ver, em “NtCreateUserProcess”, algumas implementações para o sistema compatível com POSIX. Por exemplo: vemos que existe um “wine_server” e a comunicação com ele é baseado em sockets. Abre-se os sockets com a função “socketpair”, que não tem uma implementação nativa para Windows, e se usa UNIX SOCKETS (PF_UNIX) no domínio – que só foi ter implementação no Windows com o Windows 10 1803.
É possível ver mais algumas especificidades do Unix, como a função “get_unix_curdir” para buscar o diretório “unix” atual de onde o usuário está executando o novo processo. Eventualmente se chega à função “spawn_process” que finalmente criará o processo filho.
A implementação da função “spawn_process” é essa:
Em última instância, o CreateProcessA apenas chamará um clássico fork! É exatamente o que a documentação disse: é uma “tradução” para o Unix (no meu caso, Linux). Apenas isso.
Vamos para mais um exemplo
Tradução de GetSystemInfo
A função GetSystemInfo “recupera informações sobre o sistema atual”6. Essa função pode ser usada para obter informações do hardware como o identificador OEM, tipo do processador, quantos processadores, etc. Veja que interessante uma parte da implementação de captura de dados do processador do sistema:
O interessante é que a implementação será distinta caso o wine rode em um sistema Apple, Linux ou FreeBSD. Claro! Afinal, as informações do sistema estarão em locais distintos!
Portanto se estiver rodando em um sistema Apple, chamará a função “host_processor_info” que receberá informações do processador. Se estiver rodando em um sistema Linux, o wine buscará as informações em /proc/stat. Se estiver em um sistema FreeBSD, então terá o código respectivo para buscar as informações para este sistema.
Criando um executável que infecte Windows e Linux?
Diante disso percebemos que é possível criar um malware que infecte os dois sistemas! Em 2018 saiu uma pesquisa bem interessante no “Journal of Computer Virology and Hacking Techniques” que revelou que 5 malwares a cada 30 samples conseguiam realizar o “escape” do wine e infectar o sistema6. É um número baixo, é verdade. Mas isso nos mostra que:
- É possível; e
- Existe um risco real disso acontecer.
Portanto para demonstrar essa possibilidade, quero criar de fato neste artigo um executável que identifique que está rodando em um “Wine” e s,e estiver, infecte o Linux. Se não, infecte o Windows. Vamos começar!
Buscando funções que denunciem o Wine
No início desse artigo, vimos que o Grandoreiro usa a função “wine_get_version” exportada pela “ntdll.dll” do Wine para identificar o “tradutor”, já que essa função existe apenas nele. Existem outras formas de identificá-lo?
– No fórum oficial do Wine, em uma thread de 2009, citam também a função “wine_get_unix_file_name”7. Citam algumas chaves de registro também, mas não é uma boa solução porque chaves de registro podem ser facilmente modificadas.
– Em uma questão levantada no StackOverflow, citam a já conhecida função “wine_get_version” e também citam a função “wine_get_host_version”, também exportada pela “ntdll.dll” do Wine8.
– Em outra questão do StackOverflow, colocam um código que tenta pegar o endereço da função “wine_get_version” para detecção do Wine9 (código semelhante ao do Grandoreiro, inclusive). Além desse código, citam as chaves de registro da thread do fórum oficial do Wine. Porém pelo motivo explicado acima, rejeitam a solução.
– Por fim, existe um código em C no Github para detecção do Wine baseado nessas duas funções já citadas acima10.
É verdade que a grande maioria dos malwares vão acabar usando essas duas funções. Mas um malware mais furtivo precisará descobrir alguma função menos “conhecida” ou menos “monitorada”. Portanto a primeira coisa que quero fazer é buscar algumas funções exportadas apenas pelo Wine que sejam distintas destas duas.
Existem algumas formas de fazer isso. Uma delas é ler o código fonte e encontrar as funções que se sabe que não existem no Windows! Veja: após eu fazer o download do código fonte da última versão do software (no momento que escrevo esse artigo a última versão estável é a 9.0), fui a implementação da função “CreateProcessA”, por exemplo. Percebi que diversas funções não existem na DLL do Windows. Apenas um exemplo: a função “wine_server_handle_to_fd”. Portanto ler o código fonte é um método eficiente para encontrar essas funções. Mas certamente não será o método mais rápido.
Um outro método mais rápido é olhar os arquivos de “especificações” (arquivos com extensão “.spec”). No diretório raiz do código fonte existe uma pasta intitulada “dlls”. Dentro dessa pasta existe uma pasta para cada conhecida DLL do Windows (kernel32, ntdll, etc). Dentro de cada uma dessas pastas tem um arquivo com extensão “.spec”. Nesses arquivos estão a lista de todas as funções exportadas por suas respectivas DLLs. Mas o interessante é que nessas funções exclusivas do Wine, o arquivo tem o seguinte escrito:
Portanto uma forma simples de encontrar todas as funções exportadas “exclusivas” do wine é procurar pela string “for internal function” em todos os arquivos com extensão “.spec”. Isso é bem fácil de fazer com um grep! Vamos lá! =D
Maravilha. Agora tenho arquivos que tem várias funções exclusivas do Wine para uso interno e que são exportadas! Portanto podem ser usadas para identificação do Wine! Aqui algumas além das já conhecidas “wine_get_version” e “wine_get_host_version”:
wine_get_build_id – exportada pela ntdll.dll
wine_nt_to_unix_file_name – exportada pela ntdll.dll
wine_unix_to_nt_file_name – exportada pela ntdll.dll
wine_server_fd_to_handle – exportada pela ntdll.dll
wine_server_handle_to_fd – exportada pela ntdll.dll
wine_server_call – exportada pela ntdll.dll
__wine_unix_call – exportada pela ntdll.dll
__wine_unix_spawnvp – exportada pela ntdll.dll
__wine_syscall_dispatcher – exportada pela ntdll.dll
__wine_unix_call_dispatcher – exportada pela ntdll.dll
__wine_unixlib_handle – exportada pela ntdll.dll
__wine_dbg_write – exportada pela ntdll.dll
__wine_dbg_get_channel_flags – exportada pela ntdll.dll
__wine_dbg_header – exportada pela ntdll.dll
__wine_dbg_output – exportada pela ntdll.dll
__wine_dbg_strdup – exportada pela ntdll.dll
wine_get_unix_file_name – exportada por kernel32.dll
wine_get_dos_file_name – exportada por kernel32.dll
Entre outras!
Uma terceira forma de identificar o Wine seria buscar funções que já não são mais exportadas pelas DLLs do Windows, mas ainda estão presentes no Wine. Por exemplo funções que eram exportadas no kernel32.dll do Windows 95 e já não são mais exportadas nas versões atuais do Windows mas ainda estão presentes no Wine (essas funções estão bem documentadas no arquivo “.spec” da pasta kernel32)!
Pronto. Já temos uma lista de funções que identificarão o Wine. Agora basta criar um executável que simule uma infecção do Windows e do Linux!
Alcançando a persistência
Agora que já foi identificado algumas funções diferentes para detectar o wine, é necessário determinar algum método de persistência.
Persistência, segundo o Dicionário Online de Português11, é a “ação ou efeito de persistir; qualidade do que dura”. Essa definição é excelente. É exatamente isso que desejamos alcançar! Desejamos que independente do sistema, sempre que o sistema for reiniciado, nosso pequeno “malware” reinicie com o sistema!
Persistência é um tópico bastante extenso e foge completamente do escopo desse artigo. Portanto vou apenas usar dois métodos clássicos: .bashrc para o Linux e a pasta de “Startup” para o Windows.
O .bashrc é um arquivo que contem informações para configuração do ambiente shell bash. Todas as vezes que uma sessão shell se inicia, o que está em .bashrc é executado.
Neste arquivo estão configurações do shell como cores a serem usadas, “alias” para comandos, quantos comandos devem ser salvos no histórico, etc. Portanto usarei este arquivo para alcançar persistência. Assim toda vez que uma sessão shell do usuário infectado for iniciada, o malware será executado.
E para o Windows usarei a pasta de Startup do usuário. Essa pasta é bastante conhecida dos desenvolvedores de malwares em geral porque tudo que está nela é executado assim que o usuário faz login na máquina.
A localização dessa pasta fica em C:\Users\<User>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup.
Não explicarei todo o código porque foge do escopo do artigo. Mas basicamente existem três executáveis em um. Um deles é o executável que roda pelo Wine (ou fora dele). Esse executável faz o papel de um “dropper”. Ele tem dois recursos: um executável para Linux (que é meu “malware” para Linux) e tem também um outro executável para Windows (que é o “malware” para o Windows). O “dropper”, assim que rodar, tentará localizar o endereço de memória da função “wine_get_unix_file_name” na biblioteca “kernel32.dll”. Obviamente essa função só existe na DLL do Wine portanto se encontrar, o executável está rodando no Linux através do Wine.
Para o leitor mais atento, eu estou assumindo que o usuário estará rodando em um Linux! Mas o Wine pode rodar em qualquer sistema compatível com POSIX, como eu citei lá em cima. Portanto nada impediria que um atacante desenvolvesse um malware com identificação de vários sistemas operacionais distintos e infectasse todos eles. Mas aqui focarei apenas no Windows e Linux.
Voltando ao código, ao perceber que está rodando no Linux, o meu dropper usará um combo das funções GetTempPathW e wine_get_unix_file_name do Wine. No código fonte do Wine lemos que a função wine_get_unix_file_name recebe como argumento um caminho de diretório em “wide char” e retorna o caminho correspondente a esse diretório no formato Unix! Isso para mim era perfeito porque de uma vez só eu conseguiria chegar facilmente no “.bashrc”. Mas antes eu precisava achar algum caminho de diretório que eu tivesse certeza que existisse para passar como argumento para wine_get_unix_file_name em formato “wide char”.
O “Wine” reproduz os padrões de diretório do Windows. Portanto eu poderia tentar diretórios como “C:\Windows” e outros conhecidos. Mas eu preferi usar a função GetTempPathW porque ela “retorna o caminho do diretório designado para arquivos temporários”12 e assim não seria um “chute”. O próprio Wine revelaria um caminho válido. E melhor: já retornaria no formato “wide char”! Então fica tudo perfeito! =D
A função GetTempPathW na minha máquina de teste retorna C:\users\rafael\Temp\. Claro que na máquina de cada um esse valor seria diferente. Então esse valor vai para a função wine_get_unix_file_name e essa função, por sua vez, retorna o caminho unix dessa pasta – aqui na minha máquina de teste é /home/Rafael/.wine/dosdevices/C:/users/Rafael/Temp/
Esse será o caminho padrão do Wine: %user%/.wine/dosdevices/blablabla. Agora o resto é fácil. Meu dropper corta o caminho Unix no “.wine”, adiciona “.bashrc” e agora já tem o caminho do .bashrc do usuário.
Agora que meu “dropper” também sabe o caminho “unix” real do usuário, ele escreve o “malware” (o executável ELF que está no “resource” do “dropper”) em %user%/.config/wMal. Após isso o “dropper” escreverá na última linha de %user%/.bashrc o seguinte:
chmod +x /home/user/.config/wMal;/home/user/.config/wMal
Claro que o caminho ali será ajustado conforme o usuário infectado.
E fim! Só isso! Agora toda vez que o usuário iniciar o bash, o meu malware será executado.
E o que ele faz? Ele apenas cria um arquivo chamado “oi.txt” na pasta ~ do usuário com o texto “Infectado por RafaMalware! HAHAHA! (Ler com Risada Maligna)”.
Bem, como prova de conceito tá bom, né? Rsrsrs
E se o “dropper” identificar que está rodando no Windows, ele pegará a variável de ambiente “APPDATA”, acrescentará “\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\w.exe” ao caminho de APPDATA e salvará o “malware” para Windows que está nos recursos do “dropper”. Agora todas as vezes que o usuário logar na máquina, o “malware” será executado.
E o que esse malware faz? Apenas mostra a caixinha de mensagem abaixo =D
Conclusão
O cuidado nunca é demais! Procure na internet e você verá centenas de fóruns Linux com dúvidas de usuários sobre se é possível ser infectado através do Wine. Muitos acreditam que enquanto estiverem usando Linux, estão livres de qualquer infecção. Mas vimos que não é bem assim =D
É por isso que no FAQ do Wine está explícito que “apenas porque o Wine roda em um OS diferente do Windows não significa que você está protegido de vírus, trojans e outras formas de malwares”12.
Caso queira testar o dropper, baixe ele aqui: https://u.pcloud.link/publink/show?code=XZc64C0ZORVgIgfNaTLiVpWq4tG3e5fmiEly
Prometo que é seguro! Haha! Depois é só desfazer as modificações que falei que são feitas e tá limpo ?
Abraços e até a próxima
- https://www.internetnews.com/security/linux-malware-on-the-rise/
- Ibid
- Ibid
- https://www.winehq.org/
- Ibid
- Journal of Computer Virology and Hacking Techniques. 15 (1): 39–60. doi:10.1007/s11416-018-0319-9. ISSN 2263-8733
- https://forum.winehq.org/viewtopic.php?t=4988
- https://stackoverflow.com/questions/56277858/how-to-detect-is-wine-running-from-linux-or-from-mac-os-environment-in-c
- https://stackoverflow.com/questions/7372388/determine-whether-a-program-is-running-under-wine-at-runtime
- https://gist.github.com/klange/282e62fdd1c2cae6a210443ccbc5b12f
- https://www.dicio.com.br/persistencia/
- https://wiki.winehq.org/FAQ#Is_Wine_malware-compatible?