Aprendi que o governo dinamarquês não apenas
suspendeu o programa Digital Exam Monitor, que analisamos e contornamos completamente no
artigo anterior , mas talvez
desligue completamente esse sistema uma semana depois de os informarmos sobre o método de hacking. Não quero pensar que foi apenas por nossa causa que o governo dinamarquês abandonou a idéia de monitorar os exames, mas nosso trabalho foi claramente notado.
Neste artigo, descreveremos os detalhes técnicos de como funciona outra ferramenta de rastreamento de alunos: ExamCookie. Se você estiver interessado apenas em ignorar o sistema, role para baixo até a seção apropriada.
ExamCookie
Recentemente, essa ferramenta
chegou às notícias devido a uma investigação sobre a violação do RGPD. Decidimos dar uma olhada no segundo maior concorrente do sistema de rastreamento escolar acima mencionado durante os exames:
ExamCookie . Este é um sistema de rastreamento comercial usado por mais de 20 escolas dinamarquesas. Não há documentação no site além da descrição a seguir:
O ExamCookie é um software simples que monitora a atividade do computador do aluno durante o exame para garantir que as regras sejam seguidas. O programa proíbe os alunos de usar qualquer forma ilegal de assistência.
O ExamCookie salva toda a atividade no computador: URLs ativos, conexões de rede, processos, área de transferência e capturas de tela ao redimensionar a janela.
O programa funciona de maneira simples: ao entrar no exame, você o executa no seu computador e monitora sua atividade. Quando o exame é concluído, o programa é fechado e você pode removê-lo do computador.
Para iniciar o rastreamento, você precisa usar seu login UNI, que funciona em vários sites educacionais, ou inserir manualmente as credenciais. Como não usamos a ferramenta, não podemos dizer em que casos a entrada manual é usada. Talvez isso seja feito para estudantes que não possuem um login UNI, o que não consideramos possível.
Informações binárias
O programa pode ser baixado na página inicial do ExamCookie. É um aplicativo .NET x86. Para referência, o hash MD5 binário analisado
63AFD8A8EC26C1DC368D8FF8710E337D
assinatura EXAMCOOKIE APS de 24 de abril de 2019. Como o
último artigo mostrou, a análise do binário .NET dificilmente pode ser chamada de engenharia reversa, porque a combinação de código IL e metadados de fácil leitura fornece o código fonte perfeito.
Diferentemente do programa de monitoramento anterior, os desenvolvedores dessa ferramenta não apenas a excluíram do log de depuração, como também a ofuscaram. Pelo menos eles tentaram :-)
Ofuscação (risada às lágrimas)
Quando abrimos o aplicativo no dnSpy, notamos rapidamente um ponto de entrada ausente:
Estranho, geralmente é assumido algum tipo de wrapper, ele altera os corpos dos métodos do construtor do módulo, que é executado no ponto de entrada real, vamos ver:
Legal. É 2019, e as pessoas ainda usam o Confuser (Ex).
Reconhecemos instantaneamente esse código de descompressão e verificamos os cabeçalhos do assembler:
[módulo: ConfusedBy ("Confuser.Core 1.1.0 + a36320377a")]
No momento, pensamos que o código seria realmente ofuscado, pois o construtor acima descriptografa os corpos e os recursos do método. Mas, para nossa surpresa, o desenvolvedor da ofuscação decidiu ... não renomear os metadados:
Isso mata todo o burburinho da engenharia reversa. Como dissemos em um
artigo anterior , gostaria de encontrar o problema real de uma ferramenta de vigilância de alta qualidade e protegida, cuja análise levará mais de cinco minutos.
De qualquer forma, descompactar qualquer binário protegido por confuser (ex) é muito simples: use o dumper de binários .NET ou o ponto de interrupção da instrução break em <MODULE> .ctor e faça o dump. O processo leva 30 segundos e esse empacotador sempre será o meu favorito, porque a proteção contra depuração
nunca funciona .
Decidimos usar o MegaDumper: isso é um pouco mais rápido que o dumping manual:
Após descarregar o binário ExamCookie, a seguinte mensagem deve aparecer:
Agora você tem um diretório com todos os fragmentos do assembler que são carregados no processo correspondente, desta vez com corpos de método descriptografados.
Quem implementou essa ofuscação, graças a Deus, pelo menos ele criptografou as linhas:
else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.SymbolicLink)) { Module1.DebugPrint(<Module>.smethod_5<string>(1582642794u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.Tiff)) { Module1.DebugPrint(<Module>.smethod_2<string>(4207351461u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.UnicodeText)) { Module1.DebugPrint(<Module>.smethod_5<string>(3536903244u), new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.WaveAudio)) { Module1.DebugPrint(<Module>.smethod_2<string>(2091555364u), new object[0]); }
Sim, a boa e antiga criptografia de cadeia de caracteres Confuser (Ex), a melhor pseudo-segurança do mundo do .NET. É bom que o Confuser (Ex) tenha sido invadido com tanta frequência que as ferramentas de desofuscação estejam disponíveis na Internet para cada mecanismo, para não tocarmos em nada relacionado ao .NET. Execute o
ConfuserExStringDecryptor do
CodeCracker no despejo binário:
Ele converte o snippet anterior para isso:
else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.SymbolicLink)) { Module1.DebugPrint("ContainsData.SymbolicLink", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.Tiff)) { Module1.DebugPrint("ContainsData.Tiff", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.UnicodeText)) { Module1.DebugPrint("ContainsData.UnicodeText", new object[0]); } else if (System.Windows.Forms.Clipboard.ContainsData(DataFormats.WaveAudio)) { Module1.DebugPrint("ContainsData.WaveAudio", new object[0]); }
Essa é toda a proteção do aplicativo, quebrada em menos de um minuto ... Não publicaremos nossas ferramentas aqui, porque não as desenvolvemos e não temos código-fonte. Mas quem quiser repetir o trabalho pode encontrá-los no
Tuts4You . Como não temos mais uma conta tuts4you, não podemos vincular aos espelhos.
Funcionalidade
Surpreendentemente, nenhuma "funcionalidade oculta" real foi encontrada. Conforme indicado no site, as seguintes informações são enviadas periodicamente ao servidor:
- Lista de processos (a cada 5000 ms)
- Aplicativo ativo (a cada 1000 ms)
- Área de transferência (a cada 500 ms)
- Captura de tela (a cada 5000 ms)
- Lista de adaptadores de rede (a cada 20.000 ms)
O restante do aplicativo é muito chato, por isso decidimos pular todo o procedimento de inicialização e ir diretamente para as funções responsáveis pela captura de informações.
Adaptador
Os adaptadores de rede são montados pela função .NET
NetworkInterface.GetAllNetworkInterfaces()
, exatamente como no
artigo anterior :
NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { try {
Aplicativo ativo
Isso está ficando interessante. Em vez de registrar todas as janelas abertas, o utilitário controla apenas o aplicativo ativo. A implementação é exagerada; portanto, apresentamos um pseudocódigo bonito:
var whiteList = { "devenv", "ExamCookie.WinClient", "ExamCookie.WinClient.vshost", "wermgr", "ShellExperienceHost" };
Ótimo ... as pessoas ainda usam nomes de processos para diferenciá-los. Eles nunca param e não pensam: "Espere um minuto, você pode alterar os nomes dos processos como quiser", para que possamos contornar essa proteção com segurança.
Se você leu um artigo anterior sobre outro programa de rastreamento de exames, provavelmente reconhecerá esta implementação subpara os navegadores:
private bool IsBrowser(System.Diagnostics.Process proc) { bool result; try { string left = proc.ProcessName.ToLower(); if (Operators.CompareString(left, "iexplore", false) != 0 && Operators.CompareString(left, "chrome", false) != 0 && Operators.CompareString(left, "firefox", false) != 0 && Operators.CompareString(left, "opera", false) != 0 && Operators.CompareString(left, "cliqz", false) != 0) { if (Operators.CompareString(left, "applicationframehost", false) != 0) { result = false; } else { result = proc.MainWindowTitle.Containing("Microsoft Edge"); } } else { result = true; } } catch (Exception ex) { result = false; } return result; }
private string GetBrowserName(string name) { if (Operators.CompareString(name.ToLower(), "iexplore", false) == 0) { return "IE-Explorer"; } else if (Operators.CompareString(name.ToLower(), "chrome", false) == 0) { return "Chrome"; } else if (Operators.CompareString(name.ToLower(), "firefox", false) == 0) { return "Firefox"; } else if (Operators.CompareString(name.ToLower(), "opera", false) == 0) { return "Opera"; } else if (Operators.CompareString(name.ToLower(), "cliqz", false) == 0) { return "Cliqz"; } else if (Operators.CompareString(name.ToLower(), "applicationframehost", false) == 0) { return "Microsoft Edge"; } return ""; }
E a cereja no bolo:
private static string GetBrowserUrlById(object processId, string name) {
Esta é literalmente a mesma implementação que no artigo anterior. É difícil entender como os desenvolvedores ainda não perceberam o quão ruim é. Qualquer um pode editar o URL no navegador, isso nem vale a pena demonstrar.
Descoberta de máquina virtual
Ao contrário do que o site diz, iniciar em uma máquina virtual define um sinalizador. A implementação é ... interessante.
File.WriteAllBytes("ecvmd.exe", Resources.VmDetect); using (Process process = new Process()) { process.StartInfo = new ProcessStartInfo("ecvmd.exe", "-d") { CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true }; process.Start(); try { using (StreamReader standardOutput = process.StandardOutput) { result = standardOutput.ReadToEnd().Replace("\r\n", ""); } } catch (Exception ex3) { result = "-5"; } }
Bem, por algum motivo, eles escrevem um binário externo no disco e o executam, e depois confiam completamente nos resultados de E / S. Isso realmente acontece com bastante frequência, mas a transferência de um trabalho tão importante para outro processo desprotegido é mais ou menos. Vamos ver com qual arquivo estamos lidando:
Então agora usamos C ++? Bem, a interoperabilidade não é necessariamente ruim. E isso pode significar que agora precisamos realmente trabalhar em engenharia reversa (!!). Vejamos a AID:
int __cdecl main(int argc, const char **argv, const char **envp) { int v3;
Isso verifica a presença da porta de E / S do VMWare 'VX':
int __fastcall vm_detect::vmware_port() { int result;
A seguir, é verificada a execução da instrução de
extensão do pc virtual , que deve funcionar apenas quando iniciada em um ambiente virtualizado, se não levar a uma falha da máquina se for processada incorretamente;):
char vm_detect::vpcext() { char result;
... nenhuma engenharia reversa real, apenas 30 segundos para renomear duas funções :(
Este programa simplesmente lê a chave do registro e executa duas verificações de hipervisor que parecem estranhas em comparação com o outro programa. Gostaria de saber onde eles copiaram? Ah, veja, um artigo intitulado
"Métodos para descobrir máquinas virtuais (sic)" que explica esses métodos :). De qualquer forma, esses vetores de detecção podem ser contornados editando o arquivo .vmx ou usando uma versão aprimorada de qualquer hipervisor de sua escolha.
Protecção de dados
Como mencionado anteriormente, está em andamento uma investigação por não conformidade com o GDPR, e seu
site afirma:
Os dados são criptografados e enviados para um servidor seguro do Microsoft Azure, que pode ser acessado apenas com as credenciais corretas. Após o exame, os dados são armazenados por até três meses.
Não sabemos ao certo como eles determinam a "segurança" do servidor, pois as credenciais são codificadas no aplicativo e armazenadas em texto completamente não criptografado nos recursos de metadados:
Ponto de extremidade: https://examcookiewinapidk.azurewebsites.net
Nome de usuário: VfUtTaNUEQ
Senha: AwWE9PHjVc
Não examinamos o conteúdo do servidor (isso é ilegal), mas podemos assumir que o acesso total é fornecido lá. Como a conta está codificada no aplicativo, não há isolamento entre os contêineres de dados do aluno.
Isenção de responsabilidade legal: nos reservamos o direito de publicar credenciais da API porque elas são armazenadas em um arquivo binário público e, portanto, não são obtidas ilegalmente. No entanto, usá-los com intenção maliciosa claramente viola a lei; portanto, recomendamos enfaticamente que os leitores não usem as credenciais mencionadas de forma alguma e não sejam responsáveis por possíveis ações.Bypass
Como esse aplicativo lembra muito o Digital Exam Monitor, atualizamos o código do
ayyxam para dar suporte ao ExamCookie.
Lista de processos
A interface do processo .NET armazena em cache internamente os dados do processo usando a chamada de sistema
ntdll!NtQuerySystemInformation
. Esconder processos exige algum trabalho, porque as informações sobre o processo são indicadas em muitos lugares. Felizmente, o .NET recupera apenas um tipo específico de informação; portanto, você não precisa usar todos os métodos de
latebros .
Código para ignorar a validação de processos ativos.
NTSTATUS WINAPI ayyxam::hooks::nt_query_system_information( SYSTEM_INFORMATION_CLASS system_information_class, PVOID system_information, ULONG system_information_length, PULONG return_length) {
Buffer
ole32.dll!OleGetClipboard
, que é muito suscetível a ganchos, é responsável pela implementação interna de buffers no .NET. Em vez de gastar muito tempo analisando estruturas internas, você pode simplesmente retornar
S_OK
, e o tratamento de erros do .NET fará o resto:
std::int32_t __stdcall ayyxam::hooks::get_clipboard(void* data_object[[maybe_unused]]) {
Isso ocultará todo o buffer da ferramenta de vigilância ExamCookie sem atrapalhar a funcionalidade do programa.
Screenshots
Como sempre, as pessoas adotam uma implementação .NET pronta para a função desejada. Para contornar essa função, nem precisamos alterar nada no código anterior. As capturas de tela são controladas pela função .NET
Graphics.CopyFromScreen
. É essencialmente um invólucro para a transmissão de blocos de bits, que chama
gdi32!BitBlt
. Como nos videogames para combater sistemas anti-fraude que capturam capturas de tela, podemos usar o gancho BitBlt e ocultar qualquer informação indesejada antes de capturar uma captura de tela.
Abertura de sites
O URL do grabber é completamente copiado do programa anterior, para que possamos reutilizar nosso código para contornar a proteção. No
último artigo, documentamos a estrutura AutomationElement, que executa este gancho:
std::int32_t __stdcall ayyxam::hooks::get_property_value(void* handle, std::int32_t property_id, void* value) { constexpr auto value_value_id = 0x755D; if (property_id != value_value_id) return ayyxam::hooks::original_get_property_value(handle, property_id, value); auto result = ayyxam::hooks::original_get_property_value(handle, property_id, value); if (result != S_OK)
Descoberta de máquina virtual
A detecção preguiçosa de uma máquina virtual pode ser contornada de duas maneiras: 1) um patch de um programa que é liberado para o disco; ou 2) um redirecionamento do processo de criação de processo para um aplicativo fictício. Este último parece claramente mais simples :). Então, internamente
Process.Start()
chama
CreateProcess
, então apenas conecte e redirecione para qualquer aplicativo fictício que imprima o caractere '0'.
BOOL WINAPI ayyxam::hooks::create_process( LPCWSTR application_name, LPWSTR command_line, LPSECURITY_ATTRIBUTES process_attributes, LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD creation_flags, LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_information, LPPROCESS_INFORMATION process_information ) {
Baixar
Todo o projeto está disponível no
repositório do Github . O programa funciona injetando o binário x86 no processo correspondente.