Aprendí que el gobierno danés no solo
suspendió el programa Digital Exam Monitor, que analizamos y eludimos por completo en el
artículo anterior , sino que tal vez
cerró por completo este sistema una semana después de informarles sobre el método de piratería. No quiero pensar que fue solo por nosotros que el gobierno danés rechazó la idea de monitorear los exámenes, pero nuestro trabajo se notó claramente.
En este artículo, describiremos los detalles técnicos de cómo funciona otra herramienta de seguimiento de escolares: ExamCookie. Si solo está interesado en pasar por alto el sistema, desplácese hacia abajo hasta la sección correspondiente.
ExamCookie
Recientemente, esta herramienta
llegó a las noticias debido a una investigación sobre la violación de GDPR. Decidimos echar un vistazo al segundo competidor más grande del sistema de seguimiento escolar antes mencionado durante los exámenes:
ExamCookie . Este es un sistema de seguimiento comercial utilizado por más de 20 escuelas danesas. No hay documentación en el sitio que no sea la siguiente descripción:
ExamCookie es un software simple que monitorea la actividad de la computadora del estudiante durante el examen para asegurarse de que se sigan las reglas. El programa prohíbe a los estudiantes usar cualquier forma ilegal de asistencia.
ExamCookie guarda toda la actividad en la computadora: URL activas, conexiones de red, procesos, portapapeles y capturas de pantalla al cambiar el tamaño de la ventana.
El programa funciona simplemente: al ingresar al examen, lo ejecuta en su computadora y monitorea su actividad. Cuando se completa el examen, el programa se cierra y puede eliminarlo de la computadora.
Para comenzar a rastrear, debe usar su inicio de sesión UNI, que funciona en varios sitios educativos, o ingresar manualmente las credenciales. No utilizamos la herramienta, por lo que no podemos decir en qué casos se utiliza la entrada manual. Quizás esto se haga para estudiantes que no tienen un inicio de sesión UNI, lo que no consideramos posible.
Información binaria
El programa se puede descargar desde la página de inicio de ExamCookie. Es una aplicación x86 .NET. Como referencia, el hash MD5 binario analizado
63AFD8A8EC26C1DC368D8FF8710E337D
firma EXAMCOOKIE APS con fecha 24 de abril de 2019. Como mostró el
último artículo , el análisis del binario .NET difícilmente puede llamarse ingeniería inversa, porque la combinación de código IL y metadatos fáciles de leer proporciona un código fuente perfecto.
A diferencia del programa de monitoreo anterior, los desarrolladores de esta herramienta no solo la eliminaron del registro de depuración, sino que también la ofuscaron. Al menos lo intentaron :-)
Ofuscación (risas hasta lágrimas)
Cuando abrimos la aplicación en dnSpy, notamos rápidamente que faltaba un punto de entrada:
Extraño, generalmente se asume algún tipo de contenedor, cambia los cuerpos de los métodos del constructor del módulo, que se ejecuta en el punto de entrada real, veamos:
Genial Es 2019, y la gente todavía usa Confuser (Ex).
Al instante reconocimos este código de descompresión y verificamos los encabezados del ensamblador:
[módulo: ConfusedBy ("Confuser.Core 1.1.0 + a36320377a")]
Por el momento, pensamos que el código en realidad estaría ofuscado, ya que el constructor antes mencionado descifra los cuerpos y recursos del método. Pero, para nuestra sorpresa, el desarrollador de ofuscación decidió ... no renombrar los metadatos:
Esto mata todo el zumbido de la ingeniería inversa. Como dijimos en un
artículo anterior , me gustaría encontrar el verdadero problema de una herramienta de vigilancia de alta calidad y adecuadamente protegida, cuyo análisis llevará más de cinco minutos.
En cualquier caso, desempacar cualquier binario protegido por confuser (ex) es muy simple: use el volcador de binarios de .NET o el punto de interrupción de la declaración de interrupción en <MODULE> .ctor y bote usted mismo. El proceso lleva 30 segundos, y este empaquetador siempre será mi favorito, porque la protección contra la depuración
nunca funciona .
Decidimos usar MegaDumper: esto es un poco más rápido que el volcado manual:
Después de volcar el binario ExamCookie, debería aparecer el siguiente mensaje:
Ahora tiene un directorio con todos los fragmentos de ensamblador que se cargan en el proceso correspondiente, esta vez con cuerpos de métodos descifrados.
Quien implementó esta ofuscación, gracias a Dios, al menos cifró las líneas:
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]); }
Sí, el buen viejo encriptador de cadenas Confuser (Ex), la mejor pseudo-seguridad en el mundo de .NET. Es bueno que Confuser (Ex) haya sido pirateado con tanta frecuencia que las herramientas de desofuscación estén disponibles en Internet para cada mecanismo, por lo que no tocaremos nada relacionado con .NET. Ejecute ConfuserExStringDecryptor desde
CodeCracker en el volcado binario:
Convierte el fragmento anterior a esto:
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]); }
Esa es toda la protección de la aplicación, dividida en menos de un minuto ... No publicaremos nuestras herramientas aquí, porque no las desarrollamos y no tenemos código fuente. Pero cualquiera que quiera repetir el trabajo puede encontrarlos en
Tuts4You . Ya no tenemos una cuenta tuts4you, por lo que no podemos vincularnos a los espejos.
Funcionalidad
Sorprendentemente, no se encontró ninguna "funcionalidad oculta" real. Como se indica en el sitio web, la siguiente información se envía periódicamente al servidor:
- Lista de procesos (cada 5000 ms)
- Aplicación activa (cada 1000 ms)
- Portapapeles (cada 500 ms)
- Captura de pantalla (cada 5000 ms)
- Lista de adaptadores de red (cada 20,000 ms)
El resto de la aplicación es muy aburrida, por lo que decidimos omitir todo el procedimiento de inicialización e ir directamente a las funciones responsables de capturar la información.
Adaptador
Los adaptadores de red son ensamblados por la función .NET
NetworkInterface.GetAllNetworkInterfaces()
, exactamente como en el
artículo anterior :
NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { try {
Aplicación activa
Esto se está poniendo interesante. En lugar de registrar todas las ventanas abiertas, la utilidad controla solo la aplicación activa. La implementación es exagerada, por lo tanto, presentamos un pseudocódigo embellecido:
var whiteList = { "devenv", "ExamCookie.WinClient", "ExamCookie.WinClient.vshost", "wermgr", "ShellExperienceHost" };
Genial ... la gente todavía usa nombres de procesos para diferenciarlos. Nunca se detienen y no piensan: "Espera un minuto, puedes cambiar los nombres de los procesos como quieras", para que podamos evitar esta protección de manera segura.
Si lee un artículo anterior sobre otro programa de seguimiento de exámenes, probablemente reconocerá esta implementación deficiente para los 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 ""; }
Y la guinda del pastel:
private static string GetBrowserUrlById(object processId, string name) {
Esta es literalmente la misma implementación que en el artículo anterior. Es difícil entender cómo los desarrolladores aún no se han dado cuenta de lo malo que es. Cualquiera puede editar la URL en el navegador, ni siquiera vale la pena demostrarlo.
Descubrimiento de máquinas virtuales
Al contrario de lo que dice el sitio web, comenzar en una máquina virtual establece una bandera. La implementación es ... interesante.
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"; } }
Bueno, por alguna razón, escriben un binario externo en el disco y lo ejecutan, y luego confían completamente en los resultados de E / S. Esto realmente sucede con bastante frecuencia, pero la transferencia de un trabajo tan importante a otro proceso desprotegido es regular. Veamos con qué archivo estamos tratando:
Entonces, ¿ahora usamos C ++? Bueno, la interoperabilidad en realidad no es necesariamente mala. Y esto puede significar que ahora realmente tenemos que trabajar en ingeniería inversa (!!). Veamos la IDA:
int __cdecl main(int argc, const char **argv, const char **envp) { int v3;
Esto verifica la presencia del puerto de VMWare I / O 'VX':
int __fastcall vm_detect::vmware_port() { int result;
A continuación, se verifica la ejecución de la instrucción de
extensión de PC virtual , que solo debería funcionar cuando se inicia en un entorno virtualizado, si no provoca un bloqueo de la máquina si se procesa incorrectamente;
char vm_detect::vpcext() { char result;
... sin ingeniería inversa real, solo 30 segundos para renombrar dos funciones :(
Este programa simplemente lee la clave de registro y ejecuta dos comprobaciones de hipervisor que se ven raras en comparación con su otro programa. Me pregunto dónde lo copiaron. Oh, mira, un artículo titulado
"Métodos para descubrir máquinas virtuales (sic)" que explica estos métodos :). En cualquier caso, estos vectores de detección pueden eludirse editando el archivo .vmx o usando una versión mejorada de cualquier hipervisor de su elección.
Protección de datos
Como se mencionó anteriormente, se está llevando a cabo una investigación por incumplimiento del GDPR, y su
sitio web dice:
Los datos se cifran y se envían a un servidor seguro de Microsoft Azure, al que solo se puede acceder con las credenciales correctas. Después del examen, los datos se almacenan hasta por tres meses.
No estamos muy seguros de cómo determinan la "seguridad" del servidor, ya que las credenciales están codificadas en la aplicación y se almacenan en texto completamente claro en los recursos de metadatos:
Punto final: https://examcookiewinapidk.azurewebsites.net
Nombre de usuario: VfUtTaNUEQ
Contraseña: AwWE9PHjVc
No examinamos el contenido del servidor (esto es ilegal), pero podemos suponer que allí se proporciona acceso completo. Como la cuenta está codificada en la aplicación, no hay aislamiento entre los contenedores de datos del alumno.
Descargo de responsabilidad legal: Nos reservamos el derecho de publicar las credenciales de la API porque se almacenan en un archivo binario público y, por lo tanto, no se obtienen ilegalmente. Sin embargo, usarlos con intención maliciosa viola claramente la ley, por lo tanto, recomendamos encarecidamente que los lectores no usen las credenciales mencionadas de ninguna manera y no sean responsables de ninguna acción potencial.Bypass
Dado que esta aplicación recuerda increíblemente a Digital Exam Monitor, acabamos de actualizar el código
ayyxam para admitir ExamCookie.
Lista de procesos
La interfaz de proceso .NET almacena en caché internamente los datos de proceso utilizando la llamada al sistema
ntdll!NtQuerySystemInformation
. Para ocultar los procesos requiere un poco de trabajo, porque la información sobre el proceso se indica en muchos lugares. Afortunadamente, .NET solo recupera un tipo específico de información, por lo que no tiene que usar todos los métodos de
latebros .
Código para evitar la validación de procesos activos.
NTSTATUS WINAPI ayyxam::hooks::nt_query_system_information( SYSTEM_INFORMATION_CLASS system_information_class, PVOID system_information, ULONG system_information_length, PULONG return_length) {
Tampón
ole32.dll!OleGetClipboard
, que es muy susceptible a los ganchos, es responsable de la implementación interna de buffers en .NET. En lugar de pasar mucho tiempo analizando estructuras internas, simplemente puede devolver
S_OK
, y el manejo de errores .NET hará el resto:
std::int32_t __stdcall ayyxam::hooks::get_clipboard(void* data_object[[maybe_unused]]) {
Esto ocultará todo el búfer de la herramienta de vigilancia ExamCookie sin alterar la funcionalidad del programa.
Capturas de pantalla
Como siempre, las personas toman una implementación .NET lista para usar de la función deseada. Para evitar esta función, ni siquiera tuvimos que cambiar nada en el código anterior. Las capturas de pantalla están controladas por la función
Graphics.CopyFromScreen
.NET. Es esencialmente un contenedor para transmitir bloques de bits, que llama a
gdi32!BitBlt
. Al igual que en los videojuegos para combatir sistemas anti-trampas que toman capturas de pantalla, podemos usar el gancho BitBlt y ocultar cualquier información no deseada antes de tomar una captura de pantalla.
Abrir sitios
La URL de captura se copia completamente del programa anterior, por lo que nuevamente podemos reutilizar nuestro código para evitar la protección. En el
último artículo, documentamos la estructura AutomationElement, como resultado de lo cual se lanza el siguiente enlace:
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)
Descubrimiento de máquinas virtuales
La detección diferida de una máquina virtual se puede eludir de dos maneras: 1) un parche de un programa que se vacía en el disco; o 2) una redirección del proceso de creación del proceso a una aplicación ficticia. Esto último parece claramente más simple :). Entonces, internamente
Process.Start()
llama a
CreateProcess
, así que solo engancha y redirige a cualquier aplicación ficticia que imprima el carácter '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 ) {
Descargar
Todo el proyecto está disponible en
el repositorio de Github . El programa funciona inyectando el binario x86 en el proceso correspondiente.