
Escáner y analizador de webshells.
Web shell analyzer es un binario independiente multiplataforma construido exclusivamente para identificar, decodificar y etiquetar archivos sospechosos de ser web shells. Web shell analyzer es el hermano mayor del proyecto web shell scanner (http://github.com/tstillz/webshell-scan), que solo escanea archivos mediante expresiones regulares, sin decodificación ni análisis de atributos.
Las expresiones regulares y sus rutinas de decodificación integradas proporcionadas con el escáner no garantizan encontrar todos los web shells en el disco y pueden identificar algunos falsos positivos. También se recomienda probar el analizador y evaluar su impacto antes de ejecutarlo en sistemas de producción. El analizador no tiene garantía, úselo bajo su propio riesgo.
PHP, ASP/X. JSP/X, CFM y otros tipos están en desarrollo.Cada archivo escaneado puede pasar por una acción PRE y/o POST:
La idea detrás de las funciones PreDecodeActions era usar expresiones regulares para identificar una cadena o patrón coincidente, obtener el contenido bruto de la coincidencia, realizar los pasos de decodificación/limpieza definidos y enviar la salida final de vuelta al motor de análisis para un nuevo escaneo/procesamiento.
Un ejemplo muy simple de esto es la decodificación Base64. Para verificar cualquier lógica de detección contra un web shell codificado en base64, primero debemos eliminar todas las capas de base64. Para ello, podríamos usar la siguiente PreDecodeAction:
{
Name: "PHP_Base64Decode",
Regex: *regexp.MustCompile(`(?i)(?:=|\s+)(base64_decode\('('?\"?[A-Za-z0-9+\/=]+'?\"?))`),
DataCapture: *regexp.MustCompile(`(?i)((?:'|")[A-Za-z0-9+\/=]+(?:'|"))`),
PreDecodeActions: []cm.Action{
{Function: cm.StringReplace, Arguments: []interface{}{"\"", "", -1}},
{Function: cm.StringReplace, Arguments: []interface{}{"'", "", -1}},
},
Functions: []cm.Base_Func{cm.DecodeBase64},
},
Mirando el bloque anterior, primero tenemos el nombre de la función, la expresión regular utilizada para coincidir, la expresión regular de captura de datos (a veces puede querer ajustar qué capturar frente a qué coincide) y PreDecodeActions. En este caso, ANTES de que la función cm.DecodeBase64 se aplique al texto coincidente, el sistema primero eliminará los siguientes elementos " y '.
PostDecodeActions funciona al contrario, donde la salida se verifica DESPUÉS de realizar la decodificación. Usando este modelo, podemos crear múltiples decodificadores personalizados que tienen funciones PRE/POST y de decodificación infinitas para manejar la mayoría de las necesidades de análisis de web shells.
Una detección es una expresión regular acompañada de un nombre y una descripción. La idea detrás de este modelo era hacer que las detecciones fueran modulares y escalables, manteniendo el contexto con la detección real. Las detecciones comparten el mismo formato que los atributos, excepto que los atributos no pueden generar una detección, solo pueden agregar contexto a una detección existente. Veamos el bloque de lógica de detección de ejemplo a continuación:
{
Name: "Generic_Embedded_Executable",
Description: "Looks for magic bytes associated with a PE file",
Regex: *regexp.MustCompile(`(?i)(?:(?:0x)?4d5a)`),
},
Basándonos en la expresión regular, podemos ver que busca un archivo PE de Windows incrustado basado en los bytes mágicos de cabecera 4D 5A. Si se encuentra, esto llevaría a una detección y se generaría un informe JSON para el archivo.
Actualmente, las detecciones se aplican según la extensión del archivo o de forma genérica para todos los tipos de archivo. Por ejemplo, las rutinas de decodificación para PHP se definen bajo cm.GlobalMap.Function_Php y las etiquetas para cualquier atributo se definen bajo cm.GlobalMap.Tags_Php.
Las funciones cm.GlobalMap.Function_Generics y las etiquetas bajo cm.GlobalMap.Tags_Generics se aplican a TODAS las extensiones de web shells como comodín.
El etiquetado de atributos es un nuevo concepto que he creado y que agrega "contexto" a una detección existente de web shell. Los atributos solos no pueden generar actualmente una detección por sí mismos. En un motor de escaneo tradicional, un escáner solo alertaría si se detectaba un web shell, pero proporcionaría poco o ningún contexto adicional sobre las capacidades (atributos) que potencialmente tiene el web shell. Las etiquetas de atributos funcionan igual que la lógica de detección, sin embargo, solo se muestran después de que se haya identificado una detección y no pueden generar detecciones por sí solas. Veamos el ejemplo de lógica a continuación:
cm.GlobalMap.Tags_Php = []cm.TagDef{
{
Name: "PHP_Database_Operations",
Description: "Looks for common PHP functions used for interacting with a database.",
Regex: *regexp.MustCompile(`(?i)(?:'mssql_connect\|mysql_exec\()`),
Attribute: true,
},
}
Vemos que bajo el struct Tags_Php, hemos creado una nueva etiqueta PHP. Cuando se encuentra una coincidencia durante el escaneo, se verifica la bandera Attribute y, si está establecida en True, el web shell detectado tendrá la etiqueta PHP_Database_Operations añadida a su informe JSON junto con la frecuencia y el bloque de texto coincidente, como se muestra en el ejemplo de salida a continuación:
{
"filePath": "/testers/1.php",
"size": 66109,
"md5": "6793d8ebab93e5a0f91e5a331221f331",
"timestamps": {
"birth": "2019-02-03 02:02:22",
"created": "2020-07-29 02:50:15",
"modified": "2019-02-03 02:02:22",
"accessed": "2020-07-29 02:51:07"
},
"matches": {
"FilesMAn": 5,
"FilesMan": 29,
"cmd": 20,
"eval(": 4,
"exec(": 2,
"ipconfig": 1,
"netstat": 2,
"passthru(": 1,
"shell_exec(": 1
},
"decodes": {
"Generic_Base64Decode": 40,
"Generic_Multiline_Base64Decode": 165
},
"tags": {
"Generic_Embedding_Code_C": {
"bind(": 2,
"listen(": 2
},
"PHP_Banned_Function": {
"exec(": 3,
"get_current_user(": 1,
"getmyuid(": 1,
"link(": 7,
"listen(": 2,
"passthru(": 1,
"realpath(": 1,
"set_time_limit(": 1
},
"PHP_Database_Operations": {
"mysql_query(": 1
},
"PHP_Disk_Operations": {
"@chmod(": 1,
"@filegroup(": 4,
"@fileowner(": 4,
"@rename(": 2,
"fopen(": 7,
"fwrite(": 6
}
}
}
Estas etiquetas no solo ayudan a definir lo que un web shell puede hacer, sino que también proporcionan a equipos como consultores de IR que realizan intervenciones de respuesta en vivo un punto de partida para saber dónde mirar potencialmente a continuación.
¡Ninguno! Simplemente descargue el binario para su sistema operativo, proporcione el directorio que desea escanear (los demás argumentos son opcionales) y déjelo funcionar.
Ejecutar wsa sin argumentos muestra las siguientes opciones:
/Users/beastmode$ ./wsa
Options:
-dir string
Directory to scan for web shells
-pretty
If set to true, the analyzer will output the results in a json indented form
-raw_contents
If a match is found, grab the raw contents and base64 + gzip compress the file into the JSON object.
-size int
Specify max file size to scan (default is 10 MB) (default 10)
-verbose
If set to true, the analyzer will print all files analyzer, not just matches
El único argumento requerido es dir. Puede anular los valores predeterminados del programa si lo desea.
La salida del analizador se escribirá en la consola (salida estándar). Ejemplo a continuación (para mejores resultados, envíe stdout a un archivo json y revise/procese posteriormente sin conexión):
Linux: ./wsa -dir /opt/www
Windows: wsa.exe -dir C:\Windows\Inetput\wwwroot
### Con STDOUT y el archivo web shell completo codificado y comprimido:
Linux: ./wsa -dir /opt/www -raw_contents=true > scan_results.json
Una vez que el analizador termine, mostrará las métricas generales del escaneo en STDOUT, como se muestra en el siguiente ejemplo:
{"scanned":311,"matches":122,"noMatches":189,"directory":"/webshell-master/php","scanDuration":1.4757737378333333,"systemInfo":{"hostname":"Beast","envVars":[""],"username":"beastmode","userID":"501","realName":"The Beast","userHomeDir":"/Users/beastmode"}}