Detección de Malware en scripts Web con Regex, md5 checksum y PHP

Camilo Herrera
winkhostingla
Published in
16 min readJan 23, 2023
Photo by Bernard Hermant on Unsplash

Hola!, en esté artículo vamos a crear una prueba de concepto para la detección de malware en scripts cargados en un sitio web. Ten presente que existen varias herramientas especializadas para este tipo de tareas, pero si quieres saber cómo funcionan de forma general, bienvenido.

Scripts maliciosos y firmas de malware

Los scripts maliciosos son todos aquellos que pueden llegar a ser inyectados (mezclados con un script confiable), los cargados a un servidor o sitio web usando una vulnerabilidad conocida, phishing ó a través de funcionalidades pobremente auditadas en un sitio o sistema expuesto a Internet.

Estos scripts son tan variados como puedas imaginar y utilizan diferentes mecanismos para operar y evitar su detección una vez se encuentran dentro de un servidor.

Para este caso en particular hablaremos de los scripts PHP debido a su popularidad en entornos de hosting compartido y manejadores de contenido como WordPress (Técnicamente no es un CMS pero se usa de la misma forma, no le digas a nadie — guiño — ).

La gran mayoría de scripts de este tipo tienen algo en común, contenido, cadenas de texto ó estructuras de código fuente que pueden ser usadas para generar una firma. La firma es un elemento que puede permitir diferenciar y detectar un archivo de interés y determinar si es malicioso o no. Pueden generarse firmas usando expresiones regulares, también generando un checksum (md5 por ejemplo) del contenido de un archivo ó incluso palabras claves en texto plano que pueden indicar una infección.

A continuación un veremos ejemplo de cada uno de los tipos de firmas mencionados:

Expresión Regular

$regexSample = "#/\$.*\[\$.*\] \= chr\(ord\(\$.*\[\$.*\]\) \^ ord\(\$.*\[\$.* \% \$.*\]\)\)\/#";

Checksum md5

$md5FileChecksum = "da4b6ccd2702858d185e3ef600eeaeef";

Palabras clave en texto plano

$plainText = "Hello I am a malicious Script";

Estructura de nuestra solución y scripts PHP a crear

A partir de estos conceptos, vamos a construir una prueba de concept con PHP (Obviamente porque es mi lenguaje de programación favorito) que recorrerá un listado de archivos en búsqueda de coincidencias usando las firmas predeterminadas en un archivo y concluir si algunos de ellos son maliciosos o no.

Importante: Esta solución se utilizará a través de consola de comandos, no tendrá interfaz web.

Estructura

Los componentes o funcionalidades generales de nuestra prueba de concepto serán:

  • Gestión de firmas (Expresiones regulares, md5 checksum, Texto plano)

Este componente será el encargado de cargar las firmas previamente definidas en un archivo de texto, y ponerlas a disposición del motor de detección.

  • Reporte de resultados de coincidencias

Se encargará de presentar los mensajes necesarios en pantalla y crear un log de registro que contendrá los resultados del proceso de revisión de archivos, este será generado en texto plano.

  • Gestión de acciones a partir de resultados de la detección

Este componente le permitirá al motor tomar acciones a partir de la detección de contenido que coincida con las firmas existentes, ya sea mover a cuarentena, crear una copia en un lugar seguro, generar una advertencia ó borrar el archivo.

  • Motor de detección de coincidencias

Estará encargado de recorrer una estructura de directorios, leer el contenido de cada archivo en ellos y buscar si hay coincidencias con las firmas disponibles.

Directorios y archivos requeridos antes de iniciar

Para iniciar, crea los directorios y archivos como se indica a continuación, en una ruta que prefieras y en la que tengas acceso a php desde consola de comandos. Para esta publicación usaremos PHP 8.1.6:

/malware-scan
/quarantine
/copy
/scan-folder
sample-md5.js
sample-plain.txt
sample-regex.php
signatures.txt

Muestras de archivos

En el directorio /scan-folder tendremos tres archivos con muestras del contenido a escanear, estos servirán para realizar las pruebas, en el nombre de cada archivo podemos determinar el tipo de muestra.

sample-md5.js

En el archivo sample-md5.js guarda el siguiente contenido sin espacios o líneas vacías al inicio y fin:

// Function to compute the product of p1 and p2
function myFunction(p1, p2) {
return p1 * p2;
}

sample-plain.txt

En el archivo sample-plain.txt guarda el siguiente contenido:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sit amet tempus tellus. 
Praesent malesuada lobortis erat at pharetra. Sed nec semper sapien.
Vivamus consequat dapibus finibus. Pellentesque vel est ac nisi lobortis efficitur.
Ut magna lacus, ultricies vitae vulputate in, My Signature Text accumsan vitae felis.
In mollis mollis velit aliquam consectetur. Quisque eu luctus enim.
Maecenas mollis ultricies nulla. Suspendisse quis ipsum et dolor consectetur pulvinar vel sed massa.

sample-regex.php

Y en el archivo sample-regex.php el siguiente contenido:

<?php

$var[$key] = chr(ord($anothervar[$anotherkey]) ^ ord($secondvar[$thirdvar % $d]));

Archivo de firmas

Este archivo con el nombre signatures.txt contendrá las firmas, tipo y acción a realizar para cada una de ellas.

En el archivo guarda el siguiente contenido sin espacios o líneas vacías al inicio y final:

plaintext:quarantine:My Signature Text
md5:warning:da4b6ccd2702858d185e3ef600eeaeef
regex:delete:\$.*\[\$.*\] \= chr\(ord\(\$.*\[\$.*\]\) \^ ord\(\$.*\[\$.* \% \$.*\]\)\)\

Ahora si, todo listo, vamos a implementar cada uno de los componentes de nuestra prueba de concepto.

Pro tip: Recuerda que la fuerza en todo lo que haces la adquieres avanzando en eso que te propones. Incluso programar y desarrollar software.

Gestión de firmas (Expresiones regulares, md5 checksum, Texto plano)

Para almacenar nuestras firmas debemos definir un formato general para su estructura, usaremos un formato personalizado en texto plano y cada una de las firmas tendrá los siguientes campos:

  • Type (Tipo)

Este campo contendrá el tipo que puede ser “regex” usado para expresiones regulares, “plaintext” para palabras o términos en texto plano y “md5” usado para comparar el checksum md5 del archivo con el que se encuentra en la regla.

  • Action (Acción)

Este campo indicará la acción a tomar si es detectado contenido que coincida con la regla y puede ser “quarantine”, “copy”, “warning”, “delete”.

“quarantine” moverá el archivo a un directorio predefinido para cuarentena, “copy” creará una copia en un directorio predefinido, “warning” generará un mensaje de alerta en pantalla y el correspondiente registro en el reporte de scan, “delete” borrará el archivo pero no dejará copia ni moverá a cuarentena.

  • Signature (Firma)

Este campo contendrá el texto correspondiente a la expresión regex, texto plano o checksum md5 a ser usado para detectar el contenido.

A continuación un ejemplo de firma en el formato propuesto:

#<type>:<action>:<signature>
plaintext:quarantine:My Signature Text

Se escribirá primero el tipo de firma, “:”, la acción a realizar, “:” y el texto de la firma.

Todas las firmas serán guardas en un archivo que será cargado al inicio de la ejecución del script, recuerda que lo llamaremos signatures.txt, no olvides crear el archivo en el directorio. Ten presente que serán varios items (firmas) en el mismo archivo, el formato para representarlos será algo como esto:

signatures.txt (Ejemplo)

plaintext:quarantine:My Signature Text
md5:warning:da4b6ccd2702858d185e3ef600eeaeef
regex:delete:\$.*\[\$.*\] \= chr\(ord\(\$.*\[\$.*\]\) \^ ord\(\$.*\[\$.* \% \$.*\]\)\)\

Ahora vamos a crear una clase llamada SignatureManager para leer el archivo de firmas, darles formato y prepararlas para ser usadas por el motor de detección.

Esta clase cuenta con dos Backed Enums SigType y ActType, estos son usados para definir los tipos de firmas (SigType) y los tipos de acciones (ActTypes) permitidas así:

SignatureManager.php

<?php

/**
* Tipos de firmas permitidos.
*/
enum SigType: string
{
case REGEX = "regex";
case PLAINTEXT = "plaintext";
case MD5 = "md5";
}

/**
* Tipos de acciones permitidas al detectar una coincidencia de una firma.
*/
enum ActType: string
{
case QUARANTINE = "quarantine";
case COPY = "copy";
case WARNING = "warning";
case DELETE = "delete";
}

Ahora la declaración de la clase SignatureManager, en el constructor declararemos algunas variables con sus valores por defecto, dos funciones y un getter así:



/**
* Esta clase carga las reglas almacenadas en el archivo en la ruta indicada por $signatureFilePath y las procesa para prepararlas para
* ser usadas por el motor de scan de archivos.
*
* TODO: evaluar los posibles pasos en los que puede presentarse un error al leer el archivo o las líneas en él, no hace parte de este
* artículo así que procede con precaución.
*/
class SignatureManager
{

/**
* En este punto definimos los valores por defecto de la ruta donde se encuentran las firmas
* $signatureFilePath, inicializamos el arreglo $signatures donde se guardarán las firmas y
* definimos el valor del separador de campos por defecto $fieldSeparator.
*/
function __construct(
private string $signatureFilePath = __DIR__ . "/signatures.txt",
private array $signatures = array(),
private string $fieldSeparator = ":"
) {
}

/**
* Esta función es la encargada de leer el archivo de firmas y cargarlas en el arreglo $signatures
*
* @return void
*/
public function loadSignatures(): void
{
$sigFileContents = file($this->signatureFilePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

foreach ($sigFileContents as $key => $sigLine) {

$loadedType = "";
$loadedAction = "";
$loadedSignature = "";

$loadedType = $this->loadField(SigType::cases(), $sigLine);
$loadedAction = $this->loadField(ActType::cases(), $sigLine);
$loadedSignature = $sigLine;

$this->signatures[$loadedType] = array("actType" => $loadedAction, "signature" => $loadedSignature);
}
}

/**
* Esta función procesa una línea de texto extraída del archivo de firmas y captura
* los valores de los campos necesarios para ser usada por el motor de detección.
*
* @param array $cases Este arreglo contiene los casos de un Backe Enum que puede ser
* del tipo SigType o ActType definidos en este archivo.
* @param string $textLine Este parámetro pasado por referencia contiene el texto
* de la línea a procesar.
* @return string Se retorna el campo capturado en formato texto.
*/
private function loadField(array $cases, string &$textLine): string
{
$retValue = "";

foreach ($cases as $enumItem) {
if (str_contains($textLine, $enumItem->value . $this->fieldSeparator)) {

$retValue = $enumItem->value;
$textLine = str_replace($enumItem->value . $this->fieldSeparator, "", $textLine);
}
}

return $retValue;
}

/**
* Este getter retorna el arreglo de firmas cargadas.
* @return array Contenido del arreglo $signatures en el objeto.
*/
public function getSignatures(): array
{
return $this->signatures;
}
}

En el constructor definimos la ruta del archivo de firmas $signatureFilePath que apuntará al archivo con nombre signatures.txt en el directorio en el que se ejecute el script, el arreglo de firmas $signatures en donde serán guardadas una vez leídas y procesadas desde el archivo y el separador de campos de cada línea $fieldSeparator, que en este caso será “:” a partir del formato que definimos al inicio.

La función loadSignatures() es la encargada de leer el contenido del archivo, recorrer cada una de las líneas leídas y, usando la función loadField(), detectar el tipo de firma y la acción a realizar, dejando por último la firma asociada a estos dos elementos. Al contar con los tres elementos (Firma, Tipo y Acción) se guarda la regla en el arreglo $signatures con el siguiente formato:

$this->signatures[<tipo de firma>] = array("actType" => <accion>, "signature" => <firma>);

Por último el getter getSignatures(), será usado para que las instancias de otras clases puedan obtener el listado de firmas listo para ser usado.

Reporte de resultados de coincidencias

Este componente es bastante sencillo y tiene dos responsabilidades, la primera es la presentación de mensajes en pantalla para las acciones realizadas por el motor de detección y la segunda, guardar en un archivo de registro de eventos cada uno de estos mensajes.

Vamos a iniciar con la creación del archivo para la clase encargada de estas tareas. Lo llamaremos Reporter.php. Debes crearlo en la raíz del directorio donde estás trabajando en tu proyecto, en este caso “/malware-scan”

Una vez contemos con el archivo, vamos a crear la clase con el mismo nombre dentro de él, de la siguiente forma:

Reporter.php

<?php

class Reporter
{
//ruta donde será guardado el archivo de registro de las actividades realizadas.
private string $reportFilePath;

/**
* Aquí definimos el valor por defecto para la ruta de guardado $reportFilePath
* o podemos personalizarla enviando una nueva ruta como parámetro del constructor.
*/
function __construct(string $reportFilePath = "")
{
if (empty($reportFilePath)) {
$this->reportFilePath = __DIR__ . "/report_" . date("Y-m-d_H-i-s") . ".log";
}
}

/**
* Esta función presentará el mensaje enviado en el parámetro $messageText en la consola
* y lo guardará en el archivo indicado por $reportFilePath.
* @param string $messageText Mensaje a ser mostrado y guardado en el registro
* @param bool $noDate Permite indicar si se desea agregar la fecha hora y
* milisegundos al inicio del mensaje a mostrar, también
* se agregará al registro, por defecto false que
* indica que siempre se agrega, o true para no hacerlo.
* @return void
*/
public function echoMessage(string $messageText, bool $noDate = false): void
{
$echotext = $messageText . PHP_EOL;

if (!$noDate) {
$echotext = "[" . date("Y-m-d H:i:s.u") . "] " . $echotext;
}

echo $echotext;
file_put_contents($this->reportFilePath, $echotext, FILE_APPEND | LOCK_EX);
}
}

Como puedes ver en el código, el comportamiento de esta clase es bastante sencillo, mostrar y guardar texto en un archivo de registro, los archivos son creados en la raíz del directorio donde se ejecutan tus scripts y su nombre corresponderá a la fecha y hora de ejecución.

Veremos su uso al implementar el motor de detección.

Gestión de acciones a partir de resultados de la detección

Este componente tendrá la responsabilidad de, a partir de la acción indicada por la firma o regla detectada, realizar operaciones en los archivos.

Iniciemos tal como en las secciones anteriores, crearemos el archivo para la clase y lo llamaremos Actuator.php, en él crearemos la clase Actuactor.

Importante: Esta clase requiere dos directorios por defecto dentro del directorio donde se ejecutan los scripts, estos son “quarantine” y “copy”, en ellos serán guardados los archivos para las acciones de cuarentena y copiado respectivamente.

Nuestra clase quedará así:

Actuator.php

<?php

class Actuator
{
private string $quarantinePath;
private string $copyPath;

/**
* En el constructor definimos los valores por defecto para las rutas
* de cuarentena y copiado, para las acciones correspondientes
* (quarantine y copy)
*/
function __construct()
{
$this->quarantinePath = __DIR__ . "/quarantine";
$this->copyPath = __DIR__ . "/copy";
}

/**
* esta función es la encargada de realizar la acción solicitada en el parámetro
* $action, este parámetro es del tipo ActType, que es el backed enum que contiene
* las posibles acciones a realizar.
*
* @param ActType $action Case dentro del backed enum ActType que indica
* la acción a realizar. Ej. ActType::QUARANTINE
* @param string $filePath Ruta del archivo al que se le aplicará la acción.
*/
public function performaAction(ActType $action, string $filePath): string
{
/*
Advertencia: en está línea aplicamos un poco de magia de php, y hacemos el llamado a la acción
a partir del valor del case, es decir, lo que hacemos es llamar al método
dentro de la clase con la cadena de texto de la acción.
Ej. $this->{"quarantine"}($filePath);
De esta forma evitamos hacer un switch-case o un if interminable si implementamos
más acciones en el futuro.

Castigamos legibilidad por menos código y tiempo invertido. Creo que en estos casos
vale la pena el riesgo.
*/
return $this->{$action->value}($filePath);
}

/**
* Acción a realizar para poner en cuarentena un archivo.
*
* @param string $filePath La ruta del archivo al que se le aplicará la acción
* @return string Mensaje de resultado de la acción, será usado para
* mostrar en pantalla y guardar en el registro.
*/
private function quarantine(string $filePath): string
{
rename($filePath, $this->quarantinePath . "/" . basename($filePath));
return $filePath . " moved to quarantine folder " . $this->quarantinePath;
}

/**
* Acción a realizar para crear una copia de un archivo.
*
* @param string $filePath La ruta del archivo al que se le aplicará la acción
* @return string Mensaje de resultado de la acción, será usado para
* mostrar en pantalla y guardar en el registro.
*/
private function copy(string $filePath): string
{
copy($filePath, $this->copyPath . "/" . basename($filePath));
return $filePath . " copied to folder " . $this->copyPath;
}

/**
* Acción a realizar para generar una advertencia sobre un archivo.
*
* @param string $filePath La ruta del archivo al que se le aplicará la acción
* @return string Mensaje de resultado de la acción, será usado para
* mostrar en pantalla y guardar en el registro.
*/
private function warning(string $filePath): string
{
return "WARNING!: Detected suspicious content on " . $filePath . PHP_EOL;
}

/**
* Acción a realizar para borrar un archivo.
*
* @param string $filePath La ruta del archivo al que se le aplicará la acción
* @return string Mensaje de resultado de la acción, será usado para
* mostrar en pantalla y guardar en el registro.
*/
private function delete(string $filePath): string
{
unlink($filePath);
return $filePath . " Deleted." . PHP_EOL;
}
}

Como puedes ver, esta contienen una función para cada acción disponible, estas funciones retornan un mensaje que será usado por nuestro componente de reporte para mostrarlo en pantalla y guardarlo.

La clase también define en el constructor las rutas por defecto a usar para el copiado y puesta en cuarentena de los archivos.

Motor de detección de coincidencias

El motor de detección solo tiene una responsabilidad y es iterar el contenido dentro del directorio que es designado para ser verificado y buscar en el contenido de cada archivo cada una de las firmas cargadas.

Para esto vamos a crear una clase llamadas ScanEngine en su correspondiente archivo ScanEngine.php.

Nuestra clase tendrá dos propiedades, la ruta del directorio a verificar $scanPath, que por defecto apuntará a un directorio llamado “/scan-folder” dentro del mismo directorio donde se ejecutan nuestros scripts y en él se encuentran tres muestras con las firmas que tenemos como ejemplo, la segunda propiedad es un arreglo que contiene las extensiones de los tipos de archivo de interés en la búsqueda, $allowedFileTypes.

Las extensiones que usaremos en nuestra prueba de concepto serán:

  • html
  • php
  • js
  • css
  • gif
  • jpg
  • jpeg
  • txt

Estás son las extensiones de archivos web más comunes afectadas por malware en entornos de hosting compartido.

Nuestra clase contará también con cuatro funciones: run() encargada de ejecutar el scan y tres funciones más, una para cada tipo de firma (regex, texto plano y checksum md5). Estas tres funciones restantes retornan true/false dependiendo de si es detectado el contenido buscado.

El script final para nuestro motor de detección se verá así:

ScanEngine.php

<?php

class ScanEngine
{
private string $scanPath;
private array $allowedFileTypes;


/**
* En el constructor definimos los valores por defecto del directorio a escanear y puede
* personalizarse enviando una nueva ruta el en parámetro $scanPath al instanciar el objeto
*
* Adicionalmente se definen los tipos de archivo de interés a escanear.
* @param string $scanPath Ruta a escanear.
*/
function __construct(
string $scanPath = __DIR__ . "/scan-folder"
) {
$this->scanPath = $scanPath;
$this->allowedFileTypes = array("html", "php", "js", "css", "gif", "jpg", "jpeg", "txt");
}

/**
* Ejecuta el escaneo de la ruta definida en $scanPath.
* @return void
*/
public function run(): void
{

$reportMan = new Reporter();

$reportMan->echoMessage("Malware scan started!");
$reportMan->echoMessage("", true);
$reportMan->echoMessage("Scanning Directory " . $this->scanPath);

$sigMan = new SignatureManager();
$sigMan->loadSignatures();

$signatures = $sigMan->getSignatures();

$actuator = new Actuator();

//Iteramos dentro del directorio indicado en $scanPath y analizamos cada archivo con las firmas existentes
$dirIterator = new \RecursiveDirectoryIterator($this->scanPath);
/** @var \RecursiveDirectoryIterator | \RecursiveIteratorIterator $it */
$it = new \RecursiveIteratorIterator($dirIterator);

while ($it->valid()) {
if (!$it->isDot() && $it->isFile() && $it->isReadable()) {

// $file is a SplFileInfo instance
$file = $it->current();
$filePath = $it->key();

if (in_array($file->getExtension(), $this->allowedFileTypes)) {

$reportMan->echoMessage("", true);
$reportMan->echoMessage($filePath);

foreach ($signatures as $sigType => $sigInfo) {

$flagDetected = false;

if (file_exists($filePath)) {
if ($sigType == SigType::PLAINTEXT->value) {
$fileContent = file_get_contents($filePath);
$flagDetected = $this->checkPlainText($fileContent, $sigInfo["signature"]);
}

if ($sigType == SigType::REGEX->value) {
$fileContent = file_get_contents($filePath);
$flagDetected = $this->checkRegex($fileContent, $sigInfo["signature"]);
}

if ($sigType == SigType::MD5->value) {
$flagDetected = $this->checkMd5($filePath, $sigInfo["signature"]);
}
}

if ($flagDetected) {
$reportMan->echoMessage("(" . $sigInfo["actType"] . ") " . $sigType . " Signature [" . $sigInfo["signature"] . "] detected! ");

$actionResult = $actuator->performaAction(ActType::from($sigInfo["actType"]), $filePath);
$reportMan->echoMessage($actionResult);
}
}
}
}

$it->next();
}

$reportMan->echoMessage("", true);
$reportMan->echoMessage("Malware scan ended!");
}

/**
* Verifica si la firma en texto plano se encuentra en el contenido de la cadena $fileContent
* @param string $fileContent Contenido en el que se buscará la coincidencia de la firma
* @param string $signature Texto plano de la firma a buscar en la cadena de texto $fileContent
* @return bool True=encontrada, False=no encontrada
*/
private function checkPlainText(string $fileContent, string $signature): bool
{
$retVal = false;
if (str_contains($fileContent, $signature)) {
$retVal = true;
}
return $retVal;
}

/**
* Verifica si la expresión regular coincide con contenido en $fileContent
* @param string $fileContent Contenido en el que se buscará la coincidencia de la firma
* @param string $signature Expresión regular a buscar en la cadena de texto $fileContent
* @return bool True=encontrada, False=no encontrada
*/
private function checkRegex(string $fileContent, string $signature): bool
{
$retVal = false;

$res = preg_match("#" . $signature . "#", $fileContent);

if ($res == 1) {
$retVal = true;
}

return $retVal;
}

/**
* Verifica si el checksum md5 del archivo coincide con el checksum en la firma
* @param string $filePath Ruta del archivo a verificar
* @param string $signature md5 checksum a comparar con el generado para el archivo
* @return bool True=encontrada, False=no encontrada
*/
private function checkMd5(string $filePath, string $signature): bool
{
$retVal = false;

if ($signature == md5_file($filePath)) {
$retVal = true;
}

return $retVal;
}
}

Esos serían todos nuestros scripts para los componentes, ahora vamos a crear un archivo que será el punto de entrada para iniciar un proceso de detección.

Script de entrada para ejecutar un scan

Nuestro script de entrada será el siguiente:

scan.php

<?php
//Esta variable almacenará el argumento path= recibido a través de línea de comandos.
$pathToScan = "";

foreach ($argv as $key => $argument) {
if (str_starts_with($argument, "path=")) {
$pathToScan = str_replace("path=", "", $argument);
//windows fix
$pathToScan = str_replace("\\", "/", $pathToScan);
}
}

include("SignatureManager.php");
include("ScanEngine.php");
include("Reporter.php");
include("Actuator.php");

$scanEng = new ScanEngine($pathToScan);
$scanEng->run();

Tal como está planteado, nuestro ejemplo ejecuta un scan por defecto en el directorio /scan-folder dentro de la raíz del directorio donde se encuentren nuestros scripts. Pero si envías el argumento “path=” en la línea de comandos, se usará la ruta en él para realizar el scan.

Este incluye nuestras clases, crea una instancia de ScanEngine e inicia el proceso al invocar la función run();

Estructura final de archivos y directorios

Para que nuestra prueba de concepto funcione. La estructura definitiva de archivos y directorios de tu proyecto debería quedar de la siguiente forma:

/malware-scan
/quarantine
/copy
/scan-folder
sample-md5.js
sample-plain.txt
sample-regex.php
Actuator.php
SignatureManager.php
ScanEngine.php
Reporter.php
scan.php
signatures.txt

Ya contando con todos nuestros archivos, resta hacer una prueba, vamos a ver el resultado.

Prueba de scan

Para realizar una prueba, inicia una consola de comandos y ejecuta el script scan.php de la siguiente forma:

# Ejecución para el directorio por defecto /scan-folder
php scan.php

# Ejecución designando un directorio personalizado
php scan.php path=/home/my_malware_folder

El resultado de la ejecución será mostrado en la consola y guardado en el archivo de log correspondiente, así:

[2023-01-23 14:57:01.000000] Malware scan started!

[2023-01-23 14:57:01.000000] Scanning Directory D:/wamp64/www/malware-scan/scan-folder

[2023-01-23 14:57:01.000000] D:/wamp64/www/malware-scan/scan-folder\sample-md5.js
[2023-01-23 14:57:01.000000] (warning) md5 Signature [1baa5c73942b260cbe22c4effa8b5cda] detected!
[2023-01-23 14:57:01.000000] WARNING!: Detected suspicious content on D:/wamp64/www/malware-scan/scan-folder\sample-md5.js

[2023-01-23 14:57:01.000000] D:/wamp64/www/malware-scan/scan-folder\sample-plain.txt
[2023-01-23 14:57:01.000000] (warning) plaintext Signature [My Signature Text] detected!
[2023-01-23 14:57:01.000000] WARNING!: Detected suspicious content on D:/wamp64/www/malware-scan/scan-folder\sample-plain.txt

[2023-01-23 14:57:01.000000] D:/wamp64/www/malware-scan/scan-folder\sample-regex.php
[2023-01-23 14:57:02.000000] (warning) regex Signature [\$.*\[\$.*\] \= chr\(ord\(\$.*\[\$.*\]\) \^ ord\(\$.*\[\$.* \% \$.*\]\)\)] detected!
[2023-01-23 14:57:02.000000] WARNING!: Detected suspicious content on D:/wamp64/www/malware-scan/scan-folder\sample-regex.php

[2023-01-23 14:57:02.000000] Malware scan ended!

En en este puedes consultar cada uno de los archivos verificados y el resultado de aquellos que presentan coincidencias con las firmas existentes y encontrarás un nuevo archivo .log en la raíz del directorio con el informe del scan.

Y este sería el final del proyecto, gracias por llegar hasta este punto. Recuerda que en Winkhosting.co somos mucho más que hosting.

--

--